1
1
<?php
2
2
namespace Http \Curl ;
3
3
4
+ use Http \Client \Exception ;
4
5
use Http \Client \Exception \RequestException ;
6
+ use Http \Client \HttpAsyncClient ;
5
7
use Http \Client \HttpClient ;
8
+ use Http \Client \Promise ;
6
9
use Http \Message \MessageFactory ;
7
- use Http \Message \MessageFactoryAwareTemplate ;
8
10
use Http \Message \StreamFactory ;
9
- use Http \Message \StreamFactoryAwareTemplate ;
10
11
use Psr \Http \Message \RequestInterface ;
11
12
use Psr \Http \Message \ResponseInterface ;
12
13
15
16
*
16
17
* @license http://opensource.org/licenses/MIT MIT
17
18
*
18
- * @author Kemist <kemist1980@gmail.com>
19
19
* @author Михаил Красильников <m.krasilnikov@yandex.ru>
20
20
* @author Blake Williams <github@shabbyrobe.org>
21
21
*
22
- * @since 1.0.0
22
+ * @api
23
+ * @since 1.0
23
24
*/
24
- class CurlHttpClient implements HttpClient
25
+ class CurlHttpClient implements HttpClient, HttpAsyncClient
25
26
{
26
- use MessageFactoryAwareTemplate;
27
- use StreamFactoryAwareTemplate;
27
+ /**
28
+ * Client settings
29
+ *
30
+ * @var array
31
+ */
32
+ private $ settings ;
28
33
29
34
/**
30
- * cURL handle opened resource
35
+ * cURL response parser
36
+ *
37
+ * @var ResponseParser
38
+ */
39
+ private $ responseParser ;
40
+
41
+ /**
42
+ * cURL synchronous requests handle
31
43
*
32
44
* @var resource|null
33
45
*/
34
46
private $ handle = null ;
35
47
36
48
/**
37
- * Client settings
49
+ * Simultaneous requests runner
38
50
*
39
- * @var array
51
+ * @var MultiRunner|null
40
52
*/
41
- private $ settings ;
53
+ private $ multiRunner = null ;
42
54
43
55
/**
44
56
* Create new client
@@ -54,15 +66,14 @@ class CurlHttpClient implements HttpClient
54
66
* @param StreamFactory $streamFactory HTTP Stream factory
55
67
* @param array $options Client options
56
68
*
57
- * @since 1.00
69
+ * @since 1.0
58
70
*/
59
71
public function __construct (
60
72
MessageFactory $ messageFactory ,
61
73
StreamFactory $ streamFactory ,
62
74
array $ options = []
63
75
) {
64
- $ this ->setMessageFactory ($ messageFactory );
65
- $ this ->setStreamFactory ($ streamFactory );
76
+ $ this ->responseParser = new ResponseParser ($ messageFactory , $ streamFactory );
66
77
$ this ->settings = array_merge (
67
78
[
68
79
'curl_options ' => [],
@@ -95,93 +106,56 @@ public function __destruct()
95
106
* @throws \InvalidArgumentException
96
107
* @throws RequestException
97
108
*
98
- * @since 1.00
109
+ * @since 1.0
99
110
*/
100
111
public function sendRequest (RequestInterface $ request )
101
112
{
102
113
$ options = $ this ->createCurlOptions ($ request );
103
114
104
- try {
105
- $ this ->request ( $ options , $ raw , $ info );
106
- } catch ( \ RuntimeException $ e ) {
107
- throw new RequestException ( $ e -> getMessage (), $ request , $ e );
115
+ if ( is_resource ( $ this -> handle )) {
116
+ curl_reset ( $ this ->handle );
117
+ } else {
118
+ $ this -> handle = curl_init ( );
108
119
}
109
120
110
- $ response = $ this ->getMessageFactory ()->createResponse ();
111
-
112
- $ headerSize = $ info ['header_size ' ];
113
- $ rawHeaders = substr ($ raw , 0 , $ headerSize );
114
- $ headers = $ this ->parseRawHeaders ($ rawHeaders );
115
-
116
- foreach ($ headers as $ header ) {
117
- $ header = trim ($ header );
118
- if ('' === $ header ) {
119
- continue ;
120
- }
121
-
122
- // Status line
123
- if (substr (strtolower ($ header ), 0 , 5 ) === 'http/ ' ) {
124
- $ parts = explode (' ' , $ header , 3 );
125
- $ response = $ response
126
- ->withStatus ($ parts [1 ])
127
- ->withProtocolVersion (substr ($ parts [0 ], 5 ));
128
- continue ;
129
- }
121
+ curl_setopt_array ($ this ->handle , $ options );
122
+ $ raw = curl_exec ($ this ->handle );
130
123
131
- // Extract header
132
- $ parts = explode (': ' , $ header , 2 );
133
- $ headerName = trim (urldecode ($ parts [0 ]));
134
- $ headerValue = trim (urldecode ($ parts [1 ]));
135
- if ($ response ->hasHeader ($ headerName )) {
136
- $ response = $ response ->withAddedHeader ($ headerName , $ headerValue );
137
- } else {
138
- $ response = $ response ->withHeader ($ headerName , $ headerValue );
139
- }
124
+ if (curl_errno ($ this ->handle ) > 0 ) {
125
+ throw new RequestException (curl_error ($ this ->handle ), $ request );
140
126
}
141
127
142
- /*
143
- * substr can return boolean value for empty string. But createStream does not support
144
- * booleans. Converting to string.
145
- */
146
- $ content = (string ) substr ($ raw , $ headerSize );
147
- $ stream = $ this ->getStreamFactory ()->createStream ($ content );
148
- $ response = $ response ->withBody ($ stream );
128
+ $ info = curl_getinfo ($ this ->handle );
149
129
150
- return $ response ;
130
+ return $ this -> responseParser -> parse ( $ raw , $ info ) ;
151
131
}
152
132
153
133
/**
154
- * Perform request via cURL
134
+ * Sends a PSR-7 request in an asynchronous way.
155
135
*
156
- * @param array $options cURL options
157
- * @param string $raw raw response
158
- * @param array $info cURL response info
136
+ * @param RequestInterface $request
159
137
*
160
- * @throws \RuntimeException on cURL error
138
+ * @return Promise
161
139
*
162
- * @since 1.00
140
+ * @throws Exception
141
+ *
142
+ * @since 1.0
163
143
*/
164
- protected function request ( $ options , & $ raw , & $ info )
144
+ public function sendAsyncRequest ( RequestInterface $ request )
165
145
{
166
- if (is_resource ($ this ->handle )) {
167
- curl_reset ($ this ->handle );
168
- } else {
169
- $ this ->handle = curl_init ();
146
+ if (!$ this ->multiRunner instanceof MultiRunner) {
147
+ $ this ->multiRunner = new MultiRunner ($ this ->responseParser );
170
148
}
171
149
172
- curl_setopt_array ($ this ->handle , $ options );
173
- $ raw = curl_exec ($ this ->handle );
150
+ $ handle = curl_init ();
151
+ $ options = $ this ->createCurlOptions ($ request );
152
+ curl_setopt_array ($ handle , $ options );
174
153
175
- if (curl_errno ($ this ->handle ) > 0 ) {
176
- throw new \RuntimeException (
177
- sprintf (
178
- 'Curl error: (%d) %s ' ,
179
- curl_errno ($ this ->handle ),
180
- curl_error ($ this ->handle )
181
- )
182
- );
183
- }
184
- $ info = curl_getinfo ($ this ->handle );
154
+ $ core = new PromiseCore ($ request , $ handle );
155
+ $ promise = new CurlPromise ($ core , $ this ->multiRunner );
156
+ $ this ->multiRunner ->add ($ core );
157
+
158
+ return $ promise ;
185
159
}
186
160
187
161
/**
@@ -284,22 +258,4 @@ private function createHeaders(RequestInterface $request, array $options)
284
258
}
285
259
return $ curlHeaders ;
286
260
}
287
-
288
- /**
289
- * Parse raw headers from HTTP response
290
- *
291
- * @param string $rawHeaders
292
- *
293
- * @return string[]
294
- */
295
- private function parseRawHeaders ($ rawHeaders )
296
- {
297
- $ allHeaders = explode ("\r\n\r\n" , $ rawHeaders );
298
- $ lastHeaders = trim (array_pop ($ allHeaders ));
299
- while (count ($ allHeaders ) > 0 && '' === $ lastHeaders ) {
300
- $ lastHeaders = trim (array_pop ($ allHeaders ));
301
- }
302
- $ headers = explode ("\r\n" , $ lastHeaders );
303
- return $ headers ;
304
- }
305
261
}
0 commit comments