1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
handler.php
См. документацию.
1<?php
2
9
11
14use Psr\Http\Message\RequestInterface;
17
18class Handler extends Http\Handler
19{
20 protected static ?\CurlHandle $sharedHandle = null;
21
22 protected \CurlHandle $handle;
23 protected $logFileHandle;
24
31 {
32 parent::__construct($request, $responseBuilder, $options);
33
34 if ($this->async)
35 {
36 // multihandle
37 $this->handle = curl_init();
38 }
39 else
40 {
41 // one shared handle for sync requests
42 if (static::$sharedHandle === null)
43 {
44 static::$sharedHandle = curl_init();
45 }
46 $this->handle = static::$sharedHandle;
47 }
48
49 $this->setOptions($options);
50 }
51
52 public function __destruct()
53 {
54 if ($this->async)
55 {
56 curl_close($this->handle);
57 }
58
59 if (is_resource($this->logFileHandle))
60 {
61 fclose($this->logFileHandle);
62 }
63 }
64
65 protected function setOptions(array $options): void
66 {
68 $uri = $request->getUri();
69
70 $curlOptions = [
71 CURLOPT_URL => (string)$uri,
72 CURLOPT_HEADER => false,
73 CURLOPT_RETURNTRANSFER => false,
74 CURLOPT_FOLLOWLOCATION => false,
75 CURLOPT_HTTP_VERSION => ($request->getProtocolVersion() === '1.1' ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0),
76 CURLOPT_CONNECTTIMEOUT => (int)($options['socketTimeout'] ?? 30),
77 CURLOPT_LOW_SPEED_TIME => (int)($options['streamTimeout'] ?? 60),
78 CURLOPT_LOW_SPEED_LIMIT => 1, // bytes/sec
79 CURLOPT_HTTPHEADER => $this->buildHeaders(),
80 ];
81
82 if (isset($options['contextOptions']['ssl']['verify_peer']))
83 {
84 $curlOptions[CURLOPT_SSL_VERIFYPEER] = (bool)$options['contextOptions']['ssl']['verify_peer'];
85 }
86 if (isset($options['contextOptions']['ssl']['verify_peer_name']))
87 {
88 $curlOptions[CURLOPT_SSL_VERIFYHOST] = $options['contextOptions']['ssl']['verify_peer_name'] ? 2 : 0;
89 }
90
91 $method = $request->getMethod();
92 if ($method === 'HEAD')
93 {
94 $curlOptions[CURLOPT_NOBODY] = true;
95 }
96 else
97 {
98 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
99 }
100
101 if (isset($options['effectiveIp']) && $options['effectiveIp'] instanceof IpAddress)
102 {
103 //resolved in HttpClient if private IPs were disabled
104 $curlOptions[CURLOPT_RESOLVE] = [$uri->getHost() . ':' . $uri->getPort() . ':' . $options['effectiveIp']];
105 }
106
107 if (isset($options['proxyHost']))
108 {
109 $curlOptions[CURLOPT_PROXY] = (string)$options['proxyHost'];
110
111 if (isset($options['proxyPort']))
112 {
113 $curlOptions[CURLOPT_PROXYPORT] = (int)$options['proxyPort'];
114 }
115 }
116
117 if ($method != 'GET' && $method != 'HEAD' && $method != 'TRACE')
118 {
119 $body = $request->getBody();
120 $size = $body->getSize();
121
122 if ($size !== 0)
123 {
124 if ($body->isSeekable())
125 {
126 $body->rewind();
127 }
128
129 $curlOptions[CURLOPT_UPLOAD] = true;
130
131 if ($size !== null)
132 {
133 $curlOptions[CURLOPT_INFILESIZE] = $size;
134 }
135
136 $curlOptions[CURLOPT_READFUNCTION] = [$this, 'readRequestBody'];
137 }
138 }
139
140 $curlOptions[CURLOPT_HEADERFUNCTION] = [$this, 'receiveHeaders'];
141
142 $curlOptions[CURLOPT_WRITEFUNCTION] = [$this, 'receiveBody'];
143
144 if (!empty($options['curlLogFile']))
145 {
146 $this->logFileHandle = fopen($options['curlLogFile'], 'a+');
147 $curlOptions[CURLOPT_STDERR] = $this->logFileHandle;
148 $curlOptions[CURLOPT_VERBOSE] = true;
149 }
150
151 if (!$this->async)
152 {
153 // shared handle
154 curl_reset($this->handle);
155 }
156 curl_setopt_array($this->handle, $curlOptions);
157 }
158
162 public function readRequestBody($handle, $resource, $length)
163 {
164 $part = $this->request->getBody()->read($length);
165
166 $this->log($part, HttpDebug::REQUEST_BODY);
167
168 return $part;
169 }
170
174 public function receiveHeaders($handle, $data)
175 {
176 if ($data === "\r\n")
177 {
178 // got all headers
179 $this->log("\n<<<RESPONSE\n{headers}\n", HttpDebug::RESPONSE_HEADERS, ['headers' => $this->responseHeaders]);
180
181 // build the response for the next stage
182 $this->response = $this->responseBuilder->createFromString($this->responseHeaders);
183
184 $fetchBody = $this->waitResponse;
185
186 if ($this->shouldFetchBody !== null)
187 {
188 $fetchBody = call_user_func($this->shouldFetchBody, $this->response, $this->request);
189 }
190
191 if (!$fetchBody)
192 {
193 // this is not an error really
194 throw new SkipBodyException();
195 }
196 }
197 else
198 {
199 $this->responseHeaders .= $data;
200 }
201
202 return strlen($data);
203 }
204
208 public function receiveBody($handle, $data)
209 {
210 $body = $this->response->getBody();
211
212 try
213 {
214 $result = $body->write($data);
215 }
216 catch (\RuntimeException)
217 {
218 return false;
219 }
220
221 if ($this->bodyLengthMax > 0 && $body->getSize() > $this->bodyLengthMax)
222 {
223 return false;
224 }
225
226 return $result;
227 }
228
229 protected function buildHeaders(): array
230 {
231 $headers = [];
232
233 foreach ($this->request->getHeaders() as $name => $values)
234 {
235 foreach ($values as $value)
236 {
237 $headers[] = $name . ': ' . $value;
238 }
239 }
240
241 if ($this->getLogger() && $this->debugLevel)
242 {
243 $logUri = new Uri((string)$this->request->getUri());
244 $logUri->convertToUnicode();
245
246 $this->log("***CONNECT to {uri}\n", HttpDebug::CONNECT, ['uri' => $logUri]);
247
248 $request = $this->request->getMethod() . ' ' . $this->request->getRequestTarget() . ' HTTP/' . $this->request->getProtocolVersion() . "\n"
249 . implode("\n", $headers) . "\n";
250
251 $this->log(">>>REQUEST\n{request}", HttpDebug::REQUEST_HEADERS, ['request' => $request]);
252 }
253
254 return $headers;
255 }
256
260 public function getHandle(): \CurlHandle
261 {
262 return $this->handle;
263 }
264
265 protected function getDiagnostics(): array
266 {
267 $stat = curl_getinfo($this->handle);
268
269 $handshake = 0.0;
270 if ($this->request->getUri()->getScheme() === 'https')
271 {
272 $handshake = round((float)curl_getinfo($this->handle, CURLINFO_APPCONNECT_TIME), 6);
273 }
274
275 return [
276 'connect' => round($stat['connect_time'] ?? 0.0, 6),
277 'handshake' => $handshake,
278 'request' => round($stat['pretransfer_time'] ?? 0.0, 6),
279 'total' => round($stat['total_time'] ?? 0.0, 6),
280 ];
281 }
282
283 public function execute(): Http\Response
284 {
285 $fetchBody = true;
286 try
287 {
288 $status = curl_exec($this->handle);
289 }
290 catch (SkipBodyException)
291 {
292 $fetchBody = false;
293 }
294
295 if ($status !== false)
296 {
297 if ($fetchBody)
298 {
299 if ($this->debugLevel & HttpDebug::RESPONSE_BODY)
300 {
301 $this->log($this->response->getBody(), HttpDebug::RESPONSE_BODY);
302 }
303
304 // need to ajust the response headers (PSR-18)
305 $this->response->adjustHeaders();
306 }
307
308 $this->logDiagnostics();
309
310 return $this->response;
311 }
312 else
313 {
314 $error = curl_error($this->handle);
315
316 $this->getLogger()?->error($error . "\n");
317
318 throw new Http\NetworkException($this->request, $error);
319 }
320 }
321}
Определения response.php:5
__construct(RequestInterface $request, Http\ResponseBuilderInterface $responseBuilder, array $options=[])
Определения handler.php:30
readRequestBody($handle, $resource, $length)
Определения handler.php:162
setOptions(array $options)
Определения handler.php:65
receiveHeaders($handle, $data)
Определения handler.php:174
static CurlHandle $sharedHandle
Определения handler.php:20
receiveBody($handle, $data)
Определения handler.php:208
CurlHandle $handle
Определения handler.php:22
shouldFetchBody(callable $callback)
Определения handler.php:128
log(string $logMessage, int $level, array $context=[])
Определения handler.php:90
RequestInterface $request
Определения handler.php:26
Response $response
Определения handler.php:30
bool $waitResponse
Определения handler.php:22
logDiagnostics()
Определения handler.php:108
ResponseBuilderInterface $responseBuilder
Определения handler.php:27
const CONNECT
Определения httpdebug.php:20
const RESPONSE_BODY
Определения httpdebug.php:18
const REQUEST_BODY
Определения httpdebug.php:15
const RESPONSE_HEADERS
Определения httpdebug.php:17
const REQUEST_HEADERS
Определения httpdebug.php:14
Определения uri.php:17
$options
Определения commerceml2.php:49
$data['IS_AVAILABLE']
Определения .description.php:13
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
if(file_exists($_SERVER['DOCUMENT_ROOT'] . "/urlrewrite.php")) $uri
Определения urlrewrite.php:61
$status
Определения session.php:10
$name
Определения menu_edit.php:35
$method
Определения index.php:27
$error
Определения subscription_card_product.php:20