@@ -128,8 +128,8 @@ The HTTP client supports different authentication mechanisms. They can be
128128defined globally when creating the client (to apply it to all requests) and to
129129each request (which overrides any global authentication)::
130130
131- // Use the same authentication for all requests
132- $client = HttpClient::create( [
131+ // Use the same authentication for all requests to https://example.com/
132+ $client = HttpClient::createForBaseUri('https://example.com/', [
133133 // HTTP Basic authentication (there are multiple ways of configuring it)
134134 'auth_basic' => ['the-username'],
135135 'auth_basic' => ['the-username', 'the-password'],
@@ -154,10 +154,12 @@ each request (which overrides any global authentication)::
154154.. note ::
155155
156156 The NTLM authentication mechanism requires using the cURL transport.
157+ By using ``HttpClient::createForBaseUri() ``, we ensure that the auth credentials
158+ won't be sent to any other hosts than https://example.com/.
157159
158160.. versionadded :: 4.4
159161
160- The ``auth_ntlm `` option was introduced in Symfony 4.4.
162+ The ``auth_ntlm `` option and the `` HttpClient::createForBaseUri() `` method were introduced in Symfony 4.4.
161163
162164Query String Parameters
163165~~~~~~~~~~~~~~~~~~~~~~~
@@ -378,10 +380,7 @@ Call the ``stream()`` method of the HTTP client to get *chunks* of the
378380response sequentially instead of waiting for the entire response::
379381
380382 $url = 'https://releases.ubuntu.com/18.04.1/ubuntu-18.04.1-desktop-amd64.iso';
381- $response = $client->request('GET', $url, [
382- // optional: if you don't want to buffer the response in memory
383- 'buffer' => false,
384- ]);
383+ $response = $client->request('GET', $url);
385384
386385 // Responses are lazy: this code is executed as soon as headers are received
387386 if (200 !== $response->getStatusCode()) {
@@ -395,6 +394,14 @@ response sequentially instead of waiting for the entire response::
395394 fwrite($fileHandler, $chunk->getContent());
396395 }
397396
397+ .. note ::
398+
399+ By default, ``text/* ``, JSON and XML response bodies are buffered in a local
400+ ``php://temp `` stream. You can control this behavior by using the ``buffer ``
401+ option: set it to ``true ``/``false `` to enable/disable buffering, or to a
402+ closure that should return the same based on the response headers it receives
403+ as argument.
404+
398405Canceling Responses
399406~~~~~~~~~~~~~~~~~~~
400407
@@ -541,6 +548,8 @@ response and get remaining contents that might come back in a new timeout, etc.
541548 is idle *. Big responses can last as long as needed to complete, provided they
542549 remain active during the transfer and never pause for longer than specified.
543550
551+ Use the ``max_duration `` option to limit the time a full request/response can last.
552+
544553Dealing with Network Errors
545554~~~~~~~~~~~~~~~~~~~~~~~~~~~
546555
@@ -666,15 +675,15 @@ or if it matches the ``https://api.github.com/`` base URI.
666675Interoperability
667676----------------
668677
669- The component is interoperable with three different abstractions for HTTP
670- clients: `Symfony Contracts `_, `PSR-18 `_ and `HTTPlug `_ v1 and v2. If your
671- application uses libraries that need any of them, the component is compatible
678+ The component is interoperable with four different abstractions for HTTP
679+ clients: `Symfony Contracts `_, `PSR-18 `_, `HTTPlug `_ v1/v2 and native PHP streams.
680+ If your application uses libraries that need any of them, the component is compatible
672681with all of them. They also benefit from :ref: `autowiring aliases <service-autowiring-alias >`
673682when the :ref: `framework bundle <framework-bundle-configuration >` is used.
674683
675684If you are writing or maintaining a library that makes HTTP requests, you can
676685decouple it from any specific HTTP client implementations by coding against
677- either Symfony Contracts (recommended) or PSR-18 (which superseded HTTPlug) .
686+ either Symfony Contracts (recommended), PSR-18 or HTTPlug v2 .
678687
679688Symfony Contracts
680689~~~~~~~~~~~~~~~~~
@@ -701,7 +710,7 @@ interface you need to code against when a client is needed::
701710All request options mentioned above (e.g. timeout management) are also defined
702711in the wordings of the interface, so that any compliant implementations (like
703712this component) is guaranteed to provide them. That's a major difference with
704- the PSR-18 abstraction , which provides none related to the transport itself.
713+ the other abstractions , which provide none related to the transport itself.
705714
706715Another major feature covered by the Symfony Contracts is async/multiplexing,
707716as described in the previous sections.
@@ -726,6 +735,10 @@ To use it, you need the ``psr/http-client`` package and a `PSR-17`_ implementati
726735 # with autowiring aliases provided by Symfony Flex
727736 $ composer require nyholm/psr7
728737
738+ # alternatively, install the php-http/discovery package to auto-discover
739+ # any already installed implementations from common vendors:
740+ # composer require php-http/discovery
741+
729742 Now you can make HTTP requests with the PSR-18 client as follows::
730743
731744 use Symfony\Component\HttpClient\Psr18Client;
@@ -749,27 +762,26 @@ HTTPlug
749762
750763 Support for HTTPlug was introduced in Symfony 4.4.
751764
752- The `HTTPlug `_ specification was published before PSR-18 and is superseded by
753- it. As such, you should not use it in newly written code. Yet, many libraries
754- still require v1 or v2 of it. The component is interoperable with them thanks to
755- the ``HttplugClient `` adapter class. Similarly to ``Psr18Client `` implementing
756- relevant parts of PSR-17, ``HttplugClient `` also implements the factory methods
757- defined in the related ``php-http/message-factory `` package.
758-
759- Internally, the implementation relies on the ``Psr18Client ``, so that the
760- ``psr/http-client `` package is needed to use this class:
765+ The `HTTPlug `_ v1 specification was published before PSR-18 and is superseded by
766+ it. As such, you should not use it in newly written code. The component is still
767+ interoperable with libraries that require it thanks to the
768+ :class: `Symfony\\ Component\\ HttpClient\\ HttplugClient ` class. Similarly to
769+ ``Psr18Client `` implementing relevant parts of PSR-17, ``HttplugClient `` also
770+ implements the factory methods defined in the related ``php-http/message-factory ``
771+ package.
761772
762773.. code-block :: terminal
763774
764775 # Let's suppose php-http/httplug is already required by the lib you want to use
765776
766- # installs the PSR-18 ClientInterface
767- $ composer require psr/http-client
768-
769777 # installs an efficient implementation of response and stream factories
770778 # with autowiring aliases provided by Symfony Flex
771779 $ composer require nyholm/psr7
772780
781+ # alternatively, install the php-http/discovery package to auto-discover
782+ # any already installed implementations from common vendors:
783+ # composer require php-http/discovery
784+
773785 Let's say you want to instantiate a class with the following constructor,
774786that requires HTTPlug dependencies::
775787
@@ -794,6 +806,76 @@ Because ``HttplugClient`` implements the three interfaces, you can use it this w
794806 $httpClient = new HttplugClient();
795807 $apiClient = new SomeSdk($httpClient, $httpClient, $httpClient);
796808
809+ If you'd like to work with promises, ``HttplugClient `` also implements the
810+ ``HttpAsyncClient `` interface. To use it, you need to install the
811+ ``guzzlehttp/promises `` package:
812+
813+ .. code-block :: terminal
814+
815+ $ composer require guzzlehttp/promises
816+
817+ Then you're ready to go::
818+
819+ use Psr\Http\Message\ResponseInterface;
820+ use Symfony\Component\HttpClient\HttplugClient;
821+
822+ $httpClient = new HttplugClient();
823+ $request = $httpClient->createRequest('GET', 'https://my.api.com/');
824+ $promise = $httpClient->sendRequest($request)
825+ ->then(
826+ function (ResponseInterface $response) {
827+ echo 'Got status '.$response->getStatusCode();
828+
829+ return $response;
830+ },
831+ function (\Throwable $exception) {
832+ echo 'Error: '.$exception->getMessage();
833+
834+ throw $exception;
835+ }
836+ );
837+
838+ // after you're done with sending several requests,
839+ // you must wait for them to complete concurrently
840+
841+ // wait for a specific promise to resolve while monitoring them all
842+ $response = $promise->wait();
843+
844+ // wait maximum 1 second for pending promises to resolve
845+ $httpClient->wait(1.0);
846+
847+ // wait for all remaining promises to resolve
848+ $httpClient->wait();
849+
850+ Native PHP streams
851+ ~~~~~~~~~~~~~~~~~~
852+
853+ .. versionadded :: 4.4
854+
855+ Support for native PHP streams was introduced in Symfony 4.4.
856+
857+ Responses implementing :class: `Symfony\\ Contracts\\ HttpClient\\ ResponseInterface `
858+ can be cast to native PHP streams with
859+ :method: `Symfony\\ Component\\ HttpClient\\ Response\\ StreamWrapper::createResource` `.
860+ This allows using them where native PHP streams are needed::
861+
862+ use Symfony\Component\HttpClient\HttpClient;
863+ use Symfony\Component\HttpClient\Response\StreamWrapper;
864+
865+ $client = HttpClient::create();
866+ $response = $client->request('GET', 'https://symfony.com/versions.json');
867+
868+ $streamResource = StreamWrapper::createResource($response, $client);
869+
870+ // alternatively and contrary to the previous one, this returns
871+ // a resource that is seekable and potentially stream_select()-able
872+ $streamResource = $response->toStream();
873+
874+ echo stream_get_contents($streamResource); // outputs the content of the response
875+
876+ // later on if you need to, you can access the response from the stream
877+ $response = stream_get_meta_data($streamResource)['wrapper_data']->getResponse();
878+
797879Symfony Framework Integration
798880-----------------------------
799881
0 commit comments