Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

StreamHandler.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 18.18 KiB


001  <?php
002  namespace GuzzleHttp\Handler;
003   
004  use GuzzleHttp\Exception\ConnectException;
005  use GuzzleHttp\Exception\RequestException;
006  use GuzzleHttp\Promise\FulfilledPromise;
007  use GuzzleHttp\Promise\PromiseInterface;
008  use GuzzleHttp\Psr7;
009  use GuzzleHttp\TransferStats;
010  use GuzzleHttp\Utils;
011  use Psr\Http\Message\RequestInterface;
012  use Psr\Http\Message\ResponseInterface;
013  use Psr\Http\Message\StreamInterface;
014   
015  /**
016   * HTTP handler that uses PHP's HTTP stream wrapper.
017   */
018  class StreamHandler
019  {
020      private $lastHeaders = [];
021   
022      /**
023       * Sends an HTTP request.
024       *
025       * @param RequestInterface $request Request to send.
026       * @param array            $options Request transfer options.
027       *
028       * @return PromiseInterface
029       */
030      public function __invoke(RequestInterface $request, array $options)
031      {
032          // Sleep if there is a delay specified.
033          if (isset($options['delay'])) {
034              usleep($options['delay'] * 1000);
035          }
036   
037          $startTime = isset($options['on_stats']) ? Utils::currentTime() : null;
038   
039          try {
040              // Does not support the expect header.
041              $request = $request->withoutHeader('Expect');
042   
043              // Append a content-length header if body size is zero to match
044              // cURL's behavior.
045              if (0 === $request->getBody()->getSize()) {
046                  $request = $request->withHeader('Content-Length', '0');
047              }
048   
049              return $this->createResponse(
050                  $request,
051                  $options,
052                  $this->createStream($request, $options),
053                  $startTime
054              );
055          } catch (\InvalidArgumentException $e) {
056              throw $e;
057          } catch (\Exception $e) {
058              // Determine if the error was a networking error.
059              $message = $e->getMessage();
060              // This list can probably get more comprehensive.
061              if (strpos($message, 'getaddrinfo') // DNS lookup failed
062                  || strpos($message, 'Connection refused')
063                  || strpos($message, "couldn't connect to host") // error on HHVM
064                  || strpos($message, "connection attempt failed")
065              ) {
066                  $e = new ConnectException($e->getMessage(), $request, $e);
067              }
068              $e = RequestException::wrapException($request, $e);
069              $this->invokeStats($options, $request, $startTime, null, $e);
070   
071              return \GuzzleHttp\Promise\rejection_for($e);
072          }
073      }
074   
075      private function invokeStats(
076          array $options,
077          RequestInterface $request,
078          $startTime,
079          ResponseInterface $response = null,
080          $error = null
081      ) {
082          if (isset($options['on_stats'])) {
083              $stats = new TransferStats(
084                  $request,
085                  $response,
086                  Utils::currentTime() - $startTime,
087                  $error,
088                  []
089              );
090              call_user_func($options['on_stats'], $stats);
091          }
092      }
093   
094      private function createResponse(
095          RequestInterface $request,
096          array $options,
097          $stream,
098          $startTime
099      ) {
100          $hdrs = $this->lastHeaders;
101          $this->lastHeaders = [];
102          $parts = explode(' ', array_shift($hdrs), 3);
103          $ver = explode('/', $parts[0])[1];
104          $status = $parts[1];
105          $reason = isset($parts[2]) ? $parts[2] : null;
106          $headers = \GuzzleHttp\headers_from_lines($hdrs);
107          list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
108          $stream = Psr7\stream_for($stream);
109          $sink = $stream;
110   
111          if (strcasecmp('HEAD', $request->getMethod())) {
112              $sink = $this->createSink($stream, $options);
113          }
114   
115          $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
116   
117          if (isset($options['on_headers'])) {
118              try {
119                  $options['on_headers']($response);
120              } catch (\Exception $e) {
121                  $msg = 'An error was encountered during the on_headers event';
122                  $ex = new RequestException($msg, $request, $response, $e);
123                  return \GuzzleHttp\Promise\rejection_for($ex);
124              }
125          }
126   
127          // Do not drain when the request is a HEAD request because they have
128          // no body.
129          if ($sink !== $stream) {
130              $this->drain(
131                  $stream,
132                  $sink,
133                  $response->getHeaderLine('Content-Length')
134              );
135          }
136   
137          $this->invokeStats($options, $request, $startTime, $response, null);
138   
139          return new FulfilledPromise($response);
140      }
141   
142      private function createSink(StreamInterface $stream, array $options)
143      {
144          if (!empty($options['stream'])) {
145              return $stream;
146          }
147   
148          $sink = isset($options['sink'])
149              ? $options['sink']
150              : fopen('php://temp', 'r+');
151   
152          return is_string($sink)
153              ? new Psr7\LazyOpenStream($sink, 'w+')
154              : Psr7\stream_for($sink);
155      }
156   
157      private function checkDecode(array $options, array $headers, $stream)
158      {
159          // Automatically decode responses when instructed.
160          if (!empty($options['decode_content'])) {
161              $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
162              if (isset($normalizedKeys['content-encoding'])) {
163                  $encoding = $headers[$normalizedKeys['content-encoding']];
164                  if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
165                      $stream = new Psr7\InflateStream(
166                          Psr7\stream_for($stream)
167                      );
168                      $headers['x-encoded-content-encoding']
169                          = $headers[$normalizedKeys['content-encoding']];
170                      // Remove content-encoding header
171                      unset($headers[$normalizedKeys['content-encoding']]);
172                      // Fix content-length header
173                      if (isset($normalizedKeys['content-length'])) {
174                          $headers['x-encoded-content-length']
175                              = $headers[$normalizedKeys['content-length']];
176   
177                          $length = (int) $stream->getSize();
178                          if ($length === 0) {
179                              unset($headers[$normalizedKeys['content-length']]);
180                          } else {
181                              $headers[$normalizedKeys['content-length']] = [$length];
182                          }
183                      }
184                  }
185              }
186          }
187   
188          return [$stream, $headers];
189      }
190   
191      /**
192       * Drains the source stream into the "sink" client option.
193       *
194       * @param StreamInterface $source
195       * @param StreamInterface $sink
196       * @param string          $contentLength Header specifying the amount of
197       *                                       data to read.
198       *
199       * @return StreamInterface
200       * @throws \RuntimeException when the sink option is invalid.
201       */
202      private function drain(
203          StreamInterface $source,
204          StreamInterface $sink,
205          $contentLength
206      ) {
207          // If a content-length header is provided, then stop reading once
208          // that number of bytes has been read. This can prevent infinitely
209          // reading from a stream when dealing with servers that do not honor
210          // Connection: Close headers.
211          Psr7\copy_to_stream(
212              $source,
213              $sink,
214              (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
215          );
216   
217          $sink->seek(0);
218          $source->close();
219   
220          return $sink;
221      }
222   
223      /**
224       * Create a resource and check to ensure it was created successfully
225       *
226       * @param callable $callback Callable that returns stream resource
227       *
228       * @return resource
229       * @throws \RuntimeException on error
230       */
231      private function createResource(callable $callback)
232      {
233          $errors = null;
234          set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
235              $errors[] = [
236                  'message' => $msg,
237                  'file'    => $file,
238                  'line'    => $line
239              ];
240              return true;
241          });
242   
243          $resource = $callback();
244          restore_error_handler();
245   
246          if (!$resource) {
247              $message = 'Error creating resource: ';
248              foreach ($errors as $err) {
249                  foreach ($err as $key => $value) {
250                      $message .= "[$key$value" . PHP_EOL;
251                  }
252              }
253              throw new \RuntimeException(trim($message));
254          }
255   
256          return $resource;
257      }
258   
259      private function createStream(RequestInterface $request, array $options)
260      {
261          static $methods;
262          if (!$methods) {
263              $methods = array_flip(get_class_methods(__CLASS__));
264          }
265   
266          // HTTP/1.1 streams using the PHP stream wrapper require a
267          // Connection: close header
268          if ($request->getProtocolVersion() == '1.1'
269              && !$request->hasHeader('Connection')
270          ) {
271              $request = $request->withHeader('Connection', 'close');
272          }
273   
274          // Ensure SSL is verified by default
275          if (!isset($options['verify'])) {
276              $options['verify'] = true;
277          }
278   
279          $params = [];
280          $context = $this->getDefaultContext($request);
281   
282          if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
283              throw new \InvalidArgumentException('on_headers must be callable');
284          }
285   
286          if (!empty($options)) {
287              foreach ($options as $key => $value) {
288                  $method = "add_{$key}";
289                  if (isset($methods[$method])) {
290                      $this->{$method}($request, $context, $value, $params);
291                  }
292              }
293          }
294   
295          if (isset($options['stream_context'])) {
296              if (!is_array($options['stream_context'])) {
297                  throw new \InvalidArgumentException('stream_context must be an array');
298              }
299              $context = array_replace_recursive(
300                  $context,
301                  $options['stream_context']
302              );
303          }
304   
305          // Microsoft NTLM authentication only supported with curl handler
306          if (isset($options['auth'])
307              && is_array($options['auth'])
308              && isset($options['auth'][2])
309              && 'ntlm' == $options['auth'][2]
310          ) {
311              throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
312          }
313   
314          $uri = $this->resolveHost($request, $options);
315   
316          $context = $this->createResource(
317              function () use ($context, $params) {
318                  return stream_context_create($context, $params);
319              }
320          );
321   
322          return $this->createResource(
323              function () use ($uri, &$http_response_header, $context, $options) {
324                  $resource = fopen((string) $uri, 'r', null, $context);
325                  $this->lastHeaders = $http_response_header;
326   
327                  if (isset($options['read_timeout'])) {
328                      $readTimeout = $options['read_timeout'];
329                      $sec = (int) $readTimeout;
330                      $usec = ($readTimeout - $sec) * 100000;
331                      stream_set_timeout($resource, $sec, $usec);
332                  }
333   
334                  return $resource;
335              }
336          );
337      }
338   
339      private function resolveHost(RequestInterface $request, array $options)
340      {
341          $uri = $request->getUri();
342   
343          if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
344              if ('v4' === $options['force_ip_resolve']) {
345                  $records = dns_get_record($uri->getHost(), DNS_A);
346                  if (!isset($records[0]['ip'])) {
347                      throw new ConnectException(
348                          sprintf(
349                              "Could not resolve IPv4 address for host '%s'",
350                              $uri->getHost()
351                          ),
352                          $request
353                      );
354                  }
355                  $uri = $uri->withHost($records[0]['ip']);
356              } elseif ('v6' === $options['force_ip_resolve']) {
357                  $records = dns_get_record($uri->getHost(), DNS_AAAA);
358                  if (!isset($records[0]['ipv6'])) {
359                      throw new ConnectException(
360                          sprintf(
361                              "Could not resolve IPv6 address for host '%s'",
362                              $uri->getHost()
363                          ),
364                          $request
365                      );
366                  }
367                  $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
368              }
369          }
370   
371          return $uri;
372      }
373   
374      private function getDefaultContext(RequestInterface $request)
375      {
376          $headers = '';
377          foreach ($request->getHeaders() as $name => $value) {
378              foreach ($value as $val) {
379                  $headers .= "$name$val\r\n";
380              }
381          }
382   
383          $context = [
384              'http' => [
385                  'method'           => $request->getMethod(),
386                  'header'           => $headers,
387                  'protocol_version' => $request->getProtocolVersion(),
388                  'ignore_errors'    => true,
389                  'follow_location'  => 0,
390              ],
391          ];
392   
393          $body = (string) $request->getBody();
394   
395          if (!empty($body)) {
396              $context['http']['content'] = $body;
397              // Prevent the HTTP handler from adding a Content-Type header.
398              if (!$request->hasHeader('Content-Type')) {
399                  $context['http']['header'] .= "Content-Type:\r\n";
400              }
401          }
402   
403          $context['http']['header'] = rtrim($context['http']['header']);
404   
405          return $context;
406      }
407   
408      private function add_proxy(RequestInterface $request, &$options, $value, &$params)
409      {
410          if (!is_array($value)) {
411              $options['http']['proxy'] = $value;
412          } else {
413              $scheme = $request->getUri()->getScheme();
414              if (isset($value[$scheme])) {
415                  if (!isset($value['no'])
416                      || !\GuzzleHttp\is_host_in_noproxy(
417                          $request->getUri()->getHost(),
418                          $value['no']
419                      )
420                  ) {
421                      $options['http']['proxy'] = $value[$scheme];
422                  }
423              }
424          }
425      }
426   
427      private function add_timeout(RequestInterface $request, &$options, $value, &$params)
428      {
429          if ($value > 0) {
430              $options['http']['timeout'] = $value;
431          }
432      }
433   
434      private function add_verify(RequestInterface $request, &$options, $value, &$params)
435      {
436          if ($value === true) {
437              // PHP 5.6 or greater will find the system cert by default. When
438              // < 5.6, use the Guzzle bundled cacert.
439              if (PHP_VERSION_ID < 50600) {
440                  $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
441              }
442          } elseif (is_string($value)) {
443              $options['ssl']['cafile'] = $value;
444              if (!file_exists($value)) {
445                  throw new \RuntimeException("SSL CA bundle not found: $value");
446              }
447          } elseif ($value === false) {
448              $options['ssl']['verify_peer'] = false;
449              $options['ssl']['verify_peer_name'] = false;
450              return;
451          } else {
452              throw new \InvalidArgumentException('Invalid verify request option');
453          }
454   
455          $options['ssl']['verify_peer'] = true;
456          $options['ssl']['verify_peer_name'] = true;
457          $options['ssl']['allow_self_signed'] = false;
458      }
459   
460      private function add_cert(RequestInterface $request, &$options, $value, &$params)
461      {
462          if (is_array($value)) {
463              $options['ssl']['passphrase'] = $value[1];
464              $value = $value[0];
465          }
466   
467          if (!file_exists($value)) {
468              throw new \RuntimeException("SSL certificate not found: {$value}");
469          }
470   
471          $options['ssl']['local_cert'] = $value;
472      }
473   
474      private function add_progress(RequestInterface $request, &$options, $value, &$params)
475      {
476          $this->addNotification(
477              $params,
478              function ($code, $a, $b, $c, $transferred, $total) use ($value) {
479                  if ($code == STREAM_NOTIFY_PROGRESS) {
480                      $value($total, $transferred, null, null);
481                  }
482              }
483          );
484      }
485   
486      private function add_debug(RequestInterface $request, &$options, $value, &$params)
487      {
488          if ($value === false) {
489              return;
490          }
491   
492          static $map = [
493              STREAM_NOTIFY_CONNECT       => 'CONNECT',
494              STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
495              STREAM_NOTIFY_AUTH_RESULT   => 'AUTH_RESULT',
496              STREAM_NOTIFY_MIME_TYPE_IS  => 'MIME_TYPE_IS',
497              STREAM_NOTIFY_FILE_SIZE_IS  => 'FILE_SIZE_IS',
498              STREAM_NOTIFY_REDIRECTED    => 'REDIRECTED',
499              STREAM_NOTIFY_PROGRESS      => 'PROGRESS',
500              STREAM_NOTIFY_FAILURE       => 'FAILURE',
501              STREAM_NOTIFY_COMPLETED     => 'COMPLETED',
502              STREAM_NOTIFY_RESOLVE       => 'RESOLVE',
503          ];
504          static $args = ['severity', 'message', 'message_code',
505              'bytes_transferred', 'bytes_max'];
506   
507          $value = \GuzzleHttp\debug_resource($value);
508          $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
509          $this->addNotification(
510              $params,
511              function () use ($ident, $value, $map, $args) {
512                  $passed = func_get_args();
513                  $code = array_shift($passed);
514                  fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
515                  foreach (array_filter($passed) as $i => $v) {
516                      fwrite($value, $args[$i] . ': "' . $v . '" ');
517                  }
518                  fwrite($value, "\n");
519              }
520          );
521      }
522   
523      private function addNotification(array &$params, callable $notify)
524      {
525          // Wrap the existing function if needed.
526          if (!isset($params['notification'])) {
527              $params['notification'] = $notify;
528          } else {
529              $params['notification'] = $this->callArray([
530                  $params['notification'],
531                  $notify
532              ]);
533          }
534      }
535   
536      private function callArray(array $functions)
537      {
538          return function () use ($functions) {
539              $args = func_get_args();
540              foreach ($functions as $fn) {
541                  call_user_func_array($fn, $args);
542              }
543          };
544      }
545  }
546