@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Improve top-level exception handling

Summary:
Fixes T6692. Addresses two main issues:

- The write guard would sometimes not get disposed of on exception pathways, generating an unnecessary secondary error which was just a symptom of the original root error.
- This was generally confusing and reduced the quality of reports we received because users would report the symptomatic error sometimes instead of the real error.
- Instead, reflow the handling so that we always dispose of the write guard if we create one.
- If we missed the Controller-level error page generation (normally, a nice page with full CSS, etc), we'd jump straight to Startup-level error page generation (very basic plain text).
- A large class of errors occur too early or too late to be handled by Controller-level pages, but many of these errors are not fundamental, and the plain text page is excessively severe.
- Provide a mid-level simple HTML error page for errors which can't get full CSS, but also aren't so fundamental that we have no recourse but plain text.

Test Plan:
Mid-level errors now produce an intentional-looking error page:

{F259885}

Verified that setup errors still render properly.

@chad, feel free to tweak the exception page -- I just did a rough pass on it. Like the setup error stuff, it doesn't have Celerity, so we can't use `{$colors}` and no other CSS will be loaded.

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley, chad

Maniphest Tasks: T6692

Differential Revision: https://secure.phabricator.com/D11126

+409 -212
+2
resources/celerity/map.php
··· 45 45 'rsrc/css/application/config/config-template.css' => '25d446d6', 46 46 'rsrc/css/application/config/config-welcome.css' => 'b0d16200', 47 47 'rsrc/css/application/config/setup-issue.css' => '8f852bc0', 48 + 'rsrc/css/application/config/unhandled-exception.css' => '38f08073', 48 49 'rsrc/css/application/conpherence/menu.css' => 'e1e0fdf1', 49 50 'rsrc/css/application/conpherence/message-pane.css' => '042886d1', 50 51 'rsrc/css/application/conpherence/notification.css' => '04a6e10a', ··· 816 817 'sprite-tokens-css' => '1706b943', 817 818 'syntax-highlighting-css' => '56c1ba38', 818 819 'tokens-css' => '3d0f239e', 820 + 'unhandled-exception-css' => '38f08073', 819 821 ), 820 822 'requires' => array( 821 823 '00861799' => array(
+5 -1
src/__phutil_library_map__.php
··· 161 161 'AphrontResponse' => 'aphront/response/AphrontResponse.php', 162 162 'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php', 163 163 'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php', 164 + 'AphrontStandaloneHTMLResponse' => 'aphront/response/AphrontStandaloneHTMLResponse.php', 164 165 'AphrontTableView' => 'view/control/AphrontTableView.php', 165 166 'AphrontTagView' => 'view/AphrontTagView.php', 166 167 'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php', 167 168 'AphrontTwoColumnView' => 'view/layout/AphrontTwoColumnView.php', 168 169 'AphrontTypeaheadTemplateView' => 'view/control/AphrontTypeaheadTemplateView.php', 169 170 'AphrontURIMapper' => 'aphront/AphrontURIMapper.php', 171 + 'AphrontUnhandledExceptionResponse' => 'aphront/response/AphrontUnhandledExceptionResponse.php', 170 172 'AphrontUsageException' => 'aphront/exception/AphrontUsageException.php', 171 173 'AphrontView' => 'view/AphrontView.php', 172 174 'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php', ··· 3217 3219 'AphrontRequestTestCase' => 'PhabricatorTestCase', 3218 3220 'AphrontSideNavFilterView' => 'AphrontView', 3219 3221 'AphrontStackTraceView' => 'AphrontView', 3222 + 'AphrontStandaloneHTMLResponse' => 'AphrontHTMLResponse', 3220 3223 'AphrontTableView' => 'AphrontView', 3221 3224 'AphrontTagView' => 'AphrontView', 3222 3225 'AphrontTokenizerTemplateView' => 'AphrontView', 3223 3226 'AphrontTwoColumnView' => 'AphrontView', 3224 3227 'AphrontTypeaheadTemplateView' => 'AphrontView', 3228 + 'AphrontUnhandledExceptionResponse' => 'AphrontStandaloneHTMLResponse', 3225 3229 'AphrontUsageException' => 'AphrontException', 3226 3230 'AphrontView' => array( 3227 3231 'Phobject', ··· 4669 4673 'PhabricatorMarkupInterface', 4670 4674 ), 4671 4675 'PhabricatorConfigProxySource' => 'PhabricatorConfigSource', 4672 - 'PhabricatorConfigResponse' => 'AphrontHTMLResponse', 4676 + 'PhabricatorConfigResponse' => 'AphrontStandaloneHTMLResponse', 4673 4677 'PhabricatorConfigSchemaQuery' => 'Phobject', 4674 4678 'PhabricatorConfigSchemaSpec' => 'Phobject', 4675 4679 'PhabricatorConfigServerSchema' => 'PhabricatorConfigStorageSchema',
+14 -12
src/__tests__/PhabricatorCelerityTestCase.php
··· 15 15 $new_map = id(new CelerityResourceMapGenerator($resources)) 16 16 ->generate(); 17 17 18 - $this->assertEqual( 19 - $new_map->getNameMap(), 20 - $old_map->getNameMap()); 21 - $this->assertEqual( 22 - $new_map->getSymbolMap(), 23 - $old_map->getSymbolMap()); 24 - $this->assertEqual( 25 - $new_map->getRequiresMap(), 26 - $old_map->getRequiresMap()); 27 - $this->assertEqual( 28 - $new_map->getPackageMap(), 29 - $old_map->getPackageMap()); 18 + // Don't actually compare these values with assertEqual(), since the diff 19 + // isn't helpful and is often enormously huge. 20 + 21 + $maps_are_identical = 22 + ($new_map->getNameMap() === $old_map->getNameMap()) && 23 + ($new_map->getSymbolMap() === $old_map->getSymbolMap()) && 24 + ($new_map->getRequiresMap() === $old_map->getRequiresMap()) && 25 + ($new_map->getPackageMap() === $old_map->getPackageMap()); 26 + 27 + $this->assertTrue( 28 + $maps_are_identical, 29 + pht( 30 + 'When this test fails, it means the Celerity resource map is out '. 31 + 'of date. Run `bin/celerity map` to rebuild it.')); 30 32 } 31 33 } 32 34
+186
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 54 54 public function willBuildRequest() {} 55 55 56 56 57 + /** 58 + * @phutil-external-symbol class PhabricatorStartup 59 + */ 60 + public static function runHTTPRequest(AphrontHTTPSink $sink) { 61 + PhabricatorEnv::initializeWebEnvironment(); 62 + 63 + $debug_time_limit = PhabricatorEnv::getEnvConfig('debug.time-limit'); 64 + if ($debug_time_limit) { 65 + PhabricatorStartup::setDebugTimeLimit($debug_time_limit); 66 + } 67 + 68 + // This is the earliest we can get away with this, we need env config first. 69 + PhabricatorAccessLog::init(); 70 + $access_log = PhabricatorAccessLog::getLog(); 71 + PhabricatorStartup::setGlobal('log.access', $access_log); 72 + $access_log->setData( 73 + array( 74 + 'R' => AphrontRequest::getHTTPHeader('Referer', '-'), 75 + 'r' => idx($_SERVER, 'REMOTE_ADDR', '-'), 76 + 'M' => idx($_SERVER, 'REQUEST_METHOD', '-'), 77 + )); 78 + 79 + DarkConsoleXHProfPluginAPI::hookProfiler(); 80 + DarkConsoleErrorLogPluginAPI::registerErrorHandler(); 81 + 82 + $response = PhabricatorSetupCheck::willProcessRequest(); 83 + if ($response) { 84 + PhabricatorStartup::endOutputCapture(); 85 + $sink->writeResponse($response); 86 + return; 87 + } 88 + 89 + $host = AphrontRequest::getHTTPHeader('Host'); 90 + $path = $_REQUEST['__path__']; 91 + 92 + switch ($host) { 93 + default: 94 + $config_key = 'aphront.default-application-configuration-class'; 95 + $application = PhabricatorEnv::newObjectFromConfig($config_key); 96 + break; 97 + } 98 + 99 + $application->setHost($host); 100 + $application->setPath($path); 101 + $application->willBuildRequest(); 102 + $request = $application->buildRequest(); 103 + 104 + // Build the server URI implied by the request headers. If an administrator 105 + // has not configured "phabricator.base-uri" yet, we'll use this to generate 106 + // links. 107 + 108 + $request_protocol = ($request->isHTTPS() ? 'https' : 'http'); 109 + $request_base_uri = "{$request_protocol}://{$host}/"; 110 + PhabricatorEnv::setRequestBaseURI($request_base_uri); 111 + 112 + $access_log->setData( 113 + array( 114 + 'U' => (string)$request->getRequestURI()->getPath(), 115 + )); 116 + 117 + $write_guard = new AphrontWriteGuard(array($request, 'validateCSRF')); 118 + 119 + $processing_exception = null; 120 + try { 121 + $response = $application->processRequest($request, $access_log, $sink); 122 + $response_code = $response->getHTTPResponseCode(); 123 + } catch (Exception $ex) { 124 + $processing_exception = $ex; 125 + $response_code = 500; 126 + } 127 + 128 + $write_guard->dispose(); 129 + 130 + $access_log->setData( 131 + array( 132 + 'c' => $response_code, 133 + 'T' => PhabricatorStartup::getMicrosecondsSinceStart(), 134 + )); 135 + 136 + $access_log->write(); 137 + 138 + DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log); 139 + 140 + // Add points to the rate limits for this request. 141 + if (isset($_SERVER['REMOTE_ADDR'])) { 142 + $user_ip = $_SERVER['REMOTE_ADDR']; 143 + 144 + // The base score for a request allows users to make 30 requests per 145 + // minute. 146 + $score = (1000 / 30); 147 + 148 + // If the user was logged in, let them make more requests. 149 + if ($request->getUser() && $request->getUser()->getPHID()) { 150 + $score = $score / 5; 151 + } 152 + 153 + PhabricatorStartup::addRateLimitScore($user_ip, $score); 154 + } 155 + 156 + if ($processing_exception) { 157 + throw $processing_exception; 158 + } 159 + } 160 + 161 + 162 + public function processRequest( 163 + AphrontRequest $request, 164 + PhutilDeferredLog $access_log, 165 + AphrontHTTPSink $sink) { 166 + 167 + $this->setRequest($request); 168 + 169 + list($controller, $uri_data) = $this->buildController(); 170 + 171 + $access_log->setData( 172 + array( 173 + 'C' => get_class($controller), 174 + )); 175 + 176 + $request->setURIMap($uri_data); 177 + $controller->setRequest($request); 178 + 179 + // If execution throws an exception and then trying to render that 180 + // exception throws another exception, we want to show the original 181 + // exception, as it is likely the root cause of the rendering exception. 182 + $original_exception = null; 183 + try { 184 + $response = $controller->willBeginExecution(); 185 + 186 + if ($request->getUser() && $request->getUser()->getPHID()) { 187 + $access_log->setData( 188 + array( 189 + 'u' => $request->getUser()->getUserName(), 190 + 'P' => $request->getUser()->getPHID(), 191 + )); 192 + } 193 + 194 + if (!$response) { 195 + $controller->willProcessRequest($uri_data); 196 + $response = $controller->handleRequest($request); 197 + } 198 + } catch (Exception $ex) { 199 + $original_exception = $ex; 200 + $response = $this->handleException($ex); 201 + } 202 + 203 + try { 204 + $response = $controller->didProcessRequest($response); 205 + $response = $this->willSendResponse($response, $controller); 206 + $response->setRequest($request); 207 + 208 + $unexpected_output = PhabricatorStartup::endOutputCapture(); 209 + if ($unexpected_output) { 210 + $unexpected_output = pht( 211 + "Unexpected output:\n\n%s", 212 + $unexpected_output); 213 + 214 + phlog($unexpected_output); 215 + 216 + if ($response instanceof AphrontWebpageResponse) { 217 + echo phutil_tag( 218 + 'div', 219 + array('style' => 220 + 'background: #eeddff;'. 221 + 'white-space: pre-wrap;'. 222 + 'z-index: 200000;'. 223 + 'position: relative;'. 224 + 'padding: 8px;'. 225 + 'font-family: monospace', 226 + ), 227 + $unexpected_output); 228 + } 229 + } 230 + 231 + $sink->writeResponse($response); 232 + } catch (Exception $ex) { 233 + if ($original_exception) { 234 + throw $original_exception; 235 + } 236 + throw $ex; 237 + } 238 + 239 + return $response; 240 + } 241 + 242 + 57 243 /* -( URI Routing )-------------------------------------------------------- */ 58 244 59 245
+63
src/aphront/response/AphrontStandaloneHTMLResponse.php
··· 1 + <?php 2 + 3 + abstract class AphrontStandaloneHTMLResponse 4 + extends AphrontHTMLResponse { 5 + 6 + abstract protected function getResources(); 7 + abstract protected function getResponseTitle(); 8 + abstract protected function getResponseBodyClass(); 9 + abstract protected function getResponseBody(); 10 + abstract protected function buildPlainTextResponseString(); 11 + 12 + final public function buildResponseString() { 13 + // Check to make sure we aren't requesting this via Ajax or Conduit. 14 + if (isset($_REQUEST['__ajax__']) || isset($_REQUEST['__conduit__'])) { 15 + return (string)hsprintf('%s', $this->buildPlainTextResponseString()); 16 + } 17 + 18 + $title = $this->getResponseTitle(); 19 + $resources = $this->buildResources(); 20 + $body_class = $this->getResponseBodyClass(); 21 + $body = $this->getResponseBody(); 22 + 23 + return (string)hsprintf( 24 + <<<EOTEMPLATE 25 + <!DOCTYPE html> 26 + <html> 27 + <head> 28 + <meta charset="UTF-8" /> 29 + <title>%s</title> 30 + %s 31 + </head> 32 + %s 33 + </html> 34 + EOTEMPLATE 35 + , 36 + $title, 37 + $resources, 38 + phutil_tag( 39 + 'body', 40 + array( 41 + 'class' => $body_class, 42 + ), 43 + $body)); 44 + } 45 + 46 + private function buildResources() { 47 + $paths = $this->getResources(); 48 + 49 + $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; 50 + 51 + $resources = array(); 52 + foreach ($paths as $path) { 53 + $resources[] = phutil_tag( 54 + 'style', 55 + array('type' => 'text/css'), 56 + phutil_safe_html(Filesystem::readFile($webroot.'/rsrc/'.$path))); 57 + } 58 + 59 + return phutil_implode_html("\n", $resources); 60 + } 61 + 62 + 63 + }
+74
src/aphront/response/AphrontUnhandledExceptionResponse.php
··· 1 + <?php 2 + 3 + final class AphrontUnhandledExceptionResponse 4 + extends AphrontStandaloneHTMLResponse { 5 + 6 + private $exception; 7 + 8 + public function setException(Exception $exception) { 9 + $this->exception = $exception; 10 + return $this; 11 + } 12 + 13 + public function getHTTPResponseCode() { 14 + return 500; 15 + } 16 + 17 + protected function getResources() { 18 + return array( 19 + 'css/application/config/config-template.css', 20 + 'css/application/config/unhandled-exception.css', 21 + ); 22 + } 23 + 24 + protected function getResponseTitle() { 25 + return pht('Unhandled Exception'); 26 + } 27 + 28 + protected function getResponseBodyClass() { 29 + return 'unhandled-exception'; 30 + } 31 + 32 + protected function getResponseBody() { 33 + $ex = $this->exception; 34 + 35 + if ($ex instanceof AphrontUsageException) { 36 + $title = $ex->getTitle(); 37 + } else { 38 + $title = get_class($ex); 39 + } 40 + 41 + $body = $ex->getMessage(); 42 + $body = phutil_escape_html_newlines($body); 43 + 44 + return phutil_tag( 45 + 'div', 46 + array( 47 + 'class' => 'unhandled-exception-detail', 48 + ), 49 + array( 50 + phutil_tag( 51 + 'h1', 52 + array( 53 + 'class' => 'unhandled-exception-title', 54 + ), 55 + $title), 56 + phutil_tag( 57 + 'div', 58 + array( 59 + 'class' => 'unhandled-exception-body', 60 + ), 61 + $body), 62 + )); 63 + } 64 + 65 + protected function buildPlainTextResponseString() { 66 + $ex = $this->exception; 67 + 68 + return pht( 69 + '%s: %s', 70 + get_class($ex), 71 + $ex->getMessage()); 72 + } 73 + 74 + }
+22 -40
src/applications/config/response/PhabricatorConfigResponse.php
··· 1 1 <?php 2 2 3 - final class PhabricatorConfigResponse extends AphrontHTMLResponse { 3 + final class PhabricatorConfigResponse extends AphrontStandaloneHTMLResponse { 4 4 5 5 private $view; 6 6 ··· 9 9 return $this; 10 10 } 11 11 12 - public function buildResponseString() { 13 - // Check to make sure we aren't requesting this via ajax or conduit 14 - if (isset($_REQUEST['__ajax__']) || isset($_REQUEST['__conduit__'])) { 15 - // We don't want to flood the console with html, just return a simple 16 - // message for now. 17 - return pht( 18 - 'This install has a fatal setup error, access the internet web '. 19 - 'version to view details and resolve it.'); 20 - } 21 - 22 - $resources = $this->buildResources(); 23 - 24 - $view = $this->view->render(); 25 - 26 - return hsprintf( 27 - '<!DOCTYPE html>'. 28 - '<html>'. 29 - '<head>'. 30 - '<meta charset="UTF-8" />'. 31 - '<title>Phabricator Setup</title>'. 32 - '%s'. 33 - '</head>'. 34 - '<body class="setup-fatal">%s</body>'. 35 - '</html>', 36 - $resources, 37 - $view); 12 + public function getHTTPResponseCode() { 13 + return 500; 38 14 } 39 15 40 - private function buildResources() { 41 - $css = array( 42 - 'application/config/config-template.css', 43 - 'application/config/setup-issue.css', 16 + protected function getResources() { 17 + return array( 18 + 'css/application/config/config-template.css', 19 + 'css/application/config/setup-issue.css', 44 20 ); 21 + } 45 22 46 - $webroot = dirname(phutil_get_library_root('phabricator')).'/webroot/'; 23 + protected function getResponseTitle() { 24 + return pht('Phabricator Setup Error'); 25 + } 26 + 27 + protected function getResponseBodyClass() { 28 + return 'setup-fatal'; 29 + } 47 30 48 - $resources = array(); 49 - foreach ($css as $path) { 50 - $resources[] = phutil_tag( 51 - 'style', 52 - array('type' => 'text/css'), 53 - phutil_safe_html(Filesystem::readFile($webroot.'/rsrc/css/'.$path))); 54 - } 55 - return phutil_implode_html("\n", $resources); 31 + protected function getResponseBody() { 32 + return $this->view->render(); 56 33 } 57 34 35 + protected function buildPlainTextResponseString() { 36 + return pht( 37 + 'This install has a fatal setup error, access the internet web '. 38 + 'version to view details and resolve it.'); 39 + } 58 40 59 41 }
+4
support/PhabricatorStartup.php
··· 122 122 self::setupPHP(); 123 123 self::verifyPHP(); 124 124 125 + // If we've made it this far, the environment isn't completely broken so 126 + // we can switch over to relying on our own exception recovery mechanisms. 127 + ini_set('display_errors', 0); 128 + 125 129 if (isset($_SERVER['REMOTE_ADDR'])) { 126 130 self::rateLimitRequest($_SERVER['REMOTE_ADDR']); 127 131 }
+12 -159
webroot/index.php
··· 11 11 12 12 PhabricatorStartup::didStartup(); 13 13 14 - $show_unexpected_traces = false; 15 14 try { 16 15 PhabricatorStartup::loadCoreLibraries(); 17 - 18 - PhabricatorEnv::initializeWebEnvironment(); 19 - 20 - $debug_time_limit = PhabricatorEnv::getEnvConfig('debug.time-limit'); 21 - if ($debug_time_limit) { 22 - PhabricatorStartup::setDebugTimeLimit($debug_time_limit); 23 - } 24 - 25 - $show_unexpected_traces = PhabricatorEnv::getEnvConfig( 26 - 'phabricator.developer-mode'); 27 - 28 - // This is the earliest we can get away with this, we need env config first. 29 - PhabricatorAccessLog::init(); 30 - $access_log = PhabricatorAccessLog::getLog(); 31 - PhabricatorStartup::setGlobal('log.access', $access_log); 32 - $access_log->setData( 33 - array( 34 - 'R' => AphrontRequest::getHTTPHeader('Referer', '-'), 35 - 'r' => idx($_SERVER, 'REMOTE_ADDR', '-'), 36 - 'M' => idx($_SERVER, 'REQUEST_METHOD', '-'), 37 - )); 38 - 39 - DarkConsoleXHProfPluginAPI::hookProfiler(); 40 - DarkConsoleErrorLogPluginAPI::registerErrorHandler(); 41 - 42 16 $sink = new AphrontPHPHTTPSink(); 43 17 44 - $response = PhabricatorSetupCheck::willProcessRequest(); 45 - if ($response) { 46 - PhabricatorStartup::endOutputCapture(); 47 - $sink->writeResponse($response); 48 - return; 49 - } 50 - 51 - $host = AphrontRequest::getHTTPHeader('Host'); 52 - $path = $_REQUEST['__path__']; 53 - 54 - switch ($host) { 55 - default: 56 - $config_key = 'aphront.default-application-configuration-class'; 57 - $application = PhabricatorEnv::newObjectFromConfig($config_key); 58 - break; 59 - } 60 - 61 - $application->setHost($host); 62 - $application->setPath($path); 63 - $application->willBuildRequest(); 64 - $request = $application->buildRequest(); 65 - 66 - // Until an administrator sets "phabricator.base-uri", assume it is the same 67 - // as the request URI. This will work fine in most cases, it just breaks down 68 - // when daemons need to do things. 69 - $request_protocol = ($request->isHTTPS() ? 'https' : 'http'); 70 - $request_base_uri = "{$request_protocol}://{$host}/"; 71 - PhabricatorEnv::setRequestBaseURI($request_base_uri); 72 - 73 - $write_guard = new AphrontWriteGuard(array($request, 'validateCSRF')); 74 - 75 - $application->setRequest($request); 76 - list($controller, $uri_data) = $application->buildController(); 77 - $request->setURIMap($uri_data); 78 - $controller->setRequest($request); 79 - 80 - $access_log->setData( 81 - array( 82 - 'U' => (string)$request->getRequestURI()->getPath(), 83 - 'C' => get_class($controller), 84 - )); 85 - 86 - // If execution throws an exception and then trying to render that exception 87 - // throws another exception, we want to show the original exception, as it is 88 - // likely the root cause of the rendering exception. 89 - $original_exception = null; 90 18 try { 91 - $response = $controller->willBeginExecution(); 92 - 93 - if ($request->getUser() && $request->getUser()->getPHID()) { 94 - $access_log->setData( 95 - array( 96 - 'u' => $request->getUser()->getUserName(), 97 - 'P' => $request->getUser()->getPHID(), 98 - )); 99 - } 100 - 101 - if (!$response) { 102 - $controller->willProcessRequest($uri_data); 103 - $response = $controller->handleRequest($request); 104 - } 19 + AphrontApplicationConfiguration::runHTTPRequest($sink); 105 20 } catch (Exception $ex) { 106 - $original_exception = $ex; 107 - $response = $application->handleException($ex); 108 - } 109 - 110 - try { 111 - $response = $controller->didProcessRequest($response); 112 - $response = $application->willSendResponse($response, $controller); 113 - $response->setRequest($request); 21 + try { 22 + $response = new AphrontUnhandledExceptionResponse(); 23 + $response->setException($ex); 114 24 115 - $unexpected_output = PhabricatorStartup::endOutputCapture(); 116 - if ($unexpected_output) { 117 - $unexpected_output = "Unexpected output:\n\n{$unexpected_output}"; 118 - phlog($unexpected_output); 119 - 120 - if ($response instanceof AphrontWebpageResponse) { 121 - echo phutil_tag( 122 - 'div', 123 - array('style' => 124 - 'background: #eeddff;'. 125 - 'white-space: pre-wrap;'. 126 - 'z-index: 200000;'. 127 - 'position: relative;'. 128 - 'padding: 8px;'. 129 - 'font-family: monospace', 130 - ), 131 - $unexpected_output); 132 - } 25 + PhabricatorStartup::endOutputCapture(); 26 + $sink->writeResponse($response); 27 + } catch (Exception $response_exception) { 28 + // If we hit a rendering exception, ignore it and throw the original 29 + // exception. It is generally more interesting and more likely to be 30 + // the root cause. 31 + throw $ex; 133 32 } 134 - 135 - $sink->writeResponse($response); 136 - } catch (Exception $ex) { 137 - $write_guard->dispose(); 138 - $access_log->write(); 139 - if ($original_exception) { 140 - $ex = new PhutilAggregateException( 141 - 'Multiple exceptions during processing and rendering.', 142 - array( 143 - $original_exception, 144 - $ex, 145 - )); 146 - } 147 - PhabricatorStartup::didEncounterFatalException( 148 - 'Rendering Exception', 149 - $ex, 150 - $show_unexpected_traces); 151 - } 152 - 153 - $write_guard->dispose(); 154 - 155 - $access_log->setData( 156 - array( 157 - 'c' => $response->getHTTPResponseCode(), 158 - 'T' => PhabricatorStartup::getMicrosecondsSinceStart(), 159 - )); 160 - 161 - DarkConsoleXHProfPluginAPI::saveProfilerSample($access_log); 162 - 163 - // Add points to the rate limits for this request. 164 - if (isset($_SERVER['REMOTE_ADDR'])) { 165 - $user_ip = $_SERVER['REMOTE_ADDR']; 166 - 167 - // The base score for a request allows users to make 30 requests per 168 - // minute. 169 - $score = (1000 / 30); 170 - 171 - // If the user was logged in, let them make more requests. 172 - if ($request->getUser() && $request->getUser()->getPHID()) { 173 - $score = $score / 5; 174 - } 175 - 176 - PhabricatorStartup::addRateLimitScore($user_ip, $score); 177 33 } 178 34 179 35 } catch (Exception $ex) { 180 - PhabricatorStartup::didEncounterFatalException( 181 - 'Core Exception', 182 - $ex, 183 - $show_unexpected_traces); 36 + PhabricatorStartup::didEncounterFatalException('Core Exception', $ex, false); 184 37 }
+27
webroot/rsrc/css/application/config/unhandled-exception.css
··· 1 + /** 2 + * @provides unhandled-exception-css 3 + */ 4 + 5 + .unhandled-exception { 6 + background: #222228; 7 + } 8 + 9 + .unhandled-exception-detail { 10 + max-width: 760px; 11 + margin: 16px auto; 12 + background: #f7f7f7; 13 + border: 2px solid #ffffff; 14 + } 15 + 16 + .unhandled-exception-detail .unhandled-exception-title { 17 + font-size: 15px; 18 + font-weight: bold; 19 + margin: 0; 20 + padding: 16px; 21 + background: #DFE0E2; 22 + } 23 + 24 + .unhandled-exception-detail .unhandled-exception-body { 25 + padding: 16px; 26 + color: #4B4D51; 27 + }