Skip to content

Commit d7c3a94

Browse files
[HttpClient] doc how to cancel a request + upload files
1 parent e0a86d1 commit d7c3a94

File tree

2 files changed

+81
-13
lines changed

2 files changed

+81
-13
lines changed

components/http_client.rst

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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
199199
form 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

210237
Cookies
@@ -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+
235281
Advanced Options
236282
~~~~~~~~~~~~~~~~
237283

238284
The :class:`Symfony\\Contracts\\HttpClient\\HttpClientInterface` defines all the
239285
options 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

242288
Processing 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+
306374
Handling Exceptions
307375
~~~~~~~~~~~~~~~~~~~
308376

@@ -529,7 +597,7 @@ PSR-18 Compatibility
529597
--------------------
530598

531599
This 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)
533601
specifications via the :class:`Symfony\\Component\\HttpClient\\Psr18Client`
534602
class, which is an adapter to turn a Symfony ``HttpClientInterface`` into a
535603
PSR-18 ``ClientInterface``.

reference/configuration/framework.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ This service can be configured using ``framework.http_client.default_options``:
699699
http_client:
700700
max_host_connections: 10
701701
default_options:
702-
headers: [{ 'X-Powered-By': 'ACME App' }]
702+
headers: { 'X-Powered-By': 'ACME App' }
703703
max_redirects: 7
704704
705705
.. _reference-http-client-scoped-clients:

0 commit comments

Comments
 (0)