1=================
2Client Middleware
3=================
4
5Middleware intercepts requests before they are sent over the wire and can be
6used to add functionality to handlers.
7
8Modifying Requests
9------------------
10
11Let's say you wanted to modify requests before they are sent over the wire
12so that they always add specific headers. This can be accomplished by creating
13a function that accepts a handler and returns a new function that adds the
14composed behavior.
15
16.. code-block:: php
17
18    use GuzzleHttp\Ring\Client\CurlHandler;
19
20    $handler = new CurlHandler();
21
22    $addHeaderHandler = function (callable $handler, array $headers = []) {
23        return function (array $request) use ($handler, $headers) {
24            // Add our custom headers
25            foreach ($headers as $key => $value) {
26                $request['headers'][$key] = $value;
27            }
28
29            // Send the request using the handler and return the response.
30            return $handler($request);
31        }
32    };
33
34    // Create a new handler that adds headers to each request.
35    $handler = $addHeaderHandler($handler, [
36        'X-AddMe'       => 'hello',
37        'Authorization' => 'Basic xyz'
38    ]);
39
40    $response = $handler([
41        'http_method' => 'GET',
42        'headers'     => ['Host' => ['httpbin.org']]
43    ]);
44
45Modifying Responses
46-------------------
47
48You can change a response as it's returned from a middleware. Remember that
49responses returned from an handler (including middleware) must implement
50``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen,
51you should not expect that the responses returned through your middleware will
52be completed synchronously. Instead, you should use the
53``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the
54underlying promise is resolved. This function is a helper function that makes it
55easy to create a new instance of ``FutureArrayInterface`` that wraps an existing
56``FutureArrayInterface`` object.
57
58Let's say you wanted to add headers to a response as they are returned from
59your middleware, but you want to make sure you aren't causing future
60responses to be dereferenced right away. You can achieve this by modifying the
61incoming request and using the ``Core::proxy`` function.
62
63.. code-block:: php
64
65    use GuzzleHttp\Ring\Core;
66    use GuzzleHttp\Ring\Client\CurlHandler;
67
68    $handler = new CurlHandler();
69
70    $responseHeaderHandler = function (callable $handler, array $headers) {
71        return function (array $request) use ($handler, $headers) {
72            // Send the request using the wrapped handler.
73            return Core::proxy($handler($request), function ($response) use ($headers) {
74                // Add the headers to the response when it is available.
75                foreach ($headers as $key => $value) {
76                    $response['headers'][$key] = (array) $value;
77                }
78                // Note that you can return a regular response array when using
79                // the proxy method.
80                return $response;
81            });
82        }
83    };
84
85    // Create a new handler that adds headers to each response.
86    $handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']);
87
88    $response = $handler([
89        'http_method' => 'GET',
90        'headers'     => ['Host' => ['httpbin.org']]
91    ]);
92
93    assert($response['headers']['X-Header'] == 'hello!');
94
95Built-In Middleware
96-------------------
97
98RingPHP comes with a few basic client middlewares that modify requests
99and responses.
100
101Streaming Middleware
102~~~~~~~~~~~~~~~~~~~~
103
104If you want to send all requests with the ``streaming`` option to a specific
105handler but other requests to a different handler, then use the streaming
106middleware.
107
108.. code-block:: php
109
110    use GuzzleHttp\Ring\Client\CurlHandler;
111    use GuzzleHttp\Ring\Client\StreamHandler;
112    use GuzzleHttp\Ring\Client\Middleware;
113
114    $defaultHandler = new CurlHandler();
115    $streamingHandler = new StreamHandler();
116    $streamingHandler = Middleware::wrapStreaming(
117        $defaultHandler,
118        $streamingHandler
119    );
120
121    // Send the request using the streaming handler.
122    $response = $streamingHandler([
123        'http_method' => 'GET',
124        'headers'     => ['Host' => ['www.google.com']],
125        'stream'      => true
126    ]);
127
128    // Send the request using the default handler.
129    $response = $streamingHandler([
130        'http_method' => 'GET',
131        'headers'     => ['Host' => ['www.google.com']]
132    ]);
133
134Future Middleware
135~~~~~~~~~~~~~~~~~
136
137If you want to send all requests with the ``future`` option to a specific
138handler but other requests to a different handler, then use the future
139middleware.
140
141.. code-block:: php
142
143    use GuzzleHttp\Ring\Client\CurlHandler;
144    use GuzzleHttp\Ring\Client\CurlMultiHandler;
145    use GuzzleHttp\Ring\Client\Middleware;
146
147    $defaultHandler = new CurlHandler();
148    $futureHandler = new CurlMultiHandler();
149    $futureHandler = Middleware::wrapFuture(
150        $defaultHandler,
151        $futureHandler
152    );
153
154    // Send the request using the blocking CurlHandler.
155    $response = $futureHandler([
156        'http_method' => 'GET',
157        'headers'     => ['Host' => ['www.google.com']]
158    ]);
159
160    // Send the request using the non-blocking CurlMultiHandler.
161    $response = $futureHandler([
162        'http_method' => 'GET',
163        'headers'     => ['Host' => ['www.google.com']],
164        'future'      => true
165    ]);
166