@@ -186,7 +186,7 @@ processed automatically when making the requests::
186186 'body' => ['parameter1' => 'value1', '...'],
187187
188188 // using a closure to generate the uploaded data
189- 'body' => function () {
189+ 'body' => function (int $size): string {
190190 // ...
191191 },
192192
@@ -199,12 +199,39 @@ When uploading data with the ``POST`` method, if you don't define the
199199form data and adds the required
200200``'Content-Type: application/x-www-form-urlencoded' `` header for you.
201201
202- When uploading JSON payloads, use the ``json `` option instead of `` body ``. The
203- given content will be JSON-encoded automatically and the request will add the
204- `` Content-Type: application/json `` automatically too::
202+ When the ``body `` option is set as a closure, it will be called several times until
203+ it returns the empty string, which signals the end of the body. Each time, the
204+ closure should return a string smaller than the amount requested as argument.
205205
206- $response = $httpClient->request('POST', 'https://...', [
207- 'json' => ['param1' => 'value1', '...'],
206+ A generator or any ``Traversable `` can also be used instead of a closure.
207+
208+ .. tip ::
209+
210+ When uploading JSON payloads, use the ``json `` option instead of ``body ``. The
211+ given content will be JSON-encoded automatically and the request will add the
212+ ``Content-Type: application/json `` automatically too::
213+
214+ $response = $httpClient->request('POST', 'https://...', [
215+ 'json' => ['param1' => 'value1', '...'],
216+ ]);
217+
218+ $decodedPayload = $response->toArray();
219+
220+ To submit a form with file uploads, it is your responsibility to encode the body
221+ according to the ``multipart/form-data `` content-type. The
222+ :doc: `Symfony Mime </components/mime >` component makes it a few lines of code::
223+
224+ use Symfony\Component\Mime\Part\DataPart;
225+ use Symfony\Component\Mime\Part\Multipart\FormDataPart;
226+
227+ $formFields = [
228+ 'regular_field' => 'some value',
229+ 'file_field' => DataPart::fromPath('/path/to/uploaded/file'),
230+ ];
231+ $formData = new FormDataPart($formFields);
232+ $client->request('POST', 'https://...', [
233+ 'headers' => $formData->getPreparedHeaders()->toArray(),
234+ 'body' => $formData->bodyToIterable(),
208235 ]);
209236
210237Cookies
@@ -232,12 +259,31 @@ making a request. Use the ``max_redirects`` setting to configure this behavior
232259 'max_redirects' => 0,
233260 ]);
234261
262+ Progress Callback
263+ ~~~~~~~~~~~~~~~~~
264+
265+ By providing a callable to the ``on_progress `` option, one can track
266+ uploads/downloads as they complete. This callback is guaranteed to be called on
267+ DNS resolution, on arrival of headers and on completion; additionnaly it is
268+ called on upload/download of data and at least once per seconds::
269+
270+ $response = $httpClient->request('GET', 'https://...', [
271+ 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
272+ // $dlNow is the number of bytes downloaded so far
273+ // $dlSize is the total size to be downloaded or -1 if it is unknown
274+ // $info is what $response->getInfo() would return at this very time
275+ },
276+ ];
277+
278+ Any exceptions thrown from the callback will be wrapped in an instance of
279+ ``TransportExceptionInterface `` and will abort the request.
280+
235281Advanced Options
236282~~~~~~~~~~~~~~~~
237283
238284The :class: `Symfony\\ Contracts\\ HttpClient\\ HttpClientInterface ` defines all the
239285options you might need to take full control of the way the request is performed,
240- including progress monitoring, DNS pre-resolution, timeout, SSL parameters, etc.
286+ including DNS pre-resolution, SSL parameters, public key pinning , etc.
241287
242288Processing Responses
243289--------------------
@@ -257,6 +303,9 @@ following methods::
257303 // gets the response body as a string
258304 $content = $response->getContent();
259305
306+ // cancels the request/response
307+ $response->cancel();
308+
260309 // returns info coming from the transport layer, such as "response_headers",
261310 // "redirect_count", "start_time", "redirect_url", etc.
262311 $httpInfo = $response->getInfo();
@@ -285,10 +334,6 @@ response sequentially instead of waiting for the entire response::
285334 $response = $httpClient->request('GET', $url, [
286335 // optional: if you don't want to buffer the response in memory
287336 'buffer' => false,
288- // optional: to display details about the response progress
289- 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
290- // ...
291- },
292337 ]);
293338
294339 // Responses are lazy: this code is executed as soon as headers are received
@@ -303,6 +348,29 @@ response sequentially instead of waiting for the entire response::
303348 fwrite($fileHandler, $chunk->getContent());
304349 }
305350
351+ Canceling Responses
352+ ~~~~~~~~~~~~~~~~~~~
353+
354+ To abort a request (e.g. because it didn't complete in due time, or you want to
355+ fetch only the first bytes of the response, etc.), you can either:
356+
357+ * use the ``cancel() `` method of ``ResponseInterface ``::
358+
359+ $response->cancel()
360+
361+ * throw an exception from a progress callback::
362+
363+ $response = $client->request('GET', 'https://..;', [
364+ 'on_progress' => function (int $dlNow, int $dlSize, array $info): void {
365+ // ...
366+
367+ throw new \MyException();
368+ },
369+ ]);
370+
371+ The exception will be wrapped in an instance of ``TransportExceptionInterface``
372+ and will abort the request.
373+
306374Handling Exceptions
307375~~~~~~~~~~~~~~~~~~~
308376
@@ -529,7 +597,7 @@ PSR-18 Compatibility
529597--------------------
530598
531599This component uses and implements abstractions defined by the
532- ``symfony/contracts `` package . It also implements the `PSR-18 `_ (HTTP Client)
600+ ``symfony/http-client- contracts ``. It also implements the `PSR-18 `_ (HTTP Client)
533601specifications via the :class: `Symfony\\ Component\\ HttpClient\\ Psr18Client `
534602class, which is an adapter to turn a Symfony ``HttpClientInterface `` into a
535603PSR-18 ``ClientInterface ``.
0 commit comments