/** * @param array $headers * @param array $userMetadata */ private function populateRequestHeadersWithUserMetadata(array &$headers, array $userMetadata) { $metaSize = 0; foreach ($userMetadata as $key => $value) { $key = HttpHeaders::BCE_USER_METADATA_PREFIX . HttpUtils::urlEncode(trim($key)); $value = HttpUtils::urlEncode($value); $metaSize += strlen($key) + strlen($value); if ($metaSize > BosClient::MAX_USER_METADATA_SIZE) { throw new BceClientException('User metadata size should not be greater than ' . BosClient::MAX_USER_METADATA_SIZE); } $headers[$key] = $value; } }
/** * Send request to BCE. * * @param array $config * @param string $httpMethod The Http request method, uppercase. * @param string $path The resource path. * @param string|resource $body The Http request body. * @param array $headers The extra Http request headers. * @param array $params The extra Http url query strings. * @param SignerInterface $signer This function will generate authorization header. * @param resource|string $outputStream Write the Http response to this stream. * * @return \Guzzle\Http\Message\Response body and http_headers * * @throws BceClientException * @throws BceServiceException */ public function sendRequest(array $config, $httpMethod, $path, $body, array $headers, array $params, SignerInterface $signer, $outputStream = null, $options = array()) { $headers[HttpHeaders::USER_AGENT] = sprintf('bce-sdk-php/%s/%s/%s', Bce::SDK_VERSION, php_uname(), phpversion()); if (!isset($headers[HttpHeaders::BCE_DATE])) { $now = new \DateTime(); $now->setTimezone(DateUtils::$UTC_TIMEZONE); $headers[HttpHeaders::BCE_DATE] = DateUtils::formatAlternateIso8601Date($now); } list($hostUrl, $hostHeader) = HttpUtils::parseEndpointFromConfig($config); $headers[HttpHeaders::HOST] = $hostHeader; $url = $hostUrl . HttpUtils::urlEncodeExceptSlash($path); $queryString = HttpUtils::getCanonicalQueryString($params, false); if ($queryString !== '') { $url .= "?{$queryString}"; } if (!isset($headers[HttpHeaders::CONTENT_LENGTH])) { $headers[HttpHeaders::CONTENT_LENGTH] = $this->guessContentLength($body); } $entityBody = null; if ($headers[HttpHeaders::CONTENT_LENGTH] == 0) { //if passing a stream and content length is 0, guzzle will remove //"Content-Length:0" from header, to work around this, we have to //set body to a empty string $entityBody = ""; } else { if (is_resource($body)) { $offset = ftell($body); $original = EntityBody::factory($body); $entityBody = new ReadLimitEntityBody($original, $headers[HttpHeaders::CONTENT_LENGTH], $offset); } else { $entityBody = $body; } } $headers[HttpHeaders::AUTHORIZATION] = $signer->sign($config[BceClientConfigOptions::CREDENTIALS], $httpMethod, $path, $headers, $params, $options); if (LogFactory::isDebugEnabled()) { $this->logger->debug('HTTP method: ' . $httpMethod); $this->logger->debug('HTTP url: ' . $url); $this->logger->debug('HTTP headers: ' . print_r($headers, true)); } $guzzleRequestOptions = array('exceptions' => false); if (isset($config[BceClientConfigOptions::CONNECTION_TIMEOUT_IN_MILLIS])) { $guzzleRequestOptions['connect_timeout'] = $config[BceClientConfigOptions::CONNECTION_TIMEOUT_IN_MILLIS] / 1000.0; } if (isset($config[BceClientConfigOptions::SOCKET_TIMEOUT_IN_MILLIS])) { $guzzleRequestOptions['timeout'] = $config[BceClientConfigOptions::SOCKET_TIMEOUT_IN_MILLIS] / 1000.0; } $guzzleRequest = $this->guzzleClient->createRequest($httpMethod, $url, $headers, $entityBody, $guzzleRequestOptions); if ($outputStream !== null) { $guzzleRequest->setResponseBody($outputStream); } // Send request try { $guzzleResponse = $this->guzzleClient->send($guzzleRequest); } catch (\Exception $e) { throw new BceClientException($e->getMessage()); } if ($guzzleResponse->isInformational()) { throw new BceClientException('Can not handle 1xx Http status code'); } elseif (!$guzzleResponse->isSuccessful()) { $requestId = $guzzleResponse->getHeader(HttpHeaders::BCE_REQUEST_ID); $message = $guzzleResponse->getReasonPhrase(); $code = null; if ($guzzleResponse->isContentType('json')) { try { $responseBody = $guzzleResponse->json(); if (isset($responseBody['message'])) { $message = $responseBody['message']; } if (isset($responseBody['code'])) { $code = $responseBody['code']; } } catch (\Exception $e) { // ignore this error $this->logger->warning('Fail to parse error response body: ' . $e->getMessage()); } } throw new BceServiceException($requestId, $code, $message, $guzzleResponse->getStatusCode()); } if ($outputStream === null) { $body = $guzzleResponse->getBody(true); } else { $body = null; // detach the stream so that it will not be closed when the response // is garbage collected. $guzzleResponse->getBody()->detachStream(); } return array('headers' => $this->parseHeaders($guzzleResponse), 'body' => $body); }
/** * @param $parameters array * @param $forSignature bool * @return string */ public static function getCanonicalQueryString(array $parameters, $forSignature) { if (count($parameters) == 0) { return ''; } $parameterStrings = array(); foreach ($parameters as $k => $v) { if ($forSignature && strcasecmp(HttpHeaders::AUTHORIZATION, $k) == 0) { continue; } if (!isset($k)) { throw new \InvalidArgumentException("parameter key should not be null"); } if (isset($v)) { $parameterStrings[] = HttpUtils::urlEncode($k) . '=' . HttpUtils::urlEncode((string) $v); } else { if ($forSignature) { $parameterStrings[] = HttpUtils::urlEncode($k) . '='; } else { $parameterStrings[] = HttpUtils::urlEncode($k); } } } sort($parameterStrings); return implode('&', $parameterStrings); }
/** * @param $path string * @return string */ private static function getCanonicalURIPath($path) { if (empty($path)) { return '/'; } else { if ($path[0] == '/') { return HttpUtils::urlEncodeExceptSlash($path); } else { return '/' . HttpUtils::urlEncodeExceptSlash($path); } } }