@@ -182,7 +182,7 @@ processed automatically when making the requests::
182182 'body' => ['parameter1' => 'value1', '...'],
183183
184184 // using a closure to generate the uploaded data
185- 'body' => function () {
185+ 'body' => function (int $size): string {
186186 // ...
187187 },
188188
@@ -195,12 +195,39 @@ When uploading data with the ``POST`` method, if you don't define the
195195form data and adds the required
196196``'Content-Type: application/x-www-form-urlencoded' `` header for you.
197197
198- When uploading JSON payloads, use the ``json `` option instead of `` body ``. The
199- given content will be JSON-encoded automatically and the request will add the
200- `` Content-Type: application/json `` automatically too::
198+ When the ``body `` option is set as a closure, it will be called several times until
199+ it returns the empty string, which signals the end of the body. Each time, the
200+ closure should return a string smaller than the amount requested as argument.
201201
202- $response = $httpClient->request('POST', 'https://...', [
203- 'json' => ['param1' => 'value1', '...'],
202+ A generator or any ``Traversable `` can also be used instead of a closure.
203+
204+ .. tip ::
205+
206+ When uploading JSON payloads, use the ``json `` option instead of ``body ``. The
207+ given content will be JSON-encoded automatically and the request will add the
208+ ``Content-Type: application/json `` automatically too::
209+
210+ $response = $httpClient->request('POST', 'https://...', [
211+ 'json' => ['param1' => 'value1', '...'],
212+ ]);
213+
214+ $decodedPayload = $response->toArray();
215+
216+ To submit a form with file uploads, it is your responsibility to encode the body
217+ according to the ``multipart/form-data `` content-type. The
218+ :doc: `Symfony Mime </components/mime >` component makes it a few lines of code::
219+
220+ use Symfony\Component\Mime\Part\DataPart;
221+ use Symfony\Component\Mime\Part\Multipart\FormDataPart;
222+
223+ $formFields = [
224+ 'regular_field' => 'some value',
225+ 'file_field' => DataPart::fromPath('/path/to/uploaded/file'),
226+ ];
227+ $formData = new FormDataPart($formFields);
228+ $client->request('POST', 'https://...', [
229+ 'headers' => $formData->getPreparedHeaders()->toArray(),
230+ 'body' => $formData->bodyToIterable(),
204231 ]);
205232
206233Cookies
@@ -228,12 +255,47 @@ making a request. Use the ``max_redirects`` setting to configure this behavior
228255 'max_redirects' => 0,
229256 ]);
230257
258+ HTTP Proxies
259+ ~~~~~~~~~~~~
260+
261+ By default, this component honors the standard environment variables that your
262+ Operating System defines to direct the HTTP traffic through your local proxy.
263+ This means there is usually nothing to configure to have the client work with
264+ proxies, provided these env vars are properly configured.
265+
266+ You can still set or override these settings using the ``proxy `` and ``no_proxy ``
267+ options:
268+
269+ * ``proxy `` should be set to the ``http://... `` URL of the proxy to get through
270+
271+ * ``no_proxy `` disables the proxy for a comma-separated list of hosts that do not
272+ require it to get reached.
273+
274+ Progress Callback
275+ ~~~~~~~~~~~~~~~~~
276+
277+ By providing a callable to the ``on_progress `` option, one can track
278+ uploads/downloads as they complete. This callback is guaranteed to be called on
279+ DNS resolution, on arrival of headers and on completion; additionally it is
280+ called when new data is uploaded or downloaded and at least once per second::
281+
282+ $response = $httpClient->request('GET', 'https://...', [
283+ 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
284+ // $dlNow is the number of bytes downloaded so far
285+ // $dlSize is the total size to be downloaded or -1 if it is unknown
286+ // $info is what $response->getInfo() would return at this very time
287+ },
288+ ];
289+
290+ Any exceptions thrown from the callback will be wrapped in an instance of
291+ ``TransportExceptionInterface `` and will abort the request.
292+
231293Advanced Options
232294~~~~~~~~~~~~~~~~
233295
234296The :class: `Symfony\\ Contracts\\ HttpClient\\ HttpClientInterface ` defines all the
235297options you might need to take full control of the way the request is performed,
236- including progress monitoring, DNS pre-resolution, timeout, SSL parameters, etc.
298+ including DNS pre-resolution, SSL parameters, public key pinning , etc.
237299
238300Processing Responses
239301--------------------
@@ -253,6 +315,9 @@ following methods::
253315 // gets the response body as a string
254316 $content = $response->getContent();
255317
318+ // cancels the request/response
319+ $response->cancel();
320+
256321 // returns info coming from the transport layer, such as "response_headers",
257322 // "redirect_count", "start_time", "redirect_url", etc.
258323 $httpInfo = $response->getInfo();
@@ -281,10 +346,6 @@ response sequentially instead of waiting for the entire response::
281346 $response = $httpClient->request('GET', $url, [
282347 // optional: if you don't want to buffer the response in memory
283348 'buffer' => false,
284- // optional: to display details about the response progress
285- 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
286- // ...
287- },
288349 ]);
289350
290351 // Responses are lazy: this code is executed as soon as headers are received
@@ -299,6 +360,28 @@ response sequentially instead of waiting for the entire response::
299360 fwrite($fileHandler, $chunk->getContent());
300361 }
301362
363+ Canceling Responses
364+ ~~~~~~~~~~~~~~~~~~~
365+
366+ To abort a request (e.g. because it didn't complete in due time, or you want to
367+ fetch only the first bytes of the response, etc.), you can either use the
368+ ``cancel() `` method of ``ResponseInterface ``::
369+
370+ $response->cancel()
371+
372+ Or throw an exception from a progress callback::
373+
374+ $response = $client->request('GET', 'https://...', [
375+ 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
376+ // ...
377+
378+ throw new \MyException();
379+ },
380+ ]);
381+
382+ The exception will be wrapped in an instance of ``TransportExceptionInterface ``
383+ and will abort the request.
384+
302385Handling Exceptions
303386~~~~~~~~~~~~~~~~~~~
304387
@@ -376,19 +459,6 @@ the "foreach" in the snippet with this one, the code becomes fully async::
376459 Use the ``user_data `` option combined with ``$response->getInfo('user_data') ``
377460 to track the identity of the responses in your foreach loops.
378461
379- Canceling Responses
380- ~~~~~~~~~~~~~~~~~~~
381-
382- Responses can be canceled at any moment before they are completed using the
383- ``cancel() `` method::
384-
385- foreach ($client->stream($responses) as $response => $chunk) {
386- // ...
387-
388- // if some condition happens, cancel the response
389- $response->cancel();
390- }
391-
392462Dealing with Network Timeouts
393463~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
394464
@@ -538,7 +608,7 @@ PSR-18 Compatibility
538608--------------------
539609
540610This component uses and implements abstractions defined by the
541- ``symfony/contracts `` package . It also implements the `PSR-18 `_ (HTTP Client)
611+ ``symfony/http-client- contracts ``. It also implements the `PSR-18 `_ (HTTP Client)
542612specifications via the :class: `Symfony\\ Component\\ HttpClient\\ Psr18Client `
543613class, which is an adapter to turn a Symfony ``HttpClientInterface `` into a
544614PSR-18 ``ClientInterface ``.
0 commit comments