Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
handler.php
1<?php
2
11
14use Psr\Http\Message\RequestInterface;
16
17class Handler extends Http\Handler
18{
19 protected \CurlHandle $handle;
20 protected $logFileHandle;
21
27 public function __construct(RequestInterface $request, Http\ResponseBuilder $responseBuilder, array $options = [])
28 {
29 Http\Handler::__construct($request, $responseBuilder, $options);
30
31 $this->handle = curl_init();
32
33 $this->setOptions($options);
34 }
35
36 public function __destruct()
37 {
38 curl_close($this->handle);
39
40 if (is_resource($this->logFileHandle))
41 {
42 fclose($this->logFileHandle);
43 }
44 }
45
46 protected function setOptions(array $options): void
47 {
49 $uri = $request->getUri();
50
51 $curlOptions = [
52 CURLOPT_URL => (string)$uri,
53 CURLOPT_HEADER => false,
54 CURLOPT_RETURNTRANSFER => false,
55 CURLOPT_FOLLOWLOCATION => false,
56 CURLOPT_HTTP_VERSION => ($request->getProtocolVersion() === '1.1' ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0),
57 CURLOPT_CONNECTTIMEOUT => (int)($options['socketTimeout'] ?? 30),
58 CURLOPT_LOW_SPEED_TIME => (int)($options['streamTimeout'] ?? 60),
59 CURLOPT_LOW_SPEED_LIMIT => 1, // bytes/sec
60 CURLOPT_HTTPHEADER => $this->buildHeaders(),
61 ];
62
63 if (isset($options['contextOptions']['ssl']['verify_peer']))
64 {
65 $curlOptions[CURLOPT_SSL_VERIFYPEER] = (bool)$options['contextOptions']['ssl']['verify_peer'];
66 }
67 if (isset($options['contextOptions']['ssl']['verify_peer_name']))
68 {
69 $curlOptions[CURLOPT_SSL_VERIFYHOST] = $options['contextOptions']['ssl']['verify_peer_name'] ? 2 : 0;
70 }
71
72 $method = $request->getMethod();
73 if ($method === 'HEAD')
74 {
75 $curlOptions[CURLOPT_NOBODY] = true;
76 }
77 else
78 {
79 $curlOptions[CURLOPT_CUSTOMREQUEST] = $method;
80 }
81
82 if (isset($options['effectiveIp']) && $options['effectiveIp'] instanceof IpAddress)
83 {
84 //resolved in HttpClient if private IPs were disabled
85 $curlOptions[CURLOPT_RESOLVE] = [$uri->getHost() . ':' . $uri->getPort() . ':' . $options['effectiveIp']];
86 }
87
88 if (isset($options['proxyHost']))
89 {
90 $curlOptions[CURLOPT_PROXY] = (string)$options['proxyHost'];
91
92 if (isset($options['proxyPort']))
93 {
94 $curlOptions[CURLOPT_PROXYPORT] = (int)$options['proxyPort'];
95 }
96 }
97
98 if ($method != 'GET' && $method != 'HEAD' && $method != 'TRACE')
99 {
100 $body = $request->getBody();
101 $size = $body->getSize();
102
103 if ($size !== 0)
104 {
105 if ($body->isSeekable())
106 {
107 $body->rewind();
108 }
109
110 $curlOptions[CURLOPT_UPLOAD] = true;
111
112 if ($size !== null)
113 {
114 $curlOptions[CURLOPT_INFILESIZE] = $size;
115 }
116
117 $curlOptions[CURLOPT_READFUNCTION] = [$this, 'readRequestBody'];
118 }
119 }
120
121 $curlOptions[CURLOPT_HEADERFUNCTION] = [$this, 'receiveHeaders'];
122
123 $curlOptions[CURLOPT_WRITEFUNCTION] = [$this, 'receiveBody'];
124
125 if (!empty($options['curlLogFile']))
126 {
127 $this->logFileHandle = fopen($options['curlLogFile'], 'a+');
128 $curlOptions[CURLOPT_STDERR] = $this->logFileHandle;
129 $curlOptions[CURLOPT_VERBOSE] = true;
130 }
131
132 curl_setopt_array($this->handle, $curlOptions);
133 }
134
138 public function readRequestBody($handle, $resource, $length)
139 {
140 $part = $this->request->getBody()->read($length);
141
142 $this->log($part, HttpDebug::REQUEST_BODY);
143
144 return $part;
145 }
146
150 public function receiveHeaders($handle, $data)
151 {
152 if ($data === "\r\n")
153 {
154 // got all headers
155 $this->log("\n<<<RESPONSE\n" . $this->responseHeaders . "\n", HttpDebug::RESPONSE_HEADERS);
156
157 // build the response for the next stage
158 $this->response = $this->responseBuilder->createFromString($this->responseHeaders);
159
160 $fetchBody = $this->waitResponse;
161
162 if ($this->shouldFetchBody !== null)
163 {
164 $fetchBody = call_user_func($this->shouldFetchBody, $this->response, $this->request);
165 }
166
167 if (!$fetchBody)
168 {
169 // this is not an error really
170 throw new SkipBodyException();
171 }
172 }
173 else
174 {
175 $this->responseHeaders .= $data;
176 }
177 return strlen($data);
178 }
179
183 public function receiveBody($handle, $data)
184 {
185 $body = $this->response->getBody();
186
187 try
188 {
189 $result = $body->write($data);
190 }
191 catch (\RuntimeException)
192 {
193 return false;
194 }
195
196 if ($this->bodyLengthMax > 0 && $body->getSize() > $this->bodyLengthMax)
197 {
198 return false;
199 }
200
201 return $result;
202 }
203
204 protected function buildHeaders(): array
205 {
206 $headers = [];
207
208 foreach ($this->request->getHeaders() as $name => $values)
209 {
210 foreach ($values as $value)
211 {
212 $headers[] = $name . ': ' . $value;
213 }
214 }
215
216 if ($this->getLogger() && $this->debugLevel)
217 {
218 $request = $this->request->getMethod() . ' ' . $this->request->getRequestTarget() . ' HTTP/' . $this->request->getProtocolVersion() . "\r\n"
219 . implode("\r\n", $headers) . "\r\n";
220
221 $this->log(">>>REQUEST\n" . $request, HttpDebug::REQUEST_HEADERS);
222 }
223
224 return $headers;
225 }
226
230 public function getHandle(): \CurlHandle
231 {
232 return $this->handle;
233 }
234}
readRequestBody($handle, $resource, $length)
Definition handler.php:138
__construct(RequestInterface $request, Http\ResponseBuilder $responseBuilder, array $options=[])
Definition handler.php:27
shouldFetchBody(callable $callback)
Definition handler.php:112
RequestInterface $request
Definition handler.php:24
ResponseBuilder $responseBuilder
Definition handler.php:25
log(string $logMessage, int $level)
Definition handler.php:87