5
5
use GuzzleHttp \Client ;
6
6
use GuzzleHttp \Cookie \CookieJar ;
7
7
use GuzzleHttp \Exception \ConnectException ;
8
+ use GuzzleHttp \Exception \RequestException ;
9
+ use GuzzleHttp \Exception \TransferException ;
8
10
use GuzzleHttp \HandlerStack ;
9
11
use Illuminate \Support \Collection ;
10
12
use Illuminate \Support \Str ;
11
13
use Illuminate \Support \Traits \Macroable ;
14
+ use Psr \Http \Message \MessageInterface ;
12
15
use Symfony \Component \VarDumper \VarDumper ;
13
16
14
17
class PendingRequest
@@ -22,6 +25,13 @@ class PendingRequest
22
25
*/
23
26
protected $ factory ;
24
27
28
+ /**
29
+ * The Guzzle client instance.
30
+ *
31
+ * @var \GuzzleHttp\Client
32
+ */
33
+ protected $ client ;
34
+
25
35
/**
26
36
* The base URL for the request.
27
37
*
@@ -106,6 +116,20 @@ class PendingRequest
106
116
*/
107
117
protected $ middleware ;
108
118
119
+ /**
120
+ * Whether the requests should be asynchronous.
121
+ *
122
+ * @var bool
123
+ */
124
+ protected $ async = false ;
125
+
126
+ /**
127
+ * The pending request promise.
128
+ *
129
+ * @var \GuzzleHttp\Promise\PromiseInterface
130
+ */
131
+ protected $ promise ;
132
+
109
133
/**
110
134
* Create a new HTTP Client instance.
111
135
*
@@ -571,6 +595,27 @@ public function delete($url, $data = [])
571
595
]);
572
596
}
573
597
598
+ /**
599
+ * Send a pool of asynchronous requests concurrently.
600
+ *
601
+ * @param callable $callback
602
+ * @return array
603
+ */
604
+ public function pool (callable $ callback )
605
+ {
606
+ $ results = [];
607
+
608
+ $ requests = tap (new Pool ($ this ->factory ), $ callback )->getRequests ();
609
+
610
+ foreach ($ requests as $ key => $ item ) {
611
+ $ results [$ key ] = $ item instanceof static ? $ item ->getPromise ()->wait () : $ item ->wait ();
612
+ }
613
+
614
+ ksort ($ results );
615
+
616
+ return $ results ;
617
+ }
618
+
574
619
/**
575
620
* Send the request to the given URL.
576
621
*
@@ -601,18 +646,14 @@ public function send(string $method, string $url, array $options = [])
601
646
602
647
[$ this ->pendingBody , $ this ->pendingFiles ] = [null , []];
603
648
649
+ if ($ this ->async ) {
650
+ return $ this ->makePromise ($ method , $ url , $ options );
651
+ }
652
+
604
653
return retry ($ this ->tries ?? 1 , function () use ($ method , $ url , $ options ) {
605
654
try {
606
- $ laravelData = $ this ->parseRequestData ($ method , $ url , $ options );
607
-
608
- return tap (new Response ($ this ->buildClient ()->request ($ method , $ url , $ this ->mergeOptions ([
609
- 'laravel_data ' => $ laravelData ,
610
- 'on_stats ' => function ($ transferStats ) {
611
- $ this ->transferStats = $ transferStats ;
612
- },
613
- ], $ options ))), function ($ response ) {
614
- $ response ->cookies = $ this ->cookies ;
615
- $ response ->transferStats = $ this ->transferStats ;
655
+ return tap (new Response ($ this ->sendRequest ($ method , $ url , $ options )), function ($ response ) {
656
+ $ this ->populateResponse ($ response );
616
657
617
658
if ($ this ->tries > 1 && ! $ response ->successful ()) {
618
659
$ response ->throw ();
@@ -637,6 +678,49 @@ protected function parseMultipartBodyFormat(array $data)
637
678
})->values ()->all ();
638
679
}
639
680
681
+ /**
682
+ * Send an asynchronous request to the given URL.
683
+ *
684
+ * @param string $method
685
+ * @param string $url
686
+ * @param array $options
687
+ * @return \GuzzleHttp\Promise\PromiseInterface
688
+ */
689
+ protected function makePromise (string $ method , string $ url , array $ options = [])
690
+ {
691
+ return $ this ->promise = $ this ->sendRequest ($ method , $ url , $ options )
692
+ ->then (function (MessageInterface $ message ) {
693
+ return $ this ->populateResponse (new Response ($ message ));
694
+ })
695
+ ->otherwise (function (TransferException $ e ) {
696
+ return $ e instanceof RequestException ? $ this ->populateResponse (new Response ($ e ->getResponse ())) : $ e ;
697
+ });
698
+ }
699
+
700
+ /**
701
+ * Send a request either synchronously or asynchronously.
702
+ *
703
+ * @param string $method
704
+ * @param string $url
705
+ * @param array $options
706
+ * @return \Psr\Http\Message\MessageInterface|\GuzzleHttp\Promise\PromiseInterface
707
+ *
708
+ * @throws \Exception
709
+ */
710
+ protected function sendRequest (string $ method , string $ url , array $ options = [])
711
+ {
712
+ $ clientMethod = $ this ->async ? 'requestAsync ' : 'request ' ;
713
+
714
+ $ laravelData = $ this ->parseRequestData ($ method , $ url , $ options );
715
+
716
+ return $ this ->buildClient ()->$ clientMethod ($ method , $ url , $ this ->mergeOptions ([
717
+ 'laravel_data ' => $ laravelData ,
718
+ 'on_stats ' => function ($ transferStats ) {
719
+ $ this ->transferStats = $ transferStats ;
720
+ },
721
+ ], $ options ));
722
+ }
723
+
640
724
/**
641
725
* Get the request data as an array so that we can attach it to the request for convenient assertions.
642
726
*
@@ -664,14 +748,29 @@ protected function parseRequestData($method, $url, array $options)
664
748
return $ laravelData ;
665
749
}
666
750
751
+ /**
752
+ * Populate the given response with additional data.
753
+ *
754
+ * @param \Illuminate\Http\Client\Response $response
755
+ * @return \Illuminate\Http\Client\Response
756
+ */
757
+ protected function populateResponse (Response $ response )
758
+ {
759
+ $ response ->cookies = $ this ->cookies ;
760
+
761
+ $ response ->transferStats = $ this ->transferStats ;
762
+
763
+ return $ response ;
764
+ }
765
+
667
766
/**
668
767
* Build the Guzzle client.
669
768
*
670
769
* @return \GuzzleHttp\Client
671
770
*/
672
771
public function buildClient ()
673
772
{
674
- return new Client ([
773
+ return $ this -> client = $ this -> client ?: new Client ([
675
774
'handler ' => $ this ->buildHandlerStack (),
676
775
'cookies ' => true ,
677
776
]);
@@ -826,4 +925,40 @@ public function stub($callback)
826
925
827
926
return $ this ;
828
927
}
928
+
929
+ /**
930
+ * Toggle asynchronicity in requests.
931
+ *
932
+ * @param bool $async
933
+ * @return $this
934
+ */
935
+ public function async (bool $ async = true )
936
+ {
937
+ $ this ->async = $ async ;
938
+
939
+ return $ this ;
940
+ }
941
+
942
+ /**
943
+ * Retrieve the pending request promise.
944
+ *
945
+ * @return \GuzzleHttp\Promise\PromiseInterface|null
946
+ */
947
+ public function getPromise ()
948
+ {
949
+ return $ this ->promise ;
950
+ }
951
+
952
+ /**
953
+ * Set the client instance.
954
+ *
955
+ * @param \GuzzleHttp\Client $client
956
+ * @return $this
957
+ */
958
+ public function setClient (Client $ client )
959
+ {
960
+ $ this ->client = $ client ;
961
+
962
+ return $ this ;
963
+ }
829
964
}
0 commit comments