/** * Parse the AWS service name from a URL * * @param Url $url HTTP URL * * @return string Returns a service name (or empty string) * @link http://docs.aws.amazon.com/general/latest/gr/rande.html */ public static function parseServiceName(Url $url) { // The service name is the first part of the host $parts = explode('.', $url->getHost(), 2); // Special handling for S3 if (stripos($parts[0], 's3') === 0) { return 's3'; } return $parts[0]; }
/** * Calculate string to sign * * @param RequestInterface $request Request to generate a signature for * @param int $timestamp Timestamp to use for nonce * @param string $nonce * * @return string */ public function getStringToSign(RequestInterface $request, $timestamp, $nonce) { $params = $this->getParamsToSign($request, $timestamp, $nonce); // Convert booleans to strings. $params = $this->prepareParameters($params); // Build signing string from combined params $parameterString = clone $request->getQuery(); $parameterString->replace($params); $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); return strtoupper($request->getMethod()) . '&' . rawurlencode($url) . '&' . rawurlencode((string) $parameterString); }
public function setUrl($url) { if ($url instanceof Url) { $this->url = $url; } else { $this->url = Url::factory($url); } // Update the port and host header $this->setPort($this->url->getPort()); if ($this->url->getUsername() || $this->url->getPassword()) { $this->setAuth($this->url->getUsername(), $this->url->getPassword()); // Remove the auth info from the URL $this->url->setUsername(null); $this->url->setPassword(null); } return $this; }
public function signRequest(RequestInterface $request, CredentialsInterface $credentials) { $timestamp = $this->getTimestamp(); $longDate = gmdate(DateFormat::ISO8601, $timestamp); $shortDate = substr($longDate, 0, 8); // Remove any previously set Authorization headers so that retries work $request->removeHeader('Authorization'); // Requires a x-amz-date header or Date if ($request->hasHeader('x-amz-date') || !$request->hasHeader('Date')) { $request->setHeader('x-amz-date', $longDate); } else { $request->setHeader('Date', gmdate(DateFormat::RFC1123, $timestamp)); } // Add the security token if one is present if ($credentials->getSecurityToken()) { $request->setHeader('x-amz-security-token', $credentials->getSecurityToken()); } // Parse the service and region or use one that is explicitly set $region = $this->regionName; $service = $this->serviceName; if (!$region || !$service) { $url = Url::factory($request->getUrl()); $region = $region ?: HostNameUtils::parseRegionName($url); $service = $service ?: HostNameUtils::parseServiceName($url); } $credentialScope = $this->createScope($shortDate, $region, $service); $payload = $this->getPayload($request); $signingContext = $this->createSigningContext($request, $payload); $signingContext['string_to_sign'] = $this->createStringToSign($longDate, $credentialScope, $signingContext['canonical_request']); // Calculate the signing key using a series of derived keys $signingKey = $this->getSigningKey($shortDate, $region, $service, $credentials->getSecretKey()); $signature = hash_hmac('sha256', $signingContext['string_to_sign'], $signingKey); $request->setHeader('Authorization', "AWS4-HMAC-SHA256 " . "Credential={$credentials->getAccessKeyId()}/{$credentialScope}, " . "SignedHeaders={$signingContext['signed_headers']}, Signature={$signature}"); // Add debug information to the request $request->getParams()->set('aws.signature', $signingContext); }
/** * Create a redirect request for a specific request object * * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do * (e.g. redirect POST with GET). * * @param RequestInterface $request Request being redirected * @param RequestInterface $original Original request * @param int $statusCode Status code of the redirect * @param string $location Location header of the redirect * * @return RequestInterface Returns a new redirect request * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot */ protected function createRedirectRequest(RequestInterface $request, $statusCode, $location, RequestInterface $original) { $redirectRequest = null; $strict = $original->getParams()->get(self::STRICT_REDIRECTS); // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || !$strict && $statusCode <= 302)) { $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); } else { $redirectRequest = clone $request; } $redirectRequest->setIsRedirect(true); // Always use the same response body when redirecting $redirectRequest->setResponseBody($request->getResponseBody()); $location = Url::factory($location); // If the location is not absolute, then combine it with the original URL if (!$location->isAbsolute()) { $originalUrl = $redirectRequest->getUrl(true); // Remove query string parameters and just take what is present on the redirect Location header $originalUrl->getQuery()->clear(); $location = $originalUrl->combine((string) $location, true); } $redirectRequest->setUrl($location); // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) $redirectRequest->getEventDispatcher()->addListener('request.before_send', $func = function ($e) use(&$func, $request, $redirectRequest) { $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); }); // Rewind the entity body of the request if needed if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { $body = $redirectRequest->getBody(); // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails if ($body->ftell() && !$body->rewind()) { throw new CouldNotRewindStreamException('Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' . 'entity body of the request using setRewindFunction().'); } } return $redirectRequest; }
/** * Get the URL that this handle is connecting to * * @return Url */ public function getUrl() { return Url::factory($this->options->get(CURLOPT_URL)); }
public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) { if (!$uri) { $url = $this->getBaseUrl(); } else { if (!is_array($uri)) { $templateVars = null; } else { list($uri, $templateVars) = $uri; } if (strpos($uri, '://')) { // Use absolute URLs as-is $url = $this->expandTemplate($uri, $templateVars); } else { $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); } } // If default headers are provided, then merge them under any explicitly provided headers for the request if (count($this->defaultHeaders)) { if (!$headers) { $headers = $this->defaultHeaders->toArray(); } elseif (is_array($headers)) { $headers += $this->defaultHeaders->toArray(); } elseif ($headers instanceof Collection) { $headers = $headers->toArray() + $this->defaultHeaders->toArray(); } } return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); }
/** * Parse the bucket name from a request object * * @param RequestInterface $request Request to parse * * @return string */ private function parseBucketName(RequestInterface $request) { $baseUrl = Url::factory($request->getClient()->getBaseUrl()); $baseHost = $baseUrl->getHost(); $host = $request->getHost(); if (strpos($host, $baseHost) === false) { // Does not contain the base URL, so it's either a redirect, CNAME, or using a different region $baseHost = ''; // For every known S3 host, check if that host is present on the request $regions = $request->getClient()->getDescription()->getData('regions'); foreach ($regions as $region) { if (strpos($host, $region['hostname']) !== false) { // This host matches the request host. Tells use the region and endpoint-- we can derive the bucket $baseHost = $region['hostname']; break; } } // If no matching base URL was found, then assume that this is a CNAME, and the CNAME is the bucket if (!$baseHost) { return $host; } } // Remove the baseURL from the host of the request to attempt to determine the bucket name return trim(str_replace($baseHost, '', $request->getHost()), ' .'); }
public function fromParts($method, array $urlParts, $headers = null, $body = null, $protocol = 'HTTP', $protocolVersion = '1.1') { return $this->create($method, Url::buildUrl($urlParts), $headers, $body)->setProtocolVersion($protocolVersion); }
/** * Analyzes the provided data and turns it into useful data that can be * consumed and used to build an upload form * * @return PostObject */ public function prepareData() { // Validate required options $options = Collection::fromConfig($this->data, array('ttd' => '+1 hour', 'key' => '^${filename}')); // Format ttd option $ttd = $options['ttd']; $ttd = is_numeric($ttd) ? (int) $ttd : strtotime($ttd); unset($options['ttd']); // If a policy or policy callback were provided, extract those from the options $rawJsonPolicy = $options['policy']; $policyCallback = $options['policy_callback']; unset($options['policy'], $options['policy_callback']); // Setup policy document $policy = array('expiration' => gmdate(DateFormat::ISO8601_S3, $ttd), 'conditions' => array(array('bucket' => $this->bucket))); // Configure the endpoint/action $url = Url::factory($this->client->getBaseUrl()); if ($url->getScheme() === 'https' && strpos($this->bucket, '.') !== false) { // Use path-style URLs $url->setPath($this->bucket); } else { // Use virtual-style URLs $url->setHost($this->bucket . '.' . $url->getHost()); } // Setup basic form $this->formAttributes = array('action' => (string) $url, 'method' => 'POST', 'enctype' => 'multipart/form-data'); $this->formInputs = array('AWSAccessKeyId' => $this->client->getCredentials()->getAccessKeyId()); // Add success action status $status = (int) $options->get('success_action_status'); if ($status && in_array($status, array(200, 201, 204))) { $this->formInputs['success_action_status'] = (string) $status; $policy['conditions'][] = array('success_action_status' => (string) $status); unset($options['success_action_status']); } // Add other options foreach ($options as $key => $value) { $value = (string) $value; if ($value[0] === '^') { $value = substr($value, 1); $this->formInputs[$key] = $value; $value = preg_replace('/\\$\\{(\\w*)\\}/', '', $value); $policy['conditions'][] = array('starts-with', '$' . $key, $value); } else { $this->formInputs[$key] = $value; $policy['conditions'][] = array($key => $value); } } // Handle the policy $policy = is_callable($policyCallback) ? $policyCallback($policy, $this) : $policy; $this->jsonPolicy = $rawJsonPolicy ?: json_encode($policy); $this->applyPolicy(); return $this; }