/** * Constructor. * * @param string|HTTP_Request $messageOrResponse a string (UTF-8) describing * the error, or the * HTTP_Request2_Response that * caused the exception. * @param int $code the error code. */ public function __construct($messageOrResponse, $code = 0) { $message = false; if ($messageOrResponse instanceof HTTP_Request2_Response) { $this->response = $messageOrResponse; $contentType = $this->response->getHeader('content-type'); if ($contentType == 'application/xml' && $this->response->getBody()) { $prevUseInternalErrors = libxml_use_internal_errors(true); $doc = new DOMDocument(); $ok = $doc->loadXML($this->response->getBody()); libxml_use_internal_errors($prevUseInternalErrors); if ($ok) { $xPath = new DOMXPath($doc); $this->_amazonErrorCode = $xPath->evaluate('string(/Error/Code)'); $message = $xPath->evaluate('string(/Error/Message)'); } } if (!$message) { $message = 'Bad response from server.'; } if (!$code) { $code = $this->response->getStatus(); } } else { $message = (string) $messageOrResponse; } parent::__construct($message, $code); }
/** * Callback function called by cURL for saving the response headers * * @param resource cURL handle * @param string response header (with trailing CRLF) * @return integer number of bytes saved * @see HTTP_Request2_Response::parseHeaderLine() */ protected function callbackWriteHeader($ch, $string) { // we may receive a second set of headers if doing e.g. digest auth if ($this->eventReceivedHeaders || !$this->eventSentHeaders) { // don't bother with 100-Continue responses (bug #15785) if (!$this->eventSentHeaders || $this->response->getStatus() >= 200) { $this->request->setLastEvent('sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)); } $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD); // if body wasn't read by a callback, send event with total body size if ($upload > $this->position) { $this->request->setLastEvent('sentBodyPart', $upload - $this->position); $this->position = $upload; } if ($upload && (!$this->eventSentHeaders || $this->response->getStatus() >= 200)) { $this->request->setLastEvent('sentBody', $upload); } $this->eventSentHeaders = true; // we'll need a new response object if ($this->eventReceivedHeaders) { $this->eventReceivedHeaders = false; $this->response = null; } } if (empty($this->response)) { $this->response = new HTTP_Request2_Response($string, false); } else { $this->response->parseHeaderLine($string); if ('' == trim($string)) { // don't bother with 100-Continue responses (bug #15785) if (200 <= $this->response->getStatus()) { $this->request->setLastEvent('receivedHeaders', $this->response); } if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) { $redirectUrl = new Net_URL2($this->response->getHeader('location')); // for versions lower than 5.2.10, check the redirection URL protocol if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute() && !in_array($redirectUrl->getScheme(), array('http', 'https'))) { return -1; } if ($jar = $this->request->getCookieJar()) { $jar->addCookiesFromResponse($this->response, $this->request->getUrl()); if (!$redirectUrl->isAbsolute()) { $redirectUrl = $this->request->getUrl()->resolve($redirectUrl); } if ($cookies = $jar->getMatching($redirectUrl, true)) { curl_setopt($ch, CURLOPT_COOKIE, $cookies); } } } $this->eventReceivedHeaders = true; } } return strlen($string); }
protected function extractLinks(\HTTP_Request2_Response $res) { $mimetype = explode(';', $res->getHeader('content-type'))[0]; if (!isset(static::$supportedTypes[$mimetype])) { Log::info("MIME type not supported for crawling: {$mimetype}"); return array(); } $class = static::$supportedTypes[$mimetype]; $extractor = new $class(); return $extractor->extract($res); }
/** * Compares between two responses by Content-ID header. * * @param \HTTP_Request2_Response $r1 The first response object. * @param \HTTP_Request2_Response $r2 The second response object. * * @return boolean */ private static function _compareUsingContentId($r1, $r2) { $h1 = array_change_key_case($r1->getHeader()); $h2 = array_change_key_case($r2->getHeader()); $c1 = Utilities::tryGetValue($h1, Resources::CONTENT_ID, 0); $c2 = Utilities::tryGetValue($h2, Resources::CONTENT_ID, 0); return intval($c1) >= intval($c2); }
/** * Checks whether another request should be performed with proxy digest auth * * Several conditions should be satisfied for it to return true: * - response status should be 407 * - proxy auth credentials should be set in the request object * - response should contain Proxy-Authenticate header with digest challenge * - there is either no challenge stored for this proxy or new challenge * contains stale=true parameter (in other case we probably just failed * due to invalid username / password) * * The method stores challenge values in $challenges static property * * @param HTTP_Request2_Response response to check * @return boolean whether another request should be performed * @throws HTTP_Request2_Exception in case of unsupported challenge parameters */ protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response) { if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) { return false; } if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) { return false; } $key = 'proxy://' . $this->request->getConfig('proxy_host') . ':' . $this->request->getConfig('proxy_port'); if (!empty(self::$challenges[$key]) && (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))) { $ret = false; } else { $ret = true; } self::$challenges[$key] = $challenge; return $ret; }
/** * Based on an XRI, will request the XRD document located at the proxy * prefixed URI and parse in search of the XRI Canonical Id. This is * a flexible requirement. OpenID 2.0 requires the use of the Canonical * ID instead of the raw i-name. 2idi.com, on the other hand, does not. * * @param string $xri The XRI * * @todo Imcomplete; requires interface from Yadis main class * @return string * @throws Services_Yadis_Exception */ public function toCanonicalId($xri = null) { if (!isset($xri) && !isset($this->uri)) { throw new Services_Yadis_Exception('No XRI passed as parameter as required unless called after ' . 'Services_Yadis_Xri:toUri'); } elseif (isset($xri)) { $uri = $this->toUri($xri); } else { $uri = $this->uri; } $this->httpResponse = $this->get($uri, null, $this->getHttpRequestOptions()); if (stripos($this->httpResponse->getHeader('Content-Type'), 'application/xrds+xml') === false) { throw new Services_Yadis_Exception('The response header indicates the response body is not ' . 'an XRDS document'); } $xrds = new SimpleXMLElement($this->httpResponse->getBody()); $this->namespace->registerXpathNamespaces($xrds); $id = $xrds->xpath('//xrd:CanonicalID[last()]'); $this->canonicalID = (string) array_shift($id); if (!$this->canonicalID) { throw new Services_Yadis_Exception('Unable to determine canonicalID'); } return $xrds; }
/** * Returns a DOMXPath object for the XML document in the body of the * specified HTTP response. This method is for internal use only. * * @param HTTP_Request2_Response $response the HTTP response. * * @return DOMXPath * @throws Services_Amazon_S3_ServerErrorException */ public static function getDOMXPath(HTTP_Request2_Response $response) { if ($response->getHeader('content-type') != 'application/xml') { throw new Services_Amazon_S3_ServerErrorException('Response was not of type application/xml', $response); } $prevUseInternalErrors = libxml_use_internal_errors(true); $doc = new DOMDocument(); $ok = $doc->loadXML($response->getBody()); libxml_use_internal_errors($prevUseInternalErrors); if (!$ok) { throw new Services_Amazon_S3_ServerErrorException($response); } $xPath = new DOMXPath($doc); $xPath->registerNamespace('s3', self::NS_S3); $xPath->registerNamespace('xsi', self::NS_XSI); return $xPath; }
/** * Saves the response body to a specified directory * * @param HTTP_Request2_Response $response * @return void * @throws BuildException */ protected function processResponse(HTTP_Request2_Response $response) { if ($response->getStatus() != 200) { throw new BuildException("Request unsuccessful. Response from server: " . $response->getStatus() . " " . $response->getReasonPhrase()); } $content = $response->getBody(); $disposition = $response->getHeader('content-disposition'); if ($this->filename) { $filename = $this->filename; } elseif ($disposition && 0 == strpos($disposition, 'attachment') && preg_match('/filename="([^"]+)"/', $disposition, $m)) { $filename = basename($m[1]); } else { $filename = basename(parse_url($this->url, PHP_URL_PATH)); } if (!is_writable($this->dir)) { throw new BuildException("Cannot write to directory: " . $this->dir); } $filename = $this->dir . "/" . $filename; file_put_contents($filename, $content); $this->log("Contents from " . $this->url . " saved to {$filename}"); }
/** * Gets the Expires header from the response object * * @return string */ protected function getExpiresHeader() { // @codeCoverageIgnoreStart return $this->response->getHeader('Expires'); // @codeCoverageIgnoreEnd }
/** * Parse the response (from {@link self::makeRequest()}. * * @param HTTP_Request2_Response $resp The response returned from the API. * * @return mixed * @throws RuntimeException In case the API returned an error. */ protected function parseResponse(HTTP_Request2_Response $resp) { $body = $resp->getBody(); $headers = $resp->getHeader(); if (isset($headers['content-type'])) { if ($headers['content-type'] == 'text/calendar; charset=utf-8') { return $body; } } switch ($body) { case 'Access Denied': throw new RuntimeException("API response: {$body}"); case 'Item Deleted Successfully': case 'Meeting Deleted Successfully': case 'Note Deleted Successfully': return $body; default: $resp = json_decode($body); if (isset($resp->status)) { switch ($resp->status) { case 'internal_server_error': throw new RuntimeException($resp->message); } } return $resp; } }
/** * Determines the filename from either the 'content-disposition' header * or from the basename of the current request. * * @param \HTTP_Request2 $request * @param \HTTP_Request2_Response $response * @return void */ protected function determineFilename(\HTTP_Request2 $request, \HTTP_Request2_Response $response) { $matches = array(); $disposition = $response->getHeader('content-disposition'); if ($disposition !== null && 0 === strpos($disposition, 'attachment') && 1 === preg_match('/filename="([^"]+)"/', $disposition, $matches)) { $filename = basename($matches[1]); } else { $filename = basename($request->getUrl()->getPath()); } $this->setFilename($filename); }
/** * Verifies that the HTTP response has status code 200 and * content-type application/json; charset=utf-8 * * @param HTTP_Request2_Response $res HTTP Response object * * @return void */ protected function assertResponseJson200(HTTP_Request2_Response $res) { $this->assertEquals(200, $res->getStatus()); $this->assertEquals('application/json; charset=utf-8', $res->getHeader('content-type')); }
/** * Given an HTTP response, return the requested Link: header * * @param HTTP_Request2_Response $response response to check * @param string $rel relationship to look for * @param string $type media type to look for * * @return LinkHeader discovered header, or null on failure */ static function getLink($response, $rel = null, $type = null) { $headers = $response->getHeader('Link'); if ($headers) { // Can get an array or string, so try to simplify the path if (!is_array($headers)) { $headers = array($headers); } foreach ($headers as $header) { $lh = new LinkHeader($header); if ((is_null($rel) || $lh->rel == $rel) && (is_null($type) || $lh->type == $type)) { return $lh->href; } } } return null; }
/** * Assuming this user is hosting a third party sourced identity under an * alias personal URL, we'll need to check if the website's HTML body * has a http-equiv meta element with a content attribute pointing to where * we can fetch the XRD document. * * @param HTTP_Request2_Response $response Instance of HTTP_Request2_Response * * @return boolean * @throws Services_Yadis_Exception */ protected function isMetaHttpEquiv(HTTP_Request2_Response $response) { $location = null; if (!in_array($response->getHeader('Content-Type'), $this->validHtmlContentTypes)) { return false; } /** * Find a match for a relevant <meta> element, then iterate through the * results to see if a valid http-equiv value and matching content URI * exist. */ $html = new DOMDocument(); $html->loadHTML($response->getBody()); $head = $html->getElementsByTagName('head'); if ($head->length > 0) { $metas = $head->item(0)->getElementsByTagName('meta'); if ($metas->length > 0) { foreach ($metas as $meta) { $equiv = strtolower($meta->getAttribute('http-equiv')); if ($equiv == 'x-xrds-location' || $equiv == 'x-yadis-location') { $location = $meta->getAttribute('content'); } } } } if (is_null($location)) { return false; } elseif (!self::validateURI($location)) { throw new Services_Yadis_Exception('The URI parsed from the HTML Alias document appears to be invalid, ' . 'or could not be found: ' . htmlentities($location, ENT_QUOTES, 'utf-8')); } /** * Should now contain the content value of the http-equiv type pointing * to an XRDS resource for the user's Identity Provider, as found by * passing the meta regex across the response body. */ $this->metaHttpEquivUrl = $location; return true; }