/** * Constructor. * * @param string $body The body data. * @param int $status The status code. * @param array $headers The custom headers. */ public function __construct($body = 'php://memory', $status = 200, array $headers = array()) { if (!$body instanceof StreamInterface) { $body = new Stream($body, Stream::MODE_READ_WRITE_RESET); } foreach ($headers as $name => $value) { $value = HeaderHelper::allToArray($value); if (!HeaderHelper::arrayOnlyContainsString($value)) { throw new \InvalidArgumentException('Header values should ony have string.'); } if (!HeaderHelper::isValidName($name)) { throw new \InvalidArgumentException('Invalid header name'); } $normalized = strtolower($name); $this->headerNames[$normalized] = $name; $this->headers[$name] = $value; } $this->stream = $body; $this->statusCode = $status; }
/** * Class init. * * @param string|UriInterface $uri * @param string $method * @param string|StreamInterface $body * @param array $headers */ public function __construct($uri = null, $method = null, $body = 'php://memory', $headers = array()) { if (!$body instanceof StreamInterface) { $body = new Stream($body, Stream::MODE_READ_WRITE_RESET); } if (!$uri instanceof UriInterface) { $uri = new PsrUri((string) $uri); } foreach ($headers as $name => $value) { $value = HeaderHelper::allToArray($value); if (!HeaderHelper::arrayOnlyContainsString($value)) { throw new \InvalidArgumentException('Header values should ony have string.'); } if (!HeaderHelper::isValidName($name)) { throw new \InvalidArgumentException('Invalid header name'); } $normalized = strtolower($name); $this->headerNames[$normalized] = $name; $this->headers[$name] = $value; } $this->stream = $body; $this->method = $this->validateMethod($method); $this->uri = $uri; }
/** * Send a request to the server and return a Response object with the response. * * @param RequestInterface $request The request object to store request params. * * @return ResponseInterface * * @since 2.1 */ protected function doRequest(RequestInterface $request) { // Setup the cURL handle. $ch = curl_init(); // Set the request method. $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod(); // Don't wait for body when $method is HEAD $options[CURLOPT_NOBODY] = $request->getMethod() === 'HEAD'; // Initialize the certificate store $options[CURLOPT_CAINFO] = $this->getOption('certpath', __DIR__ . '/cacert.pem'); // Set HTTP Version switch ($request->getProtocolVersion()) { case '1.0': $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0; break; case '1.1': $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1; break; case '2.0': if (defined('CURL_HTTP_VERSION_2_0')) { $options[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0; } } // If data exists let's encode it and make sure our Content-type header is set. $data = (string) $request->getBody(); if (isset($data)) { // If the data is a scalar value simply add it to the cURL post fields. if (is_scalar($data) || strpos($request->getHeaderLine('Content-Type'), 'multipart/form-data') === 0) { $options[CURLOPT_POSTFIELDS] = $data; } else { $options[CURLOPT_POSTFIELDS] = http_build_query($data); } if (!$request->getHeaderLine('Content-Type')) { $request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'); } // Add the relevant headers. if (is_scalar($options[CURLOPT_POSTFIELDS])) { $request = $request->withHeader('Content-Length', strlen($options[CURLOPT_POSTFIELDS])); } } // Build the headers string for the request. if ($headers = $request->getHeaders()) { // Add the headers string into the stream context options array. $options[CURLOPT_HTTPHEADER] = HeaderHelper::toHeaderLine($headers); } // If an explicit timeout is given user it. if ($timeout = $this->getOption('timeout')) { $options[CURLOPT_TIMEOUT] = (int) $timeout; $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout; } // If an explicit user agent is given use it. if ($userAgent = $this->getOption('userAgent')) { $options[CURLOPT_USERAGENT] = $userAgent; } // Set the request URL. $options[CURLOPT_URL] = (string) $request->getRequestTarget(); // We want our headers. :-) $options[CURLOPT_HEADER] = true; // Return it... echoing it would be tacky. $options[CURLOPT_RETURNTRANSFER] = true; // Override the Expect header to prevent cURL from confusing itself in its own stupidity. // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/ $options[CURLOPT_HTTPHEADER][] = 'Expect:'; /* * Follow redirects if server config allows * @deprecated safe_mode is removed in PHP 5.4, check will be dropped when PHP 5.3 support is dropped */ if (!ini_get('safe_mode') && !ini_get('open_basedir')) { $options[CURLOPT_FOLLOWLOCATION] = (bool) isset($this->options['follow_location']) ? $this->options['follow_location'] : true; } // Set any custom transport options if ($this->getOption('options')) { foreach ((array) $this->getOption('options') as $key => $value) { $options[$key] = $value; } } // Set the cURL options. curl_setopt_array($ch, $options); // Execute the request and close the connection. $content = curl_exec($ch); if (!$this->getOption('allow_empty_result', false) && !trim($content)) { $message = curl_error($ch); // Error but nothing from cURL? Create our own $message = $message ?: 'No HTTP response received'; throw new \RuntimeException($message); } // Get the request information. $info = curl_getinfo($ch); // Close the connection. curl_close($ch); return $this->getResponse($content, $info); }
/** * createHeader * * @param string $name * * @return static */ protected function createHeader($name) { $new = clone $this; $normalized = strtolower($name); $name = HeaderHelper::normalizeHeaderName($name); $new->headerNames[$normalized] = $name; $new->headers[$name] = array(); return $new; }
/** * Send a request to the server and return a Response object with the response. * * @param RequestInterface $request The request object to store request params. * * @return ResponseInterface * * @since 2.1 */ protected function doRequest(RequestInterface $request) { // Create the stream context options array with the required method offset. $options = array('method' => $request->getMethod()); // Set HTTP Version $options['protocol_version'] = $request->getProtocolVersion(); // If data exists let's encode it and make sure our Content-Type header is set. $data = (string) $request->getBody(); if (isset($data)) { // If the data is a scalar value simply add it to the stream context options. if (is_scalar($data)) { $options['content'] = $data; } else { $options['content'] = http_build_query($data); } if (!$request->getHeader('Content-Type')) { $request = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8'); } // Add the relevant headers. $request = $request->withHeader('Content-Length', strlen($options['content'])); } // Speed up stream get URL // @see http://stackoverflow.com/questions/3629504/php-file-get-contents-very-slow-when-using-full-url // @see http://stackoverflow.com/questions/13679976/how-to-speed-up-file-get-contents $request = $request->withHeader('Connection', 'Close'); // Build the headers string for the request. if ($headers = $request->getHeaders()) { // Add the headers string into the stream context options array. $options['header'] = HeaderHelper::toHeaderLine($headers, true); } // If an explicit timeout is given user it. if ($this->getOption('timeout')) { $options['timeout'] = (int) $this->getOption('timeout'); } // If an explicit user agent is given use it. if ($this->getOption('userAgent')) { $options['user_agent'] = $this->getOption('userAgent'); } // Ignore HTTP errors so that we can capture them. $options['ignore_errors'] = 1; // Follow redirects. $options['follow_location'] = (int) $this->getOption('follow_location', 1); foreach ((array) $this->getOption('options') as $key => $value) { $options[$key] = $value; } // Create the stream context for the request. $context = stream_context_create(array('http' => $options)); // Capture PHP errors $php_errormsg = ''; $track_errors = ini_get('track_errors'); ini_set('track_errors', true); $connection = @fopen($request->getRequestTarget(), Stream::MODE_READ_ONLY_FROM_BEGIN, false, $context); if (!$connection) { if (!$php_errormsg) { // Error but nothing from php? Create our own $php_errormsg = sprintf('Could not connect to resource: %s', $request->getRequestTarget()); } // Restore error tracking to give control to the exception handler ini_set('track_errors', $track_errors); throw new \RuntimeException($php_errormsg); } $stream = new Stream($connection); if ($dest = $this->getOption('target_file')) { $content = ''; StreamHelper::copyTo($stream, $dest); } else { $content = $stream->getContents(); } $metadata = $stream->getMetadata(); $stream->close(); if (isset($metadata['wrapper_data']['headers'])) { $headers = $metadata['wrapper_data']['headers']; } elseif (isset($metadata['wrapper_data'])) { $headers = $metadata['wrapper_data']; } else { $headers = array(); } return $this->getResponse($headers, $content); }
/** * A simple method to quickly send attachment stream download. * * @param string|resource $source The file source, can be file path or resource. * @param ResponseInterface $response A custom Response object to contain your headers. * @param array $options Options to provide some settings, currently supports * "delay" and "filename". * * @return void */ public static function sendAttachment($source, ResponseInterface $response = null, $options = array()) { $stream = new Stream($source, 'r'); /** @var MessageInterface|ResponseInterface $response */ $response = $response ?: new Response(); $filename = null; if (is_string($source)) { $filename = pathinfo($source, PATHINFO_BASENAME); } if (isset($options['filename'])) { $filename = $options['filename']; } $response = HeaderHelper::prepareAttachmentHeaders($response, $filename); $response = $response->withBody($stream); $output = static::$outputObject; if (!$output instanceof StreamOutput) { $output = new StreamOutput(); } if (isset($options['delay'])) { $output->setDelay($options['delay']); } $output->respond($response); }
/** * Return an instance with the specified header appended with the given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $name Case-insensitive header field name to add. * @param mixed $value Header value(s). * * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withAddedHeader($name, $value) { $value = HeaderHelper::allToArray($value); if (!HeaderHelper::arrayOnlyContainsString($value)) { throw new \InvalidArgumentException('Header values should ony have string.'); } if (!HeaderHelper::isValidName($name)) { throw new \InvalidArgumentException('Invalid header name'); } $new = clone $this; if (!$this->hasHeader($name)) { $new = $new->createHeader($name); } $name = $new->getHeaderName($name); $new->headers[$name] = array_merge($new->headers[$name], $value); return $new; }
/** * testPrepareAttachmentHeaders * * @return void * * @covers \Windwalker\Http\Helper\HeaderHelper::prepareAttachmentHeaders */ public function testPrepareAttachmentHeaders() { $response = HeaderHelper::prepareAttachmentHeaders(new Response()); $this->assertEquals(array('application/octet-stream'), $response->getHeader('Content-Type')); $this->assertEquals(array('binary'), $response->getHeader('Content-Transfer-Encoding')); $this->assertEquals(array('no-store, no-cache, must-revalidate'), $response->getHeader('Cache-Control')); $this->assertEquals(array(), $response->getHeader('Content-Disposition')); $response = HeaderHelper::prepareAttachmentHeaders(new Response(), 'foo.zip'); $this->assertEquals(array('attachment; filename="foo.zip"'), $response->getHeader('Content-Disposition')); }
/** * Marshal the host and port from HTTP headers and/or the PHP environment * * @param string $host The uri host. * @param string $port The request port. * @param array $server The $_SERVER superglobal. * @param array $headers The headers variable from server. */ public static function getHostAndPortFromHeaders(&$host, &$port, array $server, array $headers) { if (HeaderHelper::getValue($headers, 'host', false)) { static::getHostAndPortFromHeader($host, $port, HeaderHelper::getValue($headers, 'host')); return; } if (!isset($server['SERVER_NAME'])) { return; } $host = $server['SERVER_NAME']; if (isset($server['SERVER_PORT'])) { $port = (int) $server['SERVER_PORT']; } if (!isset($server['SERVER_ADDR']) || !preg_match('/^\\[[0-9a-fA-F\\:]+\\]$/', $host)) { return; } // Handle Ipv6 $host = '[' . $server['SERVER_ADDR'] . ']'; $port = $port ?: 80; if ($port . ']' === substr($host, strrpos($host, ':') + 1)) { // The last digit of the IPv6-Address has been taken as port // Unset the port so the default port can be used $port = null; } }
/** * Send all response headers. * * @param ResponseInterface $response Response object to contain headers. * * @return Output Instance of $this to allow chaining. */ public function sendHeaders(ResponseInterface $response) { foreach ($response->getHeaders() as $header => $values) { $first = true; $header = HeaderHelper::normalizeHeaderName($header); foreach ($values as $value) { $this->header(sprintf('%s: %s', $header, $value), $first); $first = false; } } return $this; }