public function postRequest(\SS_HTTPRequest $request, \SS_HTTPResponse $response, \DataModel $model) { if (defined('PROXY_CACHE_GENERATING') || isset($GLOBALS['__cache_publish']) || strpos($request->getURL(), 'admin/') !== false) { return; } $this->database = Db::getConn(); $queries = $this->database->queryRecord; $dupes = $this->database->getDuplicateQueries(); $str = "\n<!-- Total queries: " . count($queries) . "-->\n"; $str .= "\n<!-- Duplicate queries: " . count($dupes) . "-->\n"; $b = $response->getBody(); if (strpos($b, '</html>')) { if (count($queries) > $this->queryThreshold) { // add a floating div with info about the stuff $buildQueryList = function ($source, $class) { $html = ''; foreach ($source as $sql => $info) { $html .= "\n<p class='{$class}' style='display: none; border-top: 1px dashed #000;'>{$info->count} : {$info->query}</p>\n"; if ($info->source) { $html .= "\n<p class='{$class}' style='color: #a00; display: none; '>Last called from {$info->source}</p>\n"; } } return $html; }; $html = $buildQueryList($queries, 'debug-query'); $html .= $buildQueryList($dupes, 'debug-dupe-query'); $div = '<div id="query-stat-debugger" ' . 'style="position: fixed; bottom: 0; right: 0; border: 2px solid red; background: #fff; ' . 'font-size: 8px; font-family: sans-serif; width: 100px; z-index: 2000; padding: 1em;' . 'overflow: auto; max-height: 500px;">' . '<p id="debug-all-queries-list">Total of ' . count($queries) . ' queries</p>' . '<p id="debug-dupe-queries-list">Total of ' . count($dupes) . ' duplicates</p>' . $html . '<script>' . 'jQuery("#debug-all-queries-list").click(function () {' . 'var elems = jQuery(this).parent().find(".debug-query");' . 'jQuery(this).parent().css("width", "40%");' . 'elems.toggle();' . '}); ' . 'jQuery("#debug-dupe-queries-list").click(function () {' . 'var elems = jQuery(this).parent().find(".debug-dupe-query");' . 'jQuery(this).parent().css("width", "40%");' . 'elems.toggle();' . '}); ' . '' . '' . '</script>' . '</div>'; $b = str_replace('</body>', "{$div}</body>", $b); } $b = str_replace('</html>', "{$str}</html>", $b); $response->setBody($b); } }
public function postRequest(\SS_HTTPRequest $request, \SS_HTTPResponse $response, \DataModel $model) { if ($this->convertUrls && $response && $response->getStatusCode() == 200) { // only convert if we have an HTML content type response $body = $response->getBody(); // find urls inserted in content if (strpos($body, 'cdnfileid') > 0 && preg_match_all('/data-cdnfileid="(\\d+)"/', $body, $matches)) { $files = CdnImage::get()->filter('ID', $matches[1]); $fileIds = array(); foreach ($files as $file) { $url = $file->getUrl(); $filename = $file->Filename; $body = str_replace("src=\"{$filename}\"", "src=\"{$url}\"", $body); $fileIds[] = $file->ID; } $assets = ContentServiceAsset::get()->filter('SourceID', $matches[1]); foreach ($assets as $asset) { $url = $asset->getUrl(); $filename = $asset->Filename; // note the extra forward slash here, image_cached inserts it $body = str_replace("src=\"/{$filename}\"", "src=\"{$url}\"", $body); } $response->setBody($body); } } }
protected function ok(array $res = null, $use_etag = true) { $response = new SS_HTTPResponse(); $response->setStatusCode(200); $response->addHeader('Content-Type', 'application/json'); if (is_null($res)) { $res = array(); } $response->setBody(json_encode($res)); //conditional get Request (etags) $request = Controller::curr()->getRequest(); if ($request->isGET() && $use_etag) { $etag = md5($response->getBody()); $requestETag = $request->getHeader('If-None-Match'); foreach (array('Expires', 'Cache-Control') as $header) { $response->removeHeader($header); } $lastmod = gmdate('D, d M Y 0:0:0 \\G\\M\\T', time()); $response->addHeader('Cache-Control', 'max-age=3600'); $response->addHeader('Last-Modified', $lastmod); $response->addHeader('Expires', gmdate('D, d M Y H:m:i \\G\\M\\T', time() + 3600)); $response->addHeader('ETag', $etag); if (!empty($requestETag) && $requestETag == $etag) { $response->setStatusCode(304); $response->addHeader('ETag', $etag); $response->setBody(null); } } return $response; }
/** * Filter executed AFTER a request * * @param SS_HTTPRequest $request Request container object * @param SS_HTTPResponse $response Response output object * @param DataModel $model Current DataModel * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) */ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { if (!self::isEnabled()) { return true; } $body = $response->getBody(); $response->setBody(self::replaceCDN($body)); return true; }
public function postRequest(\SS_HTTPRequest $request, \SS_HTTPResponse $response, \DataModel $model) { $time = sprintf('%.3f ms', microtime(true) - $this->start); $response->addHeader('X-SilverStripe-Time', $time); $b = $response->getBody(); if (strpos($b, '</html>')) { $b = str_replace('</html>', "\n<!-- Generated in {$time} -->\n</html>", $b); $response->setBody($b); } }
/** * Adds Intercom script tags just before the body */ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { $mime = $response->getHeader('Content-Type'); if (!$mime || strpos($mime, 'text/html') !== false) { $intercomScriptTags = (new intercomScriptTags())->forTemplate(); if ($intercomScriptTags) { $content = $response->getBody(); $content = preg_replace("/(<\\/body[^>]*>)/i", $intercomScriptTags . "\\1", $content); $response->setBody($content); } } }
/** * Filter executed AFTER a request * * @param SS_HTTPRequest $request Request container object * @param SS_HTTPResponse $response Response output object * @param DataModel $model Current DataModel * @return boolean Whether to continue processing other filters. Null or true will continue processing (optional) */ public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { $debugbar = DebugBar::getDebugBar(); if (!$debugbar) { return; } // All queries have been displayed if (DebugBar::getShowQueries()) { exit; } $script = DebugBar::renderDebugBar(); // If the bar is not renderable, return early if (!$script) { return; } // Inject init script into the HTML response $body = $response->getBody(); if (strpos($body, '</body>') !== false) { $body = str_replace('</body>', $script . '</body>', $body); $response->setBody($body); } // Ajax support if (Director::is_ajax() && !headers_sent()) { if (DebugBar::IsAdminUrl() && !DebugBar::config()->enabled_in_admin) { return; } // Skip anything that is not a GET request if (!$request->isGET()) { return; } // Always enable in admin because everything is mostly loaded through ajax if (DebugBar::config()->ajax || DebugBar::IsAdminUrl()) { $headers = $debugbar->getDataAsHeaders(); // Prevent throwing js errors in case header size is too large if (is_array($headers)) { $debugbar->sendDataInHeaders(); } } } }
/** * @return string */ public function getContent() { return $this->response->getBody(); }
/** * Assert that a response matches the given parameters * * @param int $code HTTP code * @param string $body Body expected for 200 responses * @param SS_HTTPResponse $response */ protected function assertResponseEquals($code, $body, SS_HTTPResponse $response) { $this->assertEquals($code, $response->getStatusCode()); if ($code === 200) { $this->assertFalse($response->isError()); $this->assertEquals($body, $response->getBody()); $this->assertEquals('text/plain', $response->getHeader('Content-Type')); } else { $this->assertTrue($response->isError()); } }
public function html(SS_HTTPResponse $response) { $encoding = Config::inst()->get('ContentNegotiator', 'encoding'); $contentType = Config::inst()->get('ContentNegotiator', 'content_type'); if (empty($contentType)) { $response->addHeader("Content-Type", "text/html; charset=" . $encoding); } else { $response->addHeader("Content-Type", $contentType . "; charset=" . $encoding); } $response->addHeader("Vary", "Accept"); $content = $response->getBody(); $hasXMLHeader = substr($content, 0, 5) == '<' . '?xml'; // Fix base tag $content = preg_replace('/<base href="([^"]*)" \\/>/', '<base href="$1"><!--[if lte IE 6]></base><![endif]-->', $content); $content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content); $content = str_replace(array('/>', 'xml:lang', 'application/xhtml+xml'), array('>', 'lang', 'text/html'), $content); // Only replace the doctype in templates with the xml header if ($hasXMLHeader) { $content = preg_replace('/<!DOCTYPE[^>]+>/', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', $content); } $content = preg_replace('/<html xmlns="[^"]+"/', '<html ', $content); $response->setBody($content); }
public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { if (preg_match('/text\\/html/', $response->getHeader('Content-Type'))) { $response->setBody($this->SuppressWhitespace($response->getBody())); } }
public function applyToResponse($originator, SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model) { $responseHeaders = array(); $cacheAge = $this->cacheAge; $vary = $this->vary; // Allow overriding max-age from the object hooked up to the policed controller. if ($originator->hasMethod('getCacheAge')) { $extendedCacheAge = $originator->getCacheAge($cacheAge); if ($extendedCacheAge !== null) { $cacheAge = $extendedCacheAge; } } // Same for vary, but probably less useful. if ($originator->hasMethod('getVary')) { $extendedVary = $originator->getVary($vary); if ($extendedVary !== null) { $vary = $extendedVary; } } $etag = null; if ($cacheAge > 0) { // Note: must-revalidate means that the cache must revalidate AFTER the entry has gone stale. $responseHeaders["Cache-Control"] = "max-age=" . $cacheAge . ", must-revalidate, no-transform"; $responseHeaders["Pragma"] = ""; $responseHeaders['Vary'] = $vary; // Find out when the URI was last modified. Allows customisation, but fall back HTTP timestamp collector. if ($originator->hasMethod('getModificationTimestamp')) { $timestamp = $originator->getModificationTimestamp(); } else { $timestamp = HTTP::$modification_date; } if ($timestamp) { $responseHeaders["Last-Modified"] = self::gmt_date($timestamp); $expires = time() + $cacheAge; $responseHeaders["Expires"] = self::gmt_date($expires); } // first etag calculation based on timestamp and cookies .... // Chrome ignores Varies when redirecting back (http://code.google.com/p/chromium/issues/detail?id=79758) // which means that if you log out, you get redirected back to a page which Chrome then checks against // last-modified (which passes, getting a 304) // when it shouldn't be trying to use that page at all because it's the "logged in" version. // By also using and etag that includes both the modification date and all the varies // values which we also check against we can catch this and not return a 304 $etagParts = array($timestamp, serialize($_COOKIE)); $etagParts[] = Director::is_https() ? 'https' : 'http'; if (isset($_SERVER['HTTP_USER_AGENT'])) { $etagParts[] = $_SERVER['HTTP_USER_AGENT']; } if (isset($_SERVER['HTTP_ACCEPT'])) { $etagParts[] = $_SERVER['HTTP_ACCEPT']; } $etag = sha1(implode(':', $etagParts)); $responseHeaders["ETag"] = $etag; } // custom calculation os ETag if ($originator->hasMethod('getEtag')) { $extendedEtag = $originator->getEtag($response->getBody()); if ($extendedEtag !== null) { $etag = $extendedEtag; $responseHeaders["ETag"] = $etag; } } // Now that we've generated them, either output them or attach them to the SS_HTTPResponse as appropriate foreach ($responseHeaders as $k => $v) { $response->addHeader($k, $v); } // 304 response detection /** * The HTTP IF_MODIFIED_SINCE request-header, is a header which is sent by most browsers. * It contains the modification date provided in the Last-Modified header by the server on the last visit. */ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $ifModifiedSince = strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])); // As above, only 304 if the last request had all the same varies values // (or the etag isn't passed as part of the request - but with chrome it always is) $matchesEtag = !isset($_SERVER['HTTP_IF_NONE_MATCH']) || $_SERVER['HTTP_IF_NONE_MATCH'] == $etag; if ($ifModifiedSince >= $timestamp && $matchesEtag) { ob_clean(); $response->setStatusCode(304); $response->setBody(''); } } else { if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) { ob_clean(); $response->setStatusCode(304); $response->setBody(''); } } }
/** * @param SS_HTTPResponse $r * @return SS_HTTPResponse|void * @throws SS_HTTPResponse_Exception * @throws ValidationException * @throws null */ public function handleResolveCategoryChange(SS_HTTPResponse $r) { if (!Permission::check('ADMIN')) { return $this->httpError(403); } if (!is_numeric($r->param('ID'))) { return $this->httpError(500, "Invalid category change id"); } $vars = Convert::json2array($r->getBody()); if (!isset($vars['approved'])) { return $this->httpError(500, "Request body must contain 'approved' 1 or 0"); } $approved = (bool) $vars['approved']; $request = SummitCategoryChange::get()->byID($r->param('ID')); if (!$request) { return $this->httpError(500, "Request " . $r->param('ID') . " does not exist"); } $status = $approved ? SummitCategoryChange::STATUS_APPROVED : SummitCategoryChange::STATUS_REJECTED; if ($request->Presentation()->isSelectedByAnyone()) { return new SS_HTTPResponse("The presentation has already been selected by chairs.", 500); } if ($request->Presentation()->CategoryID == $request->NewCategoryID) { return new SS_HTTPResponse("The presentation is already in this category.", 200); } // Make the category change $summit = Summit::get_active(); $category = $summit->Categories()->filter('ID', $request->NewCategoryID)->first(); if (!$category->exists()) { return $this->httpError(500, "Category not found in current summit"); } $oldCat = $request->Presentation()->Category(); if ($approved) { $request->OldCategoryID = $request->Presentation()->CategoryID; $request->Presentation()->CategoryID = $request->NewCategoryID; $request->Presentation()->write(); $request->Presentation()->addNotification('{member} approved ' . $request->Reqester()->getName() . '\'s request to move this presentation from ' . $oldCat->Title . ' to ' . $category->Title); } else { $request->Presentation()->addNotification('{member} rejected ' . $request->Reqester()->getName() . '\'s request to move this presentation from ' . $oldCat->Title . ' to ' . $category->Title); } $request->AdminApproverID = Member::currentUserID(); $request->Status = $status; $request->ApprovalDate = SS_Datetime::now(); $request->write(); $peers = SummitCategoryChange::get()->filter(['PresentationID' => $request->PresentationID, 'NewCategoryID' => $request->NewCategoryID])->exclude(['ID' => $request->ID]); foreach ($peers as $p) { $p->AdminApproverID = Member::currentUserID(); $p->Status = SummitCategoryChange::STATUS_APPROVED; $p->ApprovalDate = SS_Datetime::now(); $p->write(); } return $this->ok('change request accepted.'); }
/** * Return an array of errors and their messages from a PayPal response * * @param SS_HTTPResponse $response * @return array */ public function getErrors($response) { $errorList = array(); $responseString = $response->getBody(); $responseArr = $this->parseResponse($response); preg_match_all('/L_ERRORCODE\\d+/', $responseString, $errorFields); preg_match_all('/L_LONGMESSAGE\\d+/', $responseString, $messageFields); if (count($errorFields[0]) != count($messageFields[0])) { throw new Exception("PayPal resonse invalid: errors and messages don't match"); } else { for ($i = 0; $i < count($errorFields[0]); $i++) { $errorField = $errorFields[0][$i]; $errorCode = $responseArr[$errorField]; $messageField = $messageFields[0][$i]; $errorMessage = $responseArr[$messageField]; $errorList[$errorCode] = $errorMessage; } } return $errorList; }
public function setResponse(SS_HTTPResponse $response) { $body = json_encode(array('data' => array('status_code' => $response->getStatusCode(), 'message' => $response->getBody()))); $this->response = new JsonDataResponse($body, $response->getStatusCode(), $response->getStatusDescription()); }
/** * @return string */ public function getBody() { // if the body has been set elsewhere, just pass that through. if (empty($this->body)) { $data = new StdClass(); if (!empty($this->events)) { $key = self::EVENTS_KEY; $data->{$key} = $this->events; } // add the pull regions, if any $pulls = $this->getPulledRegionIDs(); if (count($pulls) > 0) { foreach ($pulls as $regionID) { $this->pushRegion($regionID); } } if (!empty($this->regions)) { $key = self::REGIONS_KEY; $data->{$key} = $this->regions; } $this->setBody(json_encode($data)); } return parent::getBody(); }
protected function ok(array $res = null) { $response = new SS_HTTPResponse(); $response->setStatusCode(200); $response->addHeader('Content-Type', 'application/json'); if (is_null($res)) { $res = array(); } $response->setBody(json_encode($res)); //conditional get Request (etags) $request = Controller::curr()->getRequest(); if ($request->isGET()) { $etag = md5($response->getBody()); $requestETag = $request->getHeader('If-None-Match'); if (!empty($requestETag) && $requestETag == $etag) { $response->setStatusCode(304); foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { $response->removeHeader($header); } $response->setBody(null); } else { $response->addHeader('ETag', $etag); } } return $response; }
public function respondWithError($args) { // Make args' keys lowercase $args = array_combine(array_map('strtolower', array_keys($args)), array_values($args)); // See if an exception is passed (no default) $exception = null; if (isset($args['exception'])) { $exception = $args['exception']; } // Get the default exception noun if set $exceptionNoun = self::$exception_noun; // Add defaults $args = array_merge(array('code' => 500, 'description' => null, 'body' => null, 'noun' => $exception && $exceptionNoun ? new $exceptionNoun($exception) : null), $args); // Default "response" which we build into. Not returned, because the exception has it's own response $response = new SS_HTTPResponse(); // If there's a formatter if ($args['noun'] && ($formatter = RESTFormatter::get_formatter($this->request))) { // Format the response. Revert back to default response if we got nothing. $response = $formatter->format($args['noun'], array('*')); if (!$response) { $response = new SS_HTTPResponse(); } } else { if ($exception) { $exception = $args['exception']; $response->setBody($exception->getMessage() . "\n"); $response->addHeader('Content-Type', 'text/plain'); } } // If a specific body was provided, use that if ($args['body']) { $response->setBody($args['body']); } // Build an exception with those details $e = new SS_HTTPResponse_Exception($response->getBody(), $args['code'], $args['description']); $exceptionResponse = $e->getResponse(); // Add user specified headers foreach ($response->getHeaders() as $k => $v) { $exceptionResponse->addHeader($k, $v); } throw $e; }