diff --git a/.coveralls.yml b/.coveralls.yml
index 53bda829c8..bc71b62f87 100644
--- a/.coveralls.yml
+++ b/.coveralls.yml
@@ -1,3 +1,2 @@
coverage_clover: clover.xml
json_path: coveralls-upload.json
-src_dir: src
diff --git a/.gitignore b/.gitignore
index a7fc91d633..f146c86138 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,9 @@
.*.sw*
.*.un~
nbproject
+doc/html/
tmp/
+zf-mkdoc-theme/
clover.xml
composer.lock
diff --git a/.travis.yml b/.travis.yml
index 7c39d1bac9..7995517e78 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,16 +4,24 @@ language: php
branches:
except:
- - /^release-.*$/
+ - /^release-\d+\.\d+\.\d+.*$/
- /^ghgfk-.*$/
cache:
directories:
- $HOME/.composer/cache
+ - vendor
+ - $HOME/.local
+ - zf-mkdoc-theme
env:
global:
- TESTS_ZEND_HTTP_CLIENT_ONLINE=true
+ - SITE_URL: https://zendframework.github.io/zend-http
+ - GH_USER_NAME: "Matthew Weier O'Phinney"
+ - GH_USER_EMAIL: matthew@weierophinney.net
+ - GH_REF: github.com/zendframework/zend-http.git
+ - secure: "dla3bBhLgsNCSTFaQJF41P56GoNTDOlZA4OGdu14xsTdtcbncPtZ33oojsxXH5XtdB7bYTpMTx7IC+2FEIrWvfbtaFSdOBoriZz+DuvZKCaD/M56H3q3gAumuXvLBH/R+14uFLNRViztP/6uUUVvwRGSnYub2byaX5xTWRelzQjU15p6l8DA7N7RB9hWeZq+41RhpH3DBZOFHpsy+ezy/GWu005CVdSkBG4p5wHW9hs7LBFqn+kN0Qa1zjX3G+p/Gn28+ro1FF5T8TK/hbI/0hDnVJUjSOUXi5VcsOFcnJ50XZBa42PYI6/P6zSI7uuqQzlsefGXslsnYcsmagmACACrnH0nHJB29jlIGzRLOSGpFzOLDjDjlSCOHwZjeLF6tGeEtBQMp+VNnM3GQb5YEaXEmvvt+m+LEvm8mD2w4A47yydYP6AoLBlGpEEORfqUAhSLOJLoXuGAb90wEe4mIRIJegqQQihWppIMWIVXS2kdGSwzA/WnAuyAKywUiXgCcxsW6Aa8DZl1vTgeBA2MkwsBB3IhAweRVaJHnv4ijVWTN0BrAd0naPB+8XzcXgbtpsoRl+Z5RFAFCJQ+nJucdFMkKyr1DotfspyhnbRw4gOZufjCzTQaox8/DbPS0dmb1Zbzb1t80uqPQmMwdtek4Ovm7IVX2iRW3pluS9PWg5Q="
matrix:
fast_finish: true
@@ -24,6 +32,8 @@ matrix:
- php: 5.6
env:
- EXECUTE_TEST_COVERALLS=true
+ - DEPLOY_DOCS="$(if [[ $TRAVIS_BRANCH == 'master' && $TRAVIS_PULL_REQUEST == 'false' ]]; then echo -n 'true' ; else echo -n 'false' ; fi)"
+ - PATH="$HOME/.local/bin:$PATH"
- php: 7
- php: hhvm
allow_failures:
@@ -45,6 +55,10 @@ script:
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; fi
- if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then ./vendor/bin/phpunit ; fi
- if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run ; fi
+ - if [[ $DEPLOY_DOCS == "true" && "$TRAVIS_TEST_RESULT" == "0" ]]; then wget -O theme-installer.sh "https://raw.githubusercontent.com/zendframework/zf-mkdoc-theme/master/theme-installer.sh" ; chmod 755 theme-installer.sh ; ./theme-installer.sh ; fi
+
+after_success:
+ - if [[ $DEPLOY_DOCS == "true" ]]; then echo "Preparing to build and deploy documentation" ; ./zf-mkdoc-theme/deploy.sh ; echo "Completed deploying documentation" ; fi
after_script:
- if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/coveralls ; fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d903395349..9953edd429 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,13 @@ All notable changes to this project will be documented in this file, in reverse
### Added
-- Nothing.
+- [#44](https://github.com/zendframework/zend-http/pull/44),
+ [#45](https://github.com/zendframework/zend-http/pull/45),
+ [#46](https://github.com/zendframework/zend-http/pull/46),
+ [#47](https://github.com/zendframework/zend-http/pull/47),
+ [#48](https://github.com/zendframework/zend-http/pull/48), and
+ [#49](https://github.com/zendframework/zend-http/pull/49) prepare the
+ documentation for publication at https://zendframework.github.io/zend-http/
### Deprecated
diff --git a/README.md b/README.md
index d7cdd3a63c..50fa784165 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,13 @@
[](https://secure.travis-ci.org/zendframework/zend-http)
[](https://coveralls.io/r/zendframework/zend-http?branch=master)
-`Zend\Http` is a primary foundational component of Zend Framework. Since much of
-what PHP does is web-based, specifically HTTP, it makes sense to have a performant,
-extensible, concise and consistent API to do all things HTTP.
+zend-http provides the HTTP message abstraction used by
+[zend-mvc](https://zendframework.github.io/zend-mvc/), and also provides an
+extensible, adapter-driven HTTP client library.
+This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as
+it predates that specification. For PSR-7 support, please see our
+[Diactoros component](https://zendframework.github.io/zend-diactoros/).
- File issues at https://github.com/zendframework/zend-http/issues
-- Documentation is at http://framework.zend.com/manual/current/en/index.html#zend-http
+- Documentation is at https://zendframework.github.io/zend-http/
diff --git a/doc/book/client/adapters.md b/doc/book/client/adapters.md
new file mode 100644
index 0000000000..5a676ef732
--- /dev/null
+++ b/doc/book/client/adapters.md
@@ -0,0 +1,529 @@
+# HTTP Client Connection Adapters
+
+`Zend\Http\Client` is based on a connection adapter design. The connection adapter is the object in
+charge of performing the actual connection to the server, as well as writing requests and reading
+responses. This connection adapter can be replaced, and you can create and extend the default
+connection adapters to suit your special needs, without the need to extend or replace the entire
+HTTP client class.
+
+Currently, zend-http provides four built-in client connection adapters:
+
+- `Zend\Http\Client\Adapter\Socket` (default)
+- `Zend\Http\Client\Adapter\Proxy`
+- `Zend\Http\Client\Adapter\Curl`
+- `Zend\Http\Client\Adapter\Test`
+
+The client can accept an adapter via either its `setAdapter()` method, or during
+instantiation via its `adapter` configuration option. When using configuration,
+the value of the `adapter` option may be one of:
+
+- an adapter instance
+- the fully qualified class name of an adapter
+
+## The Socket adapter
+
+The default connection adapter used when none is specified is the
+`Zend\Http\Client\Adapter\Socket` adapter. The `Socket` adapter is based on
+PHP's built-in `fsockopen()` function, and does not require any special
+extensions or compilation flags.
+
+The `Socket` adapter allows several extra configuration options that can be set
+via either the client's constructor or `setOptions()` method:
+
+Parameter | Description | Expected Type | Default Value
+---------------------|--------------------------------------------------------------------------------------|---------------|--------------
+`persistent` | Whether to use persistent TCP connections | boolean | `FALSE`
+`ssltransport` | SSL transport layer (eg. `sslv2`, `tls`) | string | `ssl`
+`sslcert` | Path to a PEM encoded SSL certificate | string | `NULL`
+`sslpassphrase` | Passphrase for the SSL certificate file | string | `NULL`
+`sslverifypeer` | Whether to verify the SSL peer | string | `TRUE`
+`sslcapath` | Path to SSL certificate directory | string | `NULL`
+`sslallowselfsigned` | Whether to allow self-signed certificates | string | `FALSE`
+`sslusecontext` | Enables proxied connections to use SSL even if the proxy connection itself does not. | boolean | `FALSE`
+
+> ### Persistent TCP connections
+>
+> Using persistent TCP connections can potentially speed up HTTP requests, but
+> in most use cases, will have little positive effect and might overload the
+> HTTP server you are connecting to. It is recommended to use persistent TCP
+> connections only if you connect to the same server very frequently, and are
+> sure that the server is capable of handling a large number of concurrent
+> connections. In any case you are encouraged to benchmark the effect of
+> persistent connections on both the client speed and server load before using
+> this option.
+>
+> Additionally, when using persistent connections, we recommend enabling
+> Keep-Alive HTTP requests as described in [the client configuration section](intro.md#configuration);
+> otherwise persistent connections might have little or no effect.
+
+> ### HTTPS SSL stream parameters
+>
+> `ssltransport`, `sslcert` and `sslpassphrase` are only relevant when
+> connecting using HTTPS. While the default SSL/TLS settings should work for
+> most applications, you might need to change them if the server you are
+> connecting to requires special client setup. If so, please read the
+> [PHP manual chapter on SSL and TLS transport options](http://php.net/transports.inet).
+
+### Changing the HTTPS transport layer
+
+```php
+use Zend\Http\Client;
+
+// Set the configuration parameters
+$config = [
+ 'adapter' => Client\Adapter\Socket::class,
+ 'ssltransport' => 'tls',
+];
+
+// Instantiate a client object
+$client = new Client('https://www.example.com', $config);
+
+// The following request will be sent over a TLS secure connection.
+$response = $client->send();
+```
+
+The result of the example above will be similar to opening a TCP connection
+using the following PHP command:
+
+```php
+fsockopen('tls://www.example.com', 443);
+```
+
+### Customizing and accessing the Socket adapter stream context
+
+`Zend\Http\Client\Adapter\Socket` provides direct access to the underlying
+[stream context](http://php.net/stream.contexts) used to connect to the remote
+server. This allows the user to pass specific options and parameters to the TCP
+stream, and to the SSL wrapper in case of HTTPS connections.
+
+You can access the stream context using the following methods of
+`Zend\Http\Client\Adapter\Socket`:
+
+- `setStreamContext($context)`: Sets the stream context to be used by the
+ adapter. Can accept either a stream context resource created using the
+ [stream_context_create()](http://php.net/stream_context_create) PHP function,
+ or an array of stream context options, in the same format provided to this
+ function. Providing an array will create a new stream context using these
+ options, and set it.
+- `getStreamContext()`: Get the current stream context of the adapter. If no
+ stream context was set, this method will create a default stream context and
+ return it. You can then set or get the value of different context options
+ using regular PHP stream context functions.
+
+#### Setting stream context options for the Socket adapter
+
+```php
+use Zend\Http\Client;
+
+// Array of options
+$options = [
+ 'socket' => [
+ // Bind local socket side to a specific interface
+ 'bindto' => '10.1.2.3:50505',
+ ],
+ 'ssl' => [
+ // Verify server side certificate,
+ // do not accept invalid or self-signed SSL certificates
+ 'verify_peer' => true,
+ 'allow_self_signed' => false,
+
+ // Capture the peer's certificate
+ 'capture_peer_cert' => true,
+ ],
+];
+
+// Create an adapter object and attach it to the HTTP client:
+$adapter = new Client\Adapter\Socket();
+$client = new Client();
+$client->setAdapter($adapter);
+
+// Method 1: pass the options array to setStreamContext():
+$adapter->setStreamContext($options);
+
+// Method 2: create a stream context and pass it to setStreamContext():
+$context = stream_context_create($options);
+$adapter->setStreamContext($context);
+
+// Method 3: get the default stream context and set the options on it:
+$context = $adapter->getStreamContext();
+stream_context_set_option($context, $options);
+
+// Now, perform the request:
+$response = $client->send();
+
+// If everything went well, you can now access the context again:
+$opts = stream_context_get_options($adapter->getStreamContext());
+echo $opts['ssl']['peer_certificate'];
+```
+
+> #### Set stream context options prior to requests
+>
+> Note that you must set any stream context options before using the adapter to
+> perform actual requests. If no context is set before performing HTTP requests
+> with the `Socket` adapter, a default stream context will be created. This
+> context resource could be accessed after performing any requests using the
+> `getStreamContext()` method.
+
+## The Proxy adapter
+
+`Zend\Http\Client\Adapter\Proxy` is similar to the default `Socket` adapter; the
+primary difference is that the connection is made through an HTTP proxy server
+instead of a direct connection to the target server. This allows usage of
+`Zend\Http\Client` behind proxy servers, which is sometimes required for
+security or performance reasons.
+
+Using the `Proxy` adapter requires several additional client configuration
+parameters to be set, in addition to the default `adapter` option:
+
+Parameter | Description | Expected Type | Example Value
+-------------|--------------------------------|---------------|--------------
+`proxy_host` | Proxy server address | string | 'proxy.myhost.com'’ or '10.1.2.3'
+`proxy_port` | Proxy server TCP port | integer | 8080 (default) or 81
+`proxy_user` | Proxy user name, if required | string | 'shahar' or '' for none (default)
+`proxy_pass` | Proxy password, if required | string | 'secret' or '' for none (default)
+`proxy_auth` | Proxy HTTP authentication type | string | `Zend\Http\Client::AUTH_BASIC` (default)
+
+`proxy_host` should always be set; if it is not set, the client will fall back
+to a direct connection using `Zend\Http\Client\Adapter\Socket`. `proxy_port`
+defaults to '8080'; if your proxy listens on a different port, you must set this
+one as well.
+
+`proxy_user` and `proxy_pass` are only required if your proxy server requires
+you to authenticate. Providing these will add a 'Proxy-Authentication' header
+to the request. If your proxy does not require authentication, you can leave
+these two options out.
+
+`proxy_auth` sets the proxy authentication type, if your proxy server requires
+authentication. Possible values are similar to the ones accepted by the
+`Zend\Http\Client::setAuth()` method. Currently, only basic authentication
+(`Zend\Http\Client::AUTH_BASIC`) is supported.
+
+### Using Zend\\Http\\Client behind a proxy server
+
+```php
+use Zend\Http\Client;
+
+// Set the configuration parameters
+$config = [
+ 'adapter' => Client\Adapter\Proxy::class,
+ 'proxy_host' => 'proxy.int.zend.com',
+ 'proxy_port' => 8000,
+ 'proxy_user' => 'shahar.e',
+ 'proxy_pass' => 'bananashaped',
+];
+
+// Instantiate a client object
+$client = new Client('http://www.example.com', $config);
+
+// Continue working...
+```
+
+As mentioned, if `proxy_host` is not set or is set to a blank string, the
+connection will fall back to a regular direct connection. This allows you to
+write your application in a way that allows a proxy to be used optionally,
+according to a configuration parameter.
+
+> ### Access to stream context
+>
+> Since the proxy adapter inherits from `Zend\Http\Client\Adapter\Socket`, you
+> can use the stream context access method (see
+> [above](#setting-stream-context-options-for-the-socket-adapter)) to set stream
+> context options on `Proxy` connections.
+
+## The cURL Adapter
+
+cURL is a standard HTTP client library that is distributed with many operating
+systems and can be used in PHP via the cURL extension. It offers functionality
+for many special cases which can occur for a HTTP client and make it a perfect
+choice for a HTTP adapter. It supports secure connections, proxies, and multiple
+authentication mechanisms. In particular, it is very performant with regards to
+transfering large files.
+
+### Setting cURL options
+
+```php
+use Zend\Http\Client;
+
+$config = [
+ 'adapter' => Client\Adapter\Curl::class,
+ 'curloptions' => [CURLOPT_FOLLOWLOCATION => true],
+];
+$client = new Client($uri, $config);
+```
+
+By default, the cURL adapter is configured to behave exactly like the `Socket`
+adapter, and it also accepts the same configuration parameters as the `Socket`
+and `Proxy` adapters. You can also change the cURL options by either specifying
+the 'curloptions' key in the constructor of the adapter, or by calling
+`setCurlOption($name, $value)`; option names correspond to the `CURL_*`
+constants of the cURL extension. You can get access to the underling cURL handle
+by calling `$adapter->getHandle();`
+
+### Transfering files by handle
+
+You can use cURL to transfer very large files over HTTP by filehandle.
+
+```php
+use Zend\Http\Client;
+
+$putFileSize = filesize('filepath');
+$putFileHandle = fopen('filepath', 'r');
+
+$adapter = new Client\Adapter\Curl();
+$client = new Client();
+$client->setAdapter($adapter);
+$client->setMethod('PUT');
+$adapter->setOptions([
+ 'curloptions' => [
+ CURLOPT_INFILE => $putFileHandle,
+ CURLOPT_INFILESIZE => $putFileSize,
+ ],
+]);
+$client->send();
+```
+
+## The Test adapter
+
+Testing code that relies on HTTP connections poses difficulties. For example,
+testing an application that pulls an RSS feed from a remote server will require
+a network connection, which is not always available.
+
+`Zend\Http\Client\Adapter\Test` provides a solution for these situations and
+acts as a mock object for unit tests. You can write your application to use
+`Zend\Http\Client`, and, when testing (either in your unit test suite, or in
+non-production environments), replace the default adapter with the test adapter,
+allowing you to run tests without actually performing server connections.
+
+`Zend\Http\Client\Adapter\Test` provides two additional methods, `setResponse()`
+and `addResponse()`. Each takes one parameter, which represents an HTTP
+response as either text or a `Zend\Http\Response` object. `setResponse()` sets
+an individual response to always return from any request; `addResponse()` allows
+aggregating a sequence of responses. In both cases, responses are returned
+without performing actual HTTP requests.
+
+### Testing against a single HTTP response stub
+
+```php
+use Zend\Http\Client;
+
+// Instantiate a new adapter and client
+$adapter = new Client\Adapter\Test();
+$client = new Client(
+ 'http://www.example.com',
+ ['adapter' => $adapter]
+);
+
+// Set the expected response
+$adapter->setResponse(
+ "HTTP/1.1 200 OK\r\n"
+ . "Content-type: text/xml\r\n"
+ . "\r\n"
+ . ''
+ . ''
+ . ' '
+ . ' Premature Optimization'
+ // and so on...
+ . ''
+);
+
+$response = $client->send();
+// .. continue parsing $response..
+```
+
+The above example shows how you can preset your HTTP client to return the
+response you need. Then, you can continue testing your own code, without being
+dependent on a network connection, the server's response, etc. In this case, the
+test would continue to check how the application parses the XML in the response
+body.
+
+Sometimes, a single method call to an object can result in that object
+performing multiple HTTP transactions. In this case, it's not possible to use
+`setResponse()` alone because there's no opportunity to set the next response(s)
+your program might need before returning to the caller.
+
+### Testing Against Multiple HTTP Response Stubs
+
+```php
+use Zend\Http\Client;
+
+// Instantiate a new adapter and client
+$adapter = new Client\Adapter\Test();
+$client = new Client(
+ 'http://www.example.com',
+ ['adapter' => $adapter]
+);
+
+// Set the first expected response
+$adapter->setResponse(
+ "HTTP/1.1 302 Found\r\n"
+ . "Location: /\r\n"
+ . "Content-Type: text/html\r\n"
+ . "\r\n"
+ . ''
+ . '
Moved'
+ . '
This page has moved.
'
+ . ''
+);
+
+// Set the next successive response
+$adapter->addResponse(
+ "HTTP/1.1 200 OK\r\n"
+ . "Content-Type: text/html\r\n"
+ . "\r\n"
+ . ''
+ . ' My Pet Store Home Page'
+ . '
...
'
+ . ''
+);
+
+// inject the http client object ($client) into your object
+// being tested and then test your object's behavior below
+```
+
+The `setResponse()` method clears any responses in the adapter's buffer and sets
+the first response that will be returned. The `addResponse()` method will add
+successive responses.
+
+The responses will be replayed in the order that they were added. If more
+requests are made than the number of responses stored, the responses will cycle
+again in order.
+
+In the example above, the adapter is configured to test your object's behavior
+when it encounters a 302 redirect. Depending on your application, following a
+redirect may or may not be desired behavior. In our example, we expect that the
+redirect will be followed and we configure the test adapter to help us test
+this. The initial 302 response is set up with the `setResponse()` method and the
+200 response to be returned next is added with the `addResponse()` method. After
+configuring the test adapter, inject the HTTP client containing the adapter into
+your object under test and test its behavior.
+
+### Forcing the adapter to fail
+
+If you need the adapter to fail on demand you can use
+`setNextRequestWillFail($flag)`. The method will cause the next call to
+`connect()` to throw an `Zend\Http\Client\Adapter\Exception\RuntimeException`.
+This can be useful when our application caches content from an external site (in
+case the site goes down) and you want to test this feature.
+
+```php
+use Zend\Http\Client;
+
+// Instantiate a new adapter and client
+$adapter = new Client\Adapter\Test();
+$client = new Client(
+ 'http://www.example.com',
+ ['adapter' => $adapter]
+);
+
+// Force the next request to fail with an exception
+$adapter->setNextRequestWillFail(true);
+
+try {
+ // This call will result in an exception.
+ $client->send();
+} catch (Client\Adapter\Exception\RuntimeException $e) {
+ // ...
+}
+
+// Further requests will work as expected until
+// you call setNextRequestWillFail(true) again
+```
+
+## Creating your own connection adapters
+
+`Zend\Http\Client` has been designed so that you can create and use your own
+connection adapters. You could, for example, create a connection adapter that
+uses persistent sockets, or a connection adapter with caching abilities, and use
+them as needed in your application.
+
+In order to do so, you must create your own adapter class that implements
+`Zend\Http\Client\Adapter\AdapterInterface`. The following example shows the
+skeleton of a user-implemented adapter class. All the public functions defined
+in this example must be defined in your adapter as well.
+
+```php
+namespace MyApp\Http\Client\Adapter;
+
+use Zend\Http\Client\Adapter\AdapterInterface;
+
+class BananaProtocol implements AdapterInterface
+{
+ /**
+ * Set Adapter Options
+ *
+ * @param array $config
+ */
+ public function setOptions($config = [])
+ {
+ // This rarely changes - you should usually copy the
+ // implementation in Zend\Http\Client\Adapter\Socket.
+ }
+
+ /**
+ * Connect to the remote server
+ *
+ * @param string $host
+ * @param int $port
+ * @param boolean $secure
+ */
+ public function connect($host, $port = 80, $secure = false)
+ {
+ // Set up the connection to the remote server
+ }
+
+ /**
+ * Send request to the remote server
+ *
+ * @param string $method
+ * @param Zend\Uri\Http $url
+ * @param string $http_ver
+ * @param array $headers
+ * @param string $body
+ * @return string Request as text
+ */
+ public function write(
+ $method,
+ $url,
+ $http_ver = '1.1',
+ $headers = [],
+ $body = ''
+ ) {
+ // Send request to the remote server.
+ // This function is expected to return the full request
+ // (headers and body) as a string
+ }
+
+ /**
+ * Read response from server
+ *
+ * @return string
+ */
+ public function read()
+ {
+ // Read response from remote server and return it as a string
+ }
+
+ /**
+ * Close the connection to the server
+ *
+ */
+ public function close()
+ {
+ // Close the connection to the remote server - called last.
+ }
+}
+```
+
+Use the adapter as you would any other:
+
+```php
+use MyApp\Http\Client\Adapter\BananaProtocol;
+use Zend\Http\Client;
+
+$client = new Client([
+ 'adapter' => BananaProtocol::class,
+]);
+```
diff --git a/doc/book/client/advanced.md b/doc/book/client/advanced.md
new file mode 100644
index 0000000000..849b1f0925
--- /dev/null
+++ b/doc/book/client/advanced.md
@@ -0,0 +1,357 @@
+# HTTP Client Advanced Usage
+
+## HTTP redirections
+
+`Zend\Http\Client` automatically handles HTTP redirections, and by default
+will follow up to 5 redirections. This can be changed by setting the
+`maxredirects` configuration parameter.
+
+According to the HTTP/1.1 RFC, HTTP 301 and 302 responses should be treated by
+the client by resending the same request to the specified location, using the
+same request method. However, most clients to not implement this and always use
+a `GET` request when redirecting. By default, `Zend\Http\Client` does the same;
+when redirecting on a 301 or 302 response, all query and body parameters are
+reset, and a `GET` request is sent to the new location. This behavior can be
+changed by setting the `strictredirects` configuration parameter to boolean
+`TRUE`:
+
+```php
+// Strict Redirections
+$client->setOptions(['strictredirects' => true]);
+
+// Non-strict Redirections
+$client->setOptions(['strictredirects' => false]);
+```
+
+You can always get the number of redirections done after sending a request
+using the `getRedirectionsCount()` method.
+
+## Adding cookies and using cookie persistence
+
+`Zend\Http\Client` provides an interface for adding cookies to your request, so
+that no direct header modification is required. Cookies can be added using
+either the addCookie() or `setCookies()` method. `addCookie()` can accept
+either a name and value, a `SetCookie` header instance, or an array of
+`SetCookie` header instances.
+
+```php
+use Zend\Http\Header\SetCookie;
+
+// Basic usage: provide a cookie name and cookie value:
+$client->addCookie('flavor', 'chocolate chips');
+
+// Or provide a SetCookie instance:
+$cookie = SetCookie::fromString('Set-Cookie: flavor=chocolate%20chips');
+$client->addCookie($cookie);
+
+// Multiple cookies can be set at once by providing an
+// array of SetCookie instances:
+$cookies = [
+ SetCookie::fromString('Set-Cookie: flavorOne=chocolate%20chips'),
+ SetCookie::fromString('Set-Cookie: flavorTwo=vanilla'),
+];
+$client->addCookie($cookies);
+```
+
+The `setCookies()` method works in a similar manner, except that it requires an
+array of cookie name/value pairs as its only argument, and also clears the
+cookie container before adding the new cookies:
+
+```php
+// setCookies accepts an array of cookie values as $name => $value
+$client->setCookies([
+ 'flavor' => 'chocolate chips',
+ 'amount' => 10,
+]);
+```
+
+See the [Headers documentation](../headers.md#setcookie) for more detail on the
+`SetCookie` class.
+
+### Enabling Cookie Stickiness
+
+`Zend\Http\Client` also provides a means for simplifying cookie "stickiness"
+— i.e., having the client internally store all sent and received cookies,
+and resending them on subsequent requests. — via the `Zend\Http\Cookies`
+class. This is useful when you need to log in to a remote site first and
+receive an authentication or session ID cookie before sending further requests.
+
+```php
+$headers = $client->getRequest()->getHeaders();
+$cookies = new Zend\Http\Cookies($headers);
+
+// First request: log in and start a session
+$client->setUri('http://example.com/login.php');
+$client->setParameterPost(['user' => 'h4x0r', 'password' => 'l33t']);
+$client->setMethod('POST');
+
+$response = $client->getResponse();
+$cookies->addCookiesFromResponse($response, $client->getUri());
+
+// Now we can send our next request
+$client->setUri('http://example.com/read_member_news.php');
+$client->setCookies($cookies->getMatchingCookies($client->getUri()));
+$client->setMethod('GET');
+```
+
+See the chapter on [cookies](cookies.md) for more detail.
+
+## Setting custom request headers
+
+Setting custom headers is performed by first fetching the header container from
+the client's `Zend\Http\Request` instance. This `Headers` container offers a
+number of methods for setting headers:
+
+```php
+use Zend\Http\Header;
+
+// Fetch the container
+$headers = $client->getRequest()->getHeaders();
+
+// Setting a single header using a name and value. Will not overwrite any //
+previously-added headers of the same name.
+$headers->addHeaderLine('Host', 'www.example.com');
+
+// You can also use a full header line:
+$headers->addHeaderLine('Host: www.example.com');
+
+// Sometimes you may want to use a HeaderInterface instance:
+$headers->addHeader(Header\Host::fromString('Host: www.example.com'));
+
+// You can also add multiple headers at once by passing an
+// array to addHeaders() using any of the formats below:
+$headers->addHeaders([
+ // Zend\Http\Header\* instance:
+ Header\Host::fromString('Host: www.example.com'),
+
+ // Header name/value pair:
+ 'Cookie' => 'PHPSESSID=1234567890abcdef1234567890abcdef',
+
+ // Raw header string:
+ 'Cookie: language=he',
+]);
+```
+
+`Zend\Http\Client` also provides a convenience method for setting request
+headers, `setHeaders()`. This method will create a new header container, add
+the specified headers, and then store the new header container in its
+`Zend\Http\Request` instance. As a consequence, any pre-existing headers will
+be erased:
+
+```php
+use Zend\Http\Header;
+
+// Setting multiple headers via the client; removes all existing headers,
+// replacing the request header container with the following:
+$client->setHeaders([
+ Zend\Http\Header\Host::fromString('Host: www.example.com'),
+ ['Accept-Encoding' => 'gzip,deflate'],
+ 'X-Powered-By: Zend Framework',
+]);
+```
+
+## File uploads
+
+You can upload files through HTTP using the `setFileUpload()` method. This
+method takes a file name as the first parameter, a form name as the second
+parameter, and data as a third optional parameter. If the third data parameter
+is `NULL`, the first file name parameter is considered to be a real file on
+disk, and `Zend\Http\Client` will try to read this file and upload it. If the
+data parameter is not `NULL`, the first file name parameter will be sent as the
+file name, but no actual file needs to exist on the disk. The second form name
+parameter is always required, and is equivalent to the "name" attribute of an
+`` tag, if the file was to be uploaded through an HTML form. A fourth
+optional parameter provides the file's `Content-Type`. If not specified, and
+`Zend\Http\Client` reads the file from the disk, the `mime_content_type()`
+function will be used to guess the file's content type, if it is available. In
+any case, the default MIME type will be `application/octet-stream`.
+
+```php
+// Uploading arbitrary data as a file:
+$text = 'this is some plain text';
+$client->setFileUpload('some_text.txt', 'upload', $text, 'text/plain');
+
+// Uploading an existing file:
+$client->setFileUpload('/tmp/Backup.tar.gz', 'bufile');
+
+// Send the files:
+$client->setMethod('POST');
+$client->send();
+```
+
+In the first example, the `$text` variable is uploaded and will be available as
+`$_FILES['upload']` on the server side. In the second example, the existing
+file `/tmp/Backup.tar.gz` is uploaded to the server and will be available as
+`$_FILES['bufile']`. The content type will be guessed automatically if
+possible, defaulting to `application/octet-stream`.
+
+> ### Uploading files
+>
+> When uploading files, the HTTP request `Content-Type` is automatically set to
+> `multipart/form-data`. Keep in mind that you must send a POST or PUT request
+> in order to upload files; most servers will ignore the request body on other
+> request methods.
+
+## Sending raw POST data
+
+You can send raw POST data via `Zend\Http\Client` using the `setRawBody()`
+method. This method takes one parameter: the data to send in the request body.
+When sending raw POST data, it is advisable to also set the encoding type using
+`setEncType()`.
+
+```php
+$xml = ''
+ . ' Islands in the Stream'
+ . ' Ernest Hemingway'
+ . ' 1970'
+ . '';
+$client->setMethod('POST');
+$client->setRawBody($xml);
+$client->setEncType('text/xml');
+$client->send();
+```
+
+The data should be available on the server side through PHP's `php://input`
+stream.
+
+> ### Raw POST data overrides other content
+>
+> Setting raw POST data for a request will override any POST parameters or file
+> uploads; you should not try to use both on the same request. Keep in mind
+> that most servers will ignore the request body unless you send a POST
+> request.
+
+## HTTP authentication
+
+Currently, `Zend\Http\Client` only supports basic HTTP authentication. This feature is utilized
+using the `setAuth()` method, or by specifying a username and a password in the URI. The `setAuth()`
+method takes 3 parameters: the user name, the password and an optional authentication type
+parameter.
+
+```php
+use Zend\Http\Client;
+
+// Using basic authentication
+$client->setAuth('shahar', 'myPassword!', Client::AUTH_BASIC);
+
+// Since basic auth is default, you can just do this:
+$client->setAuth('shahar', 'myPassword!');
+
+// You can also specify username and password in the URI
+$client->setUri('http://christer:secret@example.com');
+```
+
+## Sending multiple requests with the same client
+
+`Zend\Http\Client` was also designed specifically to handle several consecutive
+requests with the same object. This is useful in cases where a script requires
+data to be fetched from several places, or when accessing a specific HTTP
+resource requires logging in and obtaining a session cookie, for example.
+
+When performing several requests to the same host, it is highly recommended to
+enable the 'keepalive' configuration flag. This way, if the server supports
+keep-alive connections, the connection to the server will only be closed once
+all requests are done and the `Client` object is destroyed. This prevents the
+overhead of opening and closing TCP connections to the server.
+
+When you perform several requests with the same client, but want to make sure
+all the request-specific parameters are cleared, you should use the
+`resetParameters()` method. This ensures that GET and POST parameters, request
+body, and request headers are reset and are not reused in the next request.
+
+> ### Resetting parameters
+>
+> Note that cookies are not reset by default when the `resetParameters()`
+> method is used. To clean all cookies as well, use `resetParameters(true)`, or
+> call `clearCookies()` after calling `resetParameters()`.
+
+Another feature designed specifically for consecutive requests is the
+`Zend\Http\Cookies` object. This "Cookie Jar" allow you to save cookies set by
+the server in a request, and send them back on consecutive requests
+transparently. This allows, for example, going through an authentication
+request before sending the actual data-fetching request.
+
+If your application requires one authentication request per user, and
+consecutive requests might be performed in more than one script in your
+application, it might be a good idea to store the `Cookies` object in the user's
+session. This way, you will only need to authenticate the user once every
+session.
+
+### Performing consecutive requests with one client
+
+```php
+use Zend\Http\Client;
+use Zend\Http\Cookies;
+
+// First, instantiate the client
+$client = new Client(
+ 'http://www.example.com/fetchdata.php',
+ ['keepalive' => true]
+);
+
+// Do we have the cookies stored in our session?
+if (isset($_SESSION['cookiejar'])
+ && $_SESSION['cookiejar'] instanceof Cookies
+) {
+ $cookieJar = $_SESSION['cookiejar'];
+} else {
+ // If we don't, authenticate and store cookies
+ $client->setUri('http://www.example.com/login.php');
+ $client->setParameterPost([
+ 'user' => 'shahar',
+ 'pass' => 'somesecret',
+ ]);
+ $response = $client->setMethod('POST')->send();
+ $cookieJar = Cookies::fromResponse($response);
+
+ // Now, clear parameters and set the URI to the original one
+ // (note that the cookies that were set by the server are now
+ // stored in the jar)
+ $client->resetParameters();
+ $client->setUri('http://www.example.com/fetchdata.php');
+}
+
+// Add the cookies to the new request
+$client->setCookies($cookieJar->getMatchingCookies($client->getUri()));
+$response = $client->setMethod('GET')->send();
+
+// Store cookies in session, for next page
+$_SESSION['cookiejar'] = $cookieJar;
+```
+
+## Data streaming
+
+By default, `Zend\Http\Client` accepts and returns data as PHP strings.
+However, in many cases there are big files to be received, thus keeping them in
+memory might be unnecessary or too expensive. For these cases,
+`Zend\Http\Client` supports writing data to files (streams).
+
+In order to receive data from the server as stream, use `setStream()`. The
+single, optional argument specifies the filename where the data will be stored.
+If the argument is just `TRUE` (default), a temporary file will be used and
+will be deleted once the response object is destroyed. Setting the argument to
+`FALSE` disables the streaming functionality.
+
+When using streaming, the `send()` method will return an object of class
+`Zend\Http\Response\Stream`, which has two useful methods: `getStreamName()`
+will return the name of the file where the response is stored, and
+`getStream()` will return stream from which the response could be read.
+
+You can either write the response to pre-defined file, or use temporary file
+for storing it and send it out or write it to another file using regular stream
+functions.
+
+```php
+$client-setStream(); // will use temp file
+$response = $client-send();
+
+// copy file:
+copy($response-getStreamName(), 'my/downloads/file');
+
+// use stream:
+$fp = fopen('my/downloads/file2', 'w');
+stream_copy_to_stream($response-getStream(), $fp);
+
+// write to an existing file:
+$client-setStream('my/downloads/myfile')-send();
+```
diff --git a/doc/book/client/cookies.md b/doc/book/client/cookies.md
new file mode 100644
index 0000000000..00a773adaf
--- /dev/null
+++ b/doc/book/client/cookies.md
@@ -0,0 +1,128 @@
+# Client Cookies
+
+`Zend\Http\Cookies` can be used with `Zend\Http\Client` to manage sending
+cookies in the request and setting cookies from the response; it is populated
+from the `Set-Cookie` headers obtained from a client response, and then used to
+populate the `Cookie` headers for a client request. This is highly useful in
+cases where you need to maintain a user session over consecutive HTTP requests,
+automatically sending the session ID cookies when required. Additionally, the
+`Zend\Http\Cookies` object can be serialized and stored in `$_SESSION` when
+needed.
+
+`Zend\Http\Client` already provides methods for managing cookies for requests;
+`Zend\Http\Cookies` manages the parsing of `Set-Cookie` headers returned in the
+response, and allows persisting them. Additionally, `Cookies` can return a
+subset of cookies that match the current request, ensuring you are only sending
+relevant cookies.
+
+## Usage
+
+`Cookies` is an extension of `Zend\Http\Headers`, and inherits its methods. It
+can be instantiated without any arguments.
+
+```php
+use Zend\Http\Cookies;
+
+$cookies = new Cookies();
+```
+
+On your first client request, you likely won't have any cookies, so the
+instance does nothing.
+
+Once you've made your first request, you can start using it. Populate it from
+the response:
+
+```php
+$response = $client->send();
+
+$cookies->addCookiesFromResponse($response, $client->getUri());
+```
+
+Alternately, you can create your initial `Cookies` instance using the static `fromResponse()` method:
+
+```php
+$cookies = Cookies::fromResponse($response, $client->getUri());
+```
+
+On subsequent requests, we'll notify the client of our cookies. To do this, we
+should use the same URI we'll use for the request.
+
+```php
+$client->setUri($uri);
+$client->setCookies($cookies->getMatchingCookies($uri));
+```
+
+After the request, don't forget to add any cookies returned!
+
+Essentially, `Cookies` aggregates all cookies for our client interactions, and
+allows us to send only those relevant to a given request.
+
+## Serializing and caching cookies
+
+To cache cookies — e.g., to store in `$_SESSION`, or between job
+invocations — you will need to serialize them. `Zend\Http\Cookies`
+provides this functionality via the `getAllCookies()` method.
+
+If your cache storage allows array structures, use the `COOKIE_STRING_ARRAY` constant:
+
+```php
+$cookiesToCache = $cookies->getAllCookies($cookies::COOKIE_STRING_ARRAY);
+```
+
+If your cache storage only allows string values, use `COOKIE_STRING_CONCAT`:
+
+```php
+$cookiesToCache = $cookies->getAllCookies($cookies::COOKIE_STRING_CONCAT);
+```
+
+When you retrieve the value later, you can test its type to determine how to
+deserialize the values:
+
+```php
+use Zend\Http\Cookies;
+use Zend\Http\Headers;
+
+$cookies = new Cookies();
+
+if (is_array($cachedCookies)) {
+ foreach ($cachedCookies as $cookie) {
+ $cookies->addCookie($cookie);
+ }
+} elseif (is_string($cachedCookies)) {
+ foreach (Headers::fromString($cachedCookies) as $cookie) {
+ $cookies->addCookie($cookie);
+ }
+}
+```
+
+## Public methods
+
+Besides the methods demonstrated in the examples, `Zend\Http\Cookies` defines the following:
+
+Method signature | Description
+------------------------------------------------------------------- | -----------
+`static fromResponse(Response $response, string $refUri) : Cookies` | Create a `Cookies` instance from a response and the request URI. Parses all `Set-Cookie` headers, maps them to the URI, and aggregates them.
+`addCookie(string|SetCookie $cookie, string $refUri = null) : void` | Add a cookie, mapping it to the given URI. If no URI is provided, it will be inferred from the cookie value's domain and path.
+`addCookiesFromResponse(Response $response, string $refUri) : void` | Add all `Set-Cookie` values from the provided response, mapping to the given URI.
+`getAllCookies(int $retAs = self::COOKIE_OBJECT) : array|string` | Retrieve all cookies. Returned array will have either `SetCookie` instances (the default), strings for each `Set-Cookie` declaration, or a single string containing all declarations, based on the `COOKIE_*` constant used.
+`getCookie(/* ... */) : string|SetCookie` | Retrieve a single cookie by name for the given URI. See below for argument details.
+`getMatchingCookies(/* ... */) : array` | See below for details.
+`isEmpty() : bool` | Whether or not the instance aggregates any cookies currently.
+`reset() : void` | Clear all aggregated cookies from the instance.
+
+`getCookie()` accepts the following arguments, in the following order:
+
+Argument | Description
+---------------------------------- | -----------
+`string $uri` | URI to match when retrieving the cookie. Will use its protocol, domain, and path.
+`string $cookieName` | The specific cookie name to retrieve.
+`int $retAs = self::COOKIE_OBJECT` | How to return matched cookies; defaults to `SetCookie` objects. Can be any of the `Cookies::COOKIE_*` constant values.
+
+`getMatchingCookies()` accepts the following arguments, in the following order:
+
+Argument | Description
+---------------------------------- | -----------
+`string $uri` | URI to match when retrieving cookies. Will use its protocol, domain, and path.
+`bool $matchSessionCookies = true` | Whether or not to also return related session cookies.
+`int $retAs = self::COOKIE_OBJECT` | How to return matched cookies; defaults to `SetCookie` objects. Can be any of the `Cookies::COOKIE_*` constant values.
+`int $now = null` | Timestamp against which to match; defaults to `time()`. Any expired cookies will be ignored.
diff --git a/doc/book/client/intro.md b/doc/book/client/intro.md
new file mode 100644
index 0000000000..51766c1e18
--- /dev/null
+++ b/doc/book/client/intro.md
@@ -0,0 +1,258 @@
+# HTTP Client
+
+`Zend\Http\Client` provides an interface for performing Hyper-Text Transfer
+Protocol (HTTP) requests. `Zend\Http\Client` supports all basic features
+expected from an HTTP client, as well as some more complex features such as HTTP
+authentication and file uploads. Successful requests (and most unsuccessful ones
+too) return a `Zend\Http\Response` object, which provides access to the
+response's headers and body (see the chapter on [Responses](../response.md) for
+more details).
+
+## Quick Start
+
+The class constructor optionally accepts a URL as its first parameter (which can
+be either a string or a `Zend\Uri\Http` object), and an array or `Traversable`
+object containing configuration options. The `send()` method is used to submit
+the request to the remote server, and a `Zend\Http\Response` object is returned:
+
+```php
+use Zend\Http\Client;
+
+$client = new Client(
+ 'http://example.org',
+ [
+ 'maxredirects' => 0,
+ 'timeout' => 30,
+ ]
+);
+$response = $client->send();
+```
+
+Both constructor parameters can be left out, and set later using the `setUri()`
+and `setOptions()` methods:
+
+```php
+use Zend\Http\Client;
+
+$client = new Client();
+$client->setUri('http://example.org');
+$client->setOptions([
+ 'maxredirects' => 0,
+ 'timeout' => 30,
+]);
+$response = $client->send();
+```
+
+`Zend\Http\Client` can also dispatch requests using a separately configured
+`request` object (see the [Request](../request.md) manual for full details of
+the methods available):
+
+```php
+use Zend\Http\Client;
+use Zend\Http\Request;
+
+$request = new Request();
+$request->setUri('http://example.org');
+
+$client = new Client();
+
+$response = $client->send($request);
+```
+
+> ### URL validation
+>
+> `Zend\Http\Client` uses `Zend\Uri\Http` to validate URLs. See the
+> [zend-uri](http://framework.zend.com/manual/current/en/index.html#zend-uri)
+> documentation for more information.
+
+## Configuration
+
+The constructor and `setOptions()` method accept an associative array or
+`Traversable` instance containing configuration parameters. Setting these
+parameters is optional, as they all have default values.
+
+Parameter | Description | Expected Values | Default Value
+------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|--------------
+`maxredirects` | Maximum number of redirections to follow (0 = none) | integer | 5
+`strictredirects` | Whether to strictly follow the RFC when redirecting (see this section) | boolean | FALSE
+`useragent` | User agent identifier string (sent in request headers) | string | `Zend\Http\Client`
+`timeout` | Connection timeout (seconds) | integer | 10
+`httpversion` | HTTP protocol version (usually '1.1' or '1.0') | string | 1.1
+`adapter` | Connection adapter class to use (see this section) | mixed | `Zend\Http\Client\Adapter\Socket`
+`keepalive` | Whether to enable keep-alive connections with the server. Useful and might improve performance if several consecutive requests to the same server are performed. | boolean | FALSE
+`storeresponse` | Whether to store last response for later retrieval with getLastResponse(). If set to FALSE, getLastResponse() will return NULL. | boolean | TRUE
+`encodecookies` | Whether to pass the cookie value through urlencode/urldecode. Enabling this breaks support with some web servers. Disabling this limits the range of values the cookies can contain. | boolean | TRUE
+`outputstream` | Destination for streaming of received data (options: string (filename), true for temp file, false/null to disable streaming) | boolean | FALSE
+`rfc3986strict` | Whether to strictly adhere to RFC 3986 (in practice, this means replacing '+' with '%20') | boolean | FALSE
+
+The options are also passed to the adapter class upon instantiation, so the same
+configuration can be used for adapter configuration. See the
+[adapters](adapters.md) section for more information on the adapter-specific
+options available.
+
+## Examples
+
+### Performing a GET request
+
+GET is the default method used, and requires no special configuration.
+
+```php
+use Zend\Http\Client;
+
+$client = new Client('http://example.org');
+$response = $client->send();
+```
+
+### Using request methods other than GET
+
+The request method can be set using `setMethod()`. If no method is specified,
+the method set by the last `setMethod()` call is used. If `setMethod()` was
+never called, the default request method is `GET`.
+
+```php
+use Zend\Http\Client;
+
+$client = new Client('http://example.org');
+
+// Performing a POST request
+$client->setMethod('POST');
+$response = $client->send();
+```
+
+For convenience, `Zend\Http\Request` defines all request methods as class
+constants: `Zend\Http\Request::METHOD_GET`, `Zend\Http\Request::METHOD_POST` and
+so on.
+
+```php
+use Zend\Http\Client;
+use Zend\Http\Request;
+
+$client = new Client('http://example.org');
+
+// Performing a POST request
+$client->setMethod(Request::METHOD_POST);
+$response = $client->send();
+```
+
+### Setting query parameters
+
+Adding query parameters to an HTTP request can be done either by specifying them
+as part of the URL, or by using the `setParameterGet()` method. This method
+takes the query parameters as an associative array of name/value pairs.
+
+```php
+use Zend\Http\Client;
+$client = new Client();
+
+// This is equivalent to setting a URL in the Client's constructor:
+$client->setUri('http://example.com/index.php?knight=lancelot');
+
+// Adding several parameters with one call
+$client->setParameterGet([
+ 'first_name' => 'Bender',
+ 'middle_name' => 'Bending',
+ 'last_name' => 'Rodríguez',
+ 'made_in' => 'Mexico',
+]);
+```
+
+### Setting form-encoded body parameters
+
+While query parameters can be sent with every request method, other methods can
+accept parameters via the request body. In many cases, these are
+`application/x-www-form-urlencoded` parameters; zend-http allows you to specify
+such parameters usingthe `setParameterPost()` method, which is identical to the
+`setParameterGet()` method in structure.
+
+```php
+use Zend\Http\Client;
+
+$client = new Client();
+
+// Setting several POST parameters, one of them with several values
+$client->setParameterPost([
+ 'language' => 'es',
+ 'country' => 'ar',
+ 'selection' => [45, 32, 80],
+]);
+```
+
+Note that when sending `POST` requests (or an request allowing a request body),
+you can set both query and `POST` parameters. On the other hand, setting POST
+parameters on a `GET` request will not trigger an error, rendering it useless.
+
+### Connecting to SSL URLs
+
+If you are trying to connect to an SSL or TLS (https) URL and are using the
+default (`Zend\Http\Client\Adapter\Socket`) adapter, you may need to set the
+`sslcapath` configuration option in order to allow PHP to validate the SSL
+certificate:
+
+```php
+use Zend\Http\Client;
+
+$client = new Client(
+ 'https://example.org',
+ [
+ 'sslcapath' => '/etc/ssl/certs',
+ ]
+);
+$response = $client->send();
+```
+
+The exact path to use will vary depending on your operating system. Without this
+you'll get the exception "Unable to enable crypto on TCP connection" when trying
+to connect.
+
+Alternatively, you could switch to the curl adapter, which negotiates SSL
+connections more transparently:
+
+```php
+use Zend\Http\Client;
+
+$client = new Client(
+ 'https://example.org',
+ [
+ 'adapter' => 'Zend\Http\Client\Adapter\Curl',
+ ]
+);
+$response = $client->send();
+```
+
+## Complete Example
+
+```php
+use Zend\Http\Client;
+
+$client = new Client();
+$client->setUri('http://www.example.com');
+$client->setMethod('POST');
+$client->setParameterPost([
+ 'foo' => 'bar',
+]);
+
+$response = $client->send();
+
+if ($response->isSuccess()) {
+ // the POST was successful
+}
+```
+
+or the same thing, using a request object:
+
+```php
+use Zend\Http\Client;
+use Zend\Http\Request;
+
+$request = new Request();
+$request->setUri('http://www.example.com');
+$request->setMethod('POST');
+$request->getPost()->set('foo', 'bar');
+
+$client = new Client();
+$response = $client->send($request);
+
+if ($response->isSuccess()) {
+ // the POST was successful
+}
+```
diff --git a/doc/book/client/static.md b/doc/book/client/static.md
new file mode 100644
index 0000000000..e4240250e3
--- /dev/null
+++ b/doc/book/client/static.md
@@ -0,0 +1,66 @@
+# HTTP Client - Static Usage
+
+zend-http provides another client implementation, `Zend\Http\ClientStatic`, a
+static HTTP client which exposes a simplified API for quickly performing one-off `GET`
+and `POST` operations.
+
+## Quick Start
+
+```php
+use Zend\Http\ClientStatic;
+
+// Simple GET request
+$response = ClientStatic::get('http://example.org');
+
+// More complex GET request, specifying query string 'foo=bar' and adding a
+// custom header to request JSON data be returned (Accept: application/json):
+$response = ClientStatic::get(
+ 'http://example.org',
+ ['foo' => 'bar'],
+ ['Accept' => 'application/json']
+);
+
+// We can also do a POST request using the same format. Here we POST
+// login credentials (username/password) to a login page:
+$response = ClientStatic::post(
+ 'https://example.org/login.php',
+ [
+ 'username' => 'foo',
+ 'password' => 'bar',
+ ]
+);
+```
+
+## Available Methods
+
+### get()
+
+```php
+get(
+ string $url,
+ array $query = [],
+ array $headers = [],
+ mixed $body = null,
+ $clientOptions = null
+) : Response
+```
+
+Perform an HTTP `GET` request using the provided URL, query string variables,
+headers, and request body. The fifth parameter can be used to pass configuration
+options to the HTTP client instance.
+
+### post()
+
+```php
+post(
+ string $url,
+ array $params,
+ array $headers = [],
+ mixed $body = null,
+ $clientOptions = null
+) : Response
+```
+
+Perform an HTTP `POST` request using the provided URL, parameters, headers, and
+request body. The fifth parameter can be used to pass configuration options to
+the HTTP client instance.
diff --git a/doc/book/headers.md b/doc/book/headers.md
new file mode 100644
index 0000000000..4d03eaf595
--- /dev/null
+++ b/doc/book/headers.md
@@ -0,0 +1,594 @@
+# Headers
+
+`Zend\Http\Headers` is a container for HTTP headers. It is typically accessed as
+part of a `Zend\Http\Request` or `Zend\Http\Response` instance, via a
+`getHeaders()` call. The `Headers` container will lazily load actual
+`Zend\Http\Header\HeaderInterface` instances as to reduce the overhead of header
+specific parsing.
+
+The class under the `Zend\Http\Header` namespace are the domain specific
+implementations for the various types of headers that one might encounter during
+the typical HTTP request. If a header of unknown type is encountered, it will be
+implemented as a `Zend\Http\Header\GenericHeader` instance. See the below table
+for a list of the various HTTP headers and the API that is specific to each
+header type.
+
+## Quick Start
+
+The quickest way to get started interacting with header objects is by retrieving
+an already populated `Headers` container from a request or response instance.
+
+```php
+// $client is an instance of Zend\Http\Client
+
+// You can retrieve the request headers by first retrieving
+// the Request object and then calling getHeaders on it
+$requestHeaders = $client->getRequest()->getHeaders();
+
+// The same method also works for retrieving Response headers
+$responseHeaders = $client->getResponse()->getHeaders();
+```
+
+`Zend\Http\Headers` can also extract headers from a string:
+
+```php
+use Zend\Http\Headers;
+
+$headerString = << 0` if it's greater, and `= 0` if they are equal. See [strcmp](http://www.php.net/manual/en/function.strcmp.php).
+`date() | DateTime` | Return date for this header as an instance of `DateTime`.
+
+## AbstractLocation Methods
+
+`Zend\Http\Header\AbstractLocation` defines the following methods in addition to
+those it inherits from the [HeaderInterface](#headerinterface-methods). The
+`ContentLocation`, `Location`, and `Referer` header types inherit from it.
+
+For brevity, we map the following references to the following classes or
+namespaces:
+
+- `Uri`: `Zend\Uri\UriInterface`
+- `InvalidArgumentException`: `Zend\Http\Header\Exception\InvalidArgumentException`
+
+Method signature | Description
+-------------------------------- | -----------
+`setUri(string|Uri $uri) : self` | Set the URI for this header; throws `InvalidArgumentException` for invalid `$uri` arguments.
+`getUri() : string` | Return the URI for this header.
+`uri() : Uri` | Return the `Uri` instance for this header.
+
+## List of HTTP Header Types
+
+Some header classes expose methods for manipulating their value. The following
+list contains all of the classes available in the `Zend\Http\Header\*`
+namespace, as well as any specific methods they contain. Each class implements
+`Zend\Http\Header\HeaderInterface`.
+
+### Accept
+
+Extends [AbstractAccept](#abstractaccept-methods).
+
+Method signature | Description
+------------------------------------------------------------ | -----------
+`addMediaType(string $type, int|float $priority = 1) : self` | Add a media type, with the given priority.
+`hasMediaType(string $type): bool` | Does the header have the requested media type?
+
+### AcceptCharset
+
+Extends [AbstractAccept](#abstractaccept-methods).
+
+Method signature | Description
+---------------------------------------------------------- | -----------
+`addCharset(string $type, int|float $priority = 1) : self` | Add a charset, with the given priority.
+`hasCharset(string $type) : bool` | Does the header have the requested charset?
+
+### AcceptEncoding
+
+Extends [AbstractAccept](#abstractaccept-methods).
+
+Method signature | Description
+----------------------------------------------------------- | -----------
+`addEncoding(string $type, int|float $priority = 1) : self` | Add an encoding, with the given priority.
+`hasEncoding(string $type) : bool` | Does the header have the requested encoding?
+
+### AcceptLanguage
+
+Extends [AbstractAccept](#abstractaccept-methods).
+
+Method signature | Description
+---------------------------------------------------------- | -----------
+`addLanguage(string $type, int|float $priority = 1): self` | Add a language, with the given priority.
+`hasLanguage(string $type) : bool` | Does the header have the requested language?
+
+### AcceptRanges
+
+Method signature | Description
+--------------------------------------- | -----------
+`getRangeUnit() : mixed` | (unknown)
+`setRangeUnit(mixed $rangeUnit) : self` | (unkown)
+
+### Age
+
+Method signature | Description
+-------------------------------------- | -----------
+`getDeltaSeconds() : int` | Get number of seconds.
+`setDeltaSeconds(int $seconds) : self` | Set number of seconds
+
+### Allow
+
+Method signature | Description
+---------------------------------------------------- | -----------
+`getAllMethods() : string[]` | Get list of all defined methods.
+`getAllowedMethods() : string[]` | Get list of allowed methods.
+`allowMethods(array|string $allowedMethods) : self` | Allow methods or list of methods.
+`disallowMethods(array|string $allowedMethods) self` | Disallow methods or list of methods.
+`denyMethods(array|string $allowedMethods) : self` | Convenience alias for `disallowMethods()`.
+`isAllowedMethod(string $method) : bool` | Check whether method is allowed.
+
+### AuthenticationInfo
+
+No additional methods.
+
+### Authorization
+
+No additional methods.
+
+### CacheControl
+
+Method signature | Description
+------------------------------------------------------------- | -----------
+`isEmpty(): bool` | Checks if the internal directives array is empty.
+`addDirective(string $key, string|bool $value = true) : self` | Add a directive. For directives like `max-age=60`, call as `addDirective('max-age', 60)`. For directives like `private`, use the default `$value` (`true`).
+`hasDirective(string $key) : bool` | Check the internal directives array for a directive.
+`getDirective(string $key) : null|string` | Fetch the value of a directive from the internal directive array.
+`removeDirective(string $key) : self` | Remove a directive.
+
+### Connection
+
+Method signature | Description
+---------------------------------- | -----------
+`setValue($value) : self` | Set arbitrary header value. RFC allows any token as value; 'close' and 'keep-alive' are commonly used.
+`isPersistent() : bool` | Whether or not the connection is persistent.
+`setPersistent(bool $flag) : self` | Set Connection header to define persistent connection.
+
+### ContentDisposition
+
+No additional methods.
+
+### ContentEncoding
+
+No additional methods.
+
+### ContentLanguage
+
+No additional methods.
+
+### ContentLength
+
+No additional methods.
+
+### ContentLocation
+
+See [AbstractLocation](#abstractlocation-methods).
+
+### ContentMD5
+
+No additional methods.
+
+### ContentRange
+
+No additional methods.
+
+### ContentSecurityPolicy
+
+Method signature | Description
+--------------------------------------------------- | -----------
+`getDirectives(): array` | Retrieve the defined directives for the policy.
+`setDirective(string $name, array $sources) : self` | Set the directive with the given name to include the sources. See below for an example.
+
+As an example: an auction site wishes to load images from any URI, plugin
+content from a list of trusted media providers (including a content distribution
+network), and scripts only from a server under its control hosting sanitized
+Javacript:
+
+```php
+// http://www.w3.org/TR/2012/CR-CSP-20121115/#sample-policy-definitions
+$csp = new ContentSecurityPolicy();
+$csp->setDirective('default-src', []); // No sources
+$csp->setDirective('img-src', ['*']);
+$csp->setDirective('object-src', ['media1.example.com', 'media2.example.com', '*.cdn.example.com']);
+$csp->setDirective('script-src', ['trustedscripts.example.com']);
+```
+
+### ContentTransferEncoding
+
+No additional methods.
+
+### ContentType
+
+Method signature | Description
+------------------------------------------------- | -----------
+`match(array|string $matchAgainst) : bool|string` | Determine if the mediatype value in this header matches the provided criteria.
+`getMediaType() : string` | Get the media type.
+`setMediaType(string $mediaType) : self` | Set the media type.
+`getParameters() : array` | Get any additional content-type parameters currently set.
+`setParameters(array $parameters) : self` | Set additional content-type parameters.
+`getCharset() : null|string` | Get the content-type character set encoding, if any.
+`setCharset(string $charset) : self` | Set the content-type character set encoding.
+
+### Cookie
+
+Extends `ArrayObject`.
+
+Method signature | Description
+------------------------------------------------------- | -----------
+`static fromSetCookieArray(array $setCookies) : Cookie` | Create an instance from the `$_COOKIE` array, or one structured like it.
+`setEncodeValue(bool $encode) : self` | Set flag indicating whether or not to `urlencode()` the cookie values.
+`getEncodeValue() : bool` | Get flag indicating whether or not to `urlencode()` the cookie values.
+
+### Date
+
+See [AbstractDate](#abstractdate-methods).
+
+### Etag
+
+No additional methods.
+
+### Expect
+
+No additional methods.
+
+### Expires
+
+See [AbstractDate](#abstractdate-methods).
+
+### From
+
+No additional methods.
+
+### Host
+
+No additional methods.
+
+### IfMatch
+
+No additional methods.
+
+### IfModifiedSince
+
+See [AbstractDate](#abstractdate-methods).
+
+### IfNoneMatch
+
+No additional methods.
+
+### IfRange
+
+No additional methods.
+
+### IfUnmodifiedSince
+
+See [AbstractDate](#abstractdate-methods).
+
+### KeepAlive
+
+No additional methods.
+
+### LastModified
+
+See [AbstractDate](#abstractdate-methods).
+
+### Location
+
+See [AbstractLocation](#abstractlocation-methods).
+
+### MaxForwards
+
+No additional methods.
+
+### Origin
+
+No additional methods.
+
+### Pragma
+
+No additional methods.
+
+### ProxyAuthenticate
+
+Method signature | Description
+-------------------------------------------------- | -----------
+`toStringMultipleHeaders(array $headers) : string` | Creates a string representation when multiple values are present.
+
+### ProxyAuthorization
+
+No additional methods.
+
+### Range
+
+No additional methods.
+
+### Referer
+
+See [AbstractLocation](#abstractlocation-methods).
+
+### Refresh
+
+No additional methods.
+
+### RetryAfter
+
+See [AbstractDate](#abstractdate-methods).
+
+Method signature | Description
+------------------------------------ | -----------
+`setDeltaSeconds(int $delta) : self` | Set number of seconds.
+`getDeltaSeconds() : int` | Get number of seconds.
+
+### Server
+
+No additional methods.
+
+### SetCookie
+
+Method signature | Description
+--------------------------------------------------------------------- | -----------
+`static matchCookieDomain(string $cookieDomain, string $host) : bool` | Check if a cookie's domain matches a host name.
+`static matchCookiePath(string $cookiePath, string $path) : bool` | Check if a cookie's path matches a URL path.
+`getName() : string` | Retrieve the cookie name.
+`setName(string $name) : self` | Set the cookie name.
+`getValue() : string` | Retrieve the cookie value.
+`setValue(string $value) : self` | Set the cookie value.
+`getExpires() : int` | Retrieve the expiration date for the cookie.
+`setExpires(int|string $expires) : self` | Set the cookie expiration timestamp; null indicates a session cookie.
+`getPath() : string` | Retrieve the URI path the cookie is bound to.
+`setPath(string $path) : self` | Set the URI path the cookie is bound to.
+`getDomain() : string` | Retrieve the domain the cookie is bound to.
+`setDomain(string $domain) : self` | Set the domain the cookie is bound to.
+`getMaxAge() : int` | Retrieve the maximum age for the cookie.
+`setMaxAge(int|string $maxAge) : self` | Set the maximum age for the cookie.
+`getVersion() : int` | Retrieve the cookie version.
+`setVersion(int|string $version) : self` | Set the cookie version.
+`isSecure(): bool` | Whether the cookies contains the Secure flag.
+`setSecure(bool $secure) : self` | Set whether the cookies contain the Secure flag.
+`isHttponly() : bool` | Whether the cookies can be accessed via the HTTP protocol only.
+`setHttponly(bool $httponly) : self` | Set whether the cookies can be accessed only via HTTP protocol.
+`isExpired() : bool` | Whether the cookie is expired.
+`isSessionCookie() : bool` | Whether the cookie is a session cookie.
+`setQuoteFieldValue(bool $quotedValue) : self` | Set whether the value for this cookie should be quoted.
+`hasQuoteFieldValue() : bool` | Check whether the value for this cookie should be quoted.
+`isValidForRequest() : bool` | Whether the cookie is valid for a given request domain, path and isSecure.
+`match(string $uri, bool $matchSessionCookies, int $now) : bool` | Checks whether the cookie should be sent or not in a specific scenario.
+`toStringMultipleHeaders(array $headers) : string` | Returns string representation when multiple values are present.
+
+### TE
+
+No additional methods.
+
+### Trailer
+
+No additional methods.
+
+### TransferEncoding
+
+No additional methods.
+
+### Upgrade
+
+No additional methods.
+
+### UserAgent
+
+No additional methods.
+
+### Vary
+
+No additional methods.
+
+### Via
+
+No additional methods.
+
+### Warning
+
+No additional methods.
+
+### WWWAuthenticate
+
+Defines a `toStringMultipleHeaders(array $headers)` method for serializing to
+string when multiple values are present.
+
+## Examples
+
+### Retrieving headers from a Headers object
+
+```php
+// $client is an instance of Zend\Http\Client
+$response = $client->send();
+$headers = $response->getHeaders();
+
+// We can check if the Request contains a specific header by
+// using the ``has`` method. Returns boolean ``TRUE`` if at least
+// one matching header found, and ``FALSE`` otherwise
+$headers->has('Content-Type');
+
+// We can retrieve all instances of a specific header by using
+// the ``get`` method:
+$contentTypeHeaders = $headers->get('Content-Type');
+```
+
+There are three possibilities for the return value of the above call to the `get` method:
+
+- If no `Content-Type` header was set in the `Request`, `get` will return `false`.
+- If only one `Content-Type` header was set in the `Request`, `get` will return
+ an instance of `Zend\Http\Header\ContentType`.
+- If more than one `Content-Type` header was set in the `Request`, `get` will
+ return an `ArrayIterator` containing one `Zend\Http\Header\ContentType`
+ instance per header.
+
+### Adding headers to a Headers object
+
+```php
+use Zend\Http\Header;
+use Zend\Http\Headers;
+
+$headers = new Headers();
+
+// We can directly add any object that implements Zend\Http\Header\HeaderInterface
+$typeHeader = Header\ContentType::fromString('Content-Type: text/html');
+$headers->addHeader($typeHeader);
+
+// We can add headers using the raw string representation, either
+// passing the header name and value as separate arguments...
+$headers->addHeaderLine('Content-Type', 'text/html');
+
+// .. or we can pass the entire header as the only argument
+$headers->addHeaderLine('Content-Type: text/html');
+
+// We can also add headers in bulk using addHeaders, which accepts
+// an array of individual header definitions that can be in any of
+// the accepted formats outlined below:
+$headers->addHeaders([
+ // An object implementing Header\HeaderInterface
+ Header\ContentType::fromString('Content-Type: text/html'),
+
+ // A raw header string
+ 'Content-Type: text/html',
+
+ // We can also pass the header name as the array key and the
+ // header content as that array key's value
+ 'Content-Type' => 'text/html',
+]);
+```
+
+### Removing headers from a Headers object
+
+We can remove all headers of a specific type using the `removeHeader` method,
+which accepts a single object implementing `Zend\Http\Header\HeaderInterface`
+
+```php
+use ArrayIterator;
+use Zend\Http\Header\HeaderInterface;
+
+// $headers is a pre-configured instance of Zend\Http\Headers
+
+// We can also delete individual headers or groups of headers
+$matches = $headers->get('Content-Type');
+
+if ($matches instanceof ArrayIterator) {
+ // If more than one header was found, iterate over the collection
+ // and remove each one individually
+ foreach ($headers as $header) {
+ $headers->removeHeader($header);
+ }
+} elseif ($matches instanceof HeaderInterface) {
+ // If only a single header was found, remove it directly
+ $headers->removeHeader($header);
+}
+
+// In addition to this, we can clear all the headers currently stored in
+// the container by calling the clearHeaders() method
+$matches->clearHeaders();
+```
diff --git a/doc/book/index.html b/doc/book/index.html
new file mode 100644
index 0000000000..10b3ea6d70
--- /dev/null
+++ b/doc/book/index.html
@@ -0,0 +1,10 @@
+
+
+
zend-http
+
+
HTTP message and header abstractions, and HTTP client implementation. (Not a PSR-7 implementation; see Diactoros for PSR-7 support.
+
+
$ composer require zendframework/zend-http
+
+
+
diff --git a/doc/book/index.md b/doc/book/index.md
new file mode 120000
index 0000000000..fe84005413
--- /dev/null
+++ b/doc/book/index.md
@@ -0,0 +1 @@
+../../README.md
\ No newline at end of file
diff --git a/doc/book/intro.md b/doc/book/intro.md
new file mode 100644
index 0000000000..68b260a9ae
--- /dev/null
+++ b/doc/book/intro.md
@@ -0,0 +1,35 @@
+# Introduction
+
+zend-http provides the HTTP message abstraction used by
+[zend-mvc](https://zendframework.github.io/zend-mvc/), and also provides an
+extensible, adapter-driven HTTP client library. It provides the following
+abstractions:
+
+- Context-less `Request` and `Response` classes that expose a fluent API for
+ introspecting several aspects of HTTP messages:
+ - Request line information and response status information
+ - Parameters, such as those found in POST and GET
+ - Message Body
+ - Headers
+- A client implementation with various adapters that allow for sending requests
+ and introspecting responses.
+
+> ### Not PSR-7!
+>
+> This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as
+> it predates that specification. For PSR-7 support, please see our
+> [Diactoros component](https://zendframework.github.io/zend-diactoros/).
+
+## Zend\Http Request, Response and Headers
+
+The request, response and headers implementations of the zend-http component
+provides a fluent, object-oriented interface for introspecting information from
+all the various parts of an HTTP request or HTTP response. The primary classes
+are `Zend\Http\Request` and `Zend\Http\Response`. Both are “context-less”,
+meaning that they model a request or response in the same way whether it is
+presented by a client (to **send** a request and **receive** a response) or by a
+server (to **receive** a request and **send** a response). In other words,
+regardless of the context, the API remains the same for introspecting their
+various respective parts. Each attempts to fully model a request or response so
+that a developer can create these objects from a factory, or create and populate
+them manually.
diff --git a/doc/book/request.md b/doc/book/request.md
new file mode 100644
index 0000000000..d26677471b
--- /dev/null
+++ b/doc/book/request.md
@@ -0,0 +1,177 @@
+# The Request Class
+
+`Zend\Http\Request` is responsible for providing a fluent API that allows a
+developer to interact with all the various parts of an HTTP request.
+
+A typical HTTP request looks like this:
+
+```text
+| METHOD | URI | VERSION |
+| HEADERS |
+| BODY |
+```
+
+In simplified terms, the request consists of a method, URI, and HTTP version
+number; together, they make up the "Request Line." This line is followed by zero
+or more HTTP headers, which is followed by an empty line and then the request
+body; the body is typically used when a client wishes to send data — which
+could be urlencoded parameters, a JSON document, an XML document, or even one or
+more files — to the server. More information on the structure and
+specification of a HTTP request can be found in
+[RFC-2616 on the W3.org site](http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html).
+
+## Quick Start
+
+Request objects can either be created from the provided `fromString()` factory,
+or, if you wish to have a completely empty object to start with, by
+manually instantiating the `Zend\Http\Request` class with no parameters.
+
+```php
+use Zend\Http\Request;
+
+$request = Request::fromString(<<setMethod(Request::METHOD_POST);
+$request->setUri('/foo');
+$request->getHeaders()->addHeaders([
+ 'HeaderField1' => 'header-field-value1',
+ 'HeaderField2' => 'header-field-value2',
+]);
+$request->getPost()->set('foo', 'bar');
+```
+
+## Configuration Options
+
+No configuration options are available.
+
+## Available Methods
+
+The following table details available methods, their signatures, and a brief
+description. Note that the following references refer to the following
+fully qualified class names and/or namespaces:
+
+- `HeaderInterface`: `Zend\Http\Header\HeaderInterface`
+- `Headers`: `Zend\Http\Headers`
+- `Header`: `Zend\Http\Header`
+- `Parameters`: `Zend\Stdlib\ParametersInterface`
+- `Request`: `Zend\Http\Request`
+- `Uri`: `Zend\Uri\Http`
+
+Method signature | Description
+--------------------------------------------------------------------------- | -----------
+`static fromString(string $string) : Request` | A factory that produces a `Request` object from a well-formed HTTP request message string.
+`setMethod(string $method) : self` | Set the method for this request.
+`getMethod() : string` | Return the method for this request.
+`setUri(string|Uri $uri) : self` | Set the URI/URL for this request; this can be a string or an instance of `Zend\Uri\Http`.
+`getUri() : Uri` | Return the URI for this request object.
+`getUriString() : string` | Return the URI for this request object as a string.
+`setVersion(string $version) : self` | Set the HTTP version for this object, one of 1.0 or 1.1 (`Request::VERSION_10`, `Request::VERSION_11`).
+`getVersion() : string` | Return the HTTP version for this request.
+`setQuery(Parameters $query) : self` | Provide an alternate Parameter Container implementation for query parameters in this object. (This is NOT the primary API for value setting; for that, see `getQuery()`).
+`getQuery(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for query parameters or a single query parameter based on `$name`.
+`setPost(Parameters $post) : self` | Provide an alternate Parameter Container implementation for POST parameters in this object. (This is NOT the primary API for value setting; for that, see `getPost()`).
+`getPost(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for POST parameters or a single POST parameter, based on `$name`.
+`getCookie() : Header\Cookie` | Return the Cookie header, this is the same as calling `$request->getHeaders()->get('Cookie');`.
+`setFiles(Parameters $files) : self` | Provide an alternate Parameter Container implementation for file parameters in this object, (This is NOT the primary API for value setting; for that, see `getFiles()`).
+`getFiles(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for file parameters or a single file parameter, based on `$name`.
+`setHeaders(Headers $headers) : self` | Provide an alternate Parameter Container implementation for headers in this object, (this is NOT the primary API for value setting, for that see `getHeaders()`).
+`getHeaders(string|null $name, mixed|null $default) : mixed` | Return the container responsible for storing HTTP headers. This container exposes the primary API for manipulating headers set in the HTTP request. See the section on [Headers](headers.md) for more information. Return value is based on `$name`; `null` returns `Headers`, while a matched header returns a `Header\HeaderInterface` implementation for single-value headers or an `ArrayIterator` for multi-value headers.
+`setMetadata(string|int|array|Traversable $spec, mixed $value) : self` | Set message metadata. Non-destructive setting of message metadata; always adds to the metadata, never overwrites the entire metadata container.
+`getMetadata(null|string|int $key, null|mixed $default) : mixed` | Retrieve all metadata or a single metadatum as specified by key.
+`setContent(mixed $value) : self` | Set request body (content).
+`getContent() : mixed` | Get request body (content).
+`isOptions() : bool` | Is this an OPTIONS method request?
+`isGet() : bool` | Is this a GET method request?
+`isHead() : bool` | Is this a HEAD method request?
+`isPost() : bool` | Is this a POST method request?
+`isPut() : bool` | Is this a PUT method request?
+`isDelete() : bool` | Is this a DELETE method request?
+`isTrace() : bool` | Is this a TRACE method request?
+`isConnect() : bool` | Is this a CONNECT method request?
+`isPatch() : bool` | Is this a PATCH method request?
+`isXmlHttpRequest() : bool` | Is this a Javascript XMLHttpRequest?
+`isFlashRequest() : bool` | Is this a Flash request?
+`renderRequestLine() : string` | Return the formatted request line (first line) for this HTTP request.
+`toString() : string` | Returns string
+`__toString() : string` | Allow PHP casting of this object.
+
+## Examples
+
+### Generating a Request object from a string
+
+```php
+use Zend\Http\Request;
+
+$string = "GET /foo HTTP/1.1\r\n\r\nSome Content";
+$request = Request::fromString($string);
+
+$request->getMethod(); // returns Request::METHOD_GET
+$request->getUri(); // returns Zend\Uri\Http object
+$request->getUriString(); // returns '/foo'
+$request->getVersion(); // returns Request::VERSION_11 or '1.1'
+$request->getContent(); // returns 'Some Content'
+```
+
+### Retrieving and setting headers
+
+```php
+use Zend\Http\Request;
+use Zend\Http\Header\Cookie;
+
+$request = new Request();
+$request->getHeaders()->get('Content-Type'); // return content type
+$request->getHeaders()->addHeader(new Cookie(['foo' => 'bar']));
+foreach ($request->getHeaders() as $header) {
+ printf("%s with value %s\n", $header->getFieldName(), $header->getFieldValue());
+}
+```
+
+### Retrieving and setting GET and POST values
+
+```php
+use Zend\Http\Request;
+
+$request = new Request();
+
+// getPost() and getQuery() both return, by default, a Parameters object, which
+// extends ArrayObject
+$request->getPost()->foo = 'Foo value';
+$request->getQuery()->bar = 'Bar value';
+$request->getPost('foo'); // returns 'Foo value'
+$request->getQuery()->offsetGet('bar'); // returns 'Bar value'
+```
+
+### Generating a formatted HTTP Request from a Request object
+
+```php
+use Zend\Http\Request;
+
+$request = new Request();
+$request->setMethod(Request::METHOD_POST);
+$request->setUri('/foo');
+$request->getHeaders()->addHeaders([
+ 'HeaderField1' => 'header-field-value1',
+ 'HeaderField2' => 'header-field-value2',
+]);
+$request->getPost()->set('foo', 'bar');
+$request->setContent($request->getPost()->toString());
+echo $request->toString();
+
+/** Will produce:
+POST /foo HTTP/1.1
+HeaderField1: header-field-value1
+HeaderField2: header-field-value2
+
+foo=bar
+*/
+```
diff --git a/doc/book/response.md b/doc/book/response.md
new file mode 100644
index 0000000000..acce073da2
--- /dev/null
+++ b/doc/book/response.md
@@ -0,0 +1,140 @@
+# The Response Class
+
+`Zend\Http\Response` is responsible for providing a fluent API that allows a
+developer to interact with all the various parts of an HTTP response.
+
+A typical HTTP Response looks like this:
+
+```text
+| VERSION | CODE | REASON |
+| HEADERS |
+| BODY |
+```
+
+The first line of the response consists of the HTTP version, status code, and
+the reason string for the provided status code; this is called the Response
+Line. Next is a set of zero or more headers. The remainder of the response is
+the response body, which is typically a string of HTML that will render on the
+client's browser, but which can also be a place for request/response payload
+data typical of an AJAX request. More information on the structure and
+specification of an HTTP response can be found in
+[RFC-2616 on the W3.org site](http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html).
+
+## Quick Start
+
+Response objects can either be created from the provided `fromString()` factory,
+or, if you wish to have a completely empty object to start with, by
+instantiating the `Zend\Http\Response` class with no arguments.
+
+```php
+use Zend\Http\Response;
+$response = Response::fromString(<<
+
+ Hello World
+
+