Example #1
0
 protected function sendRequest(SocketStream $socket, HttpRequest $request, int &$sent) : \Generator
 {
     $request = $this->normalizeRequest($request);
     $body = $request->getBody();
     $size = (yield $body->getSize());
     $sendfile = false;
     if ($body instanceof FileBody && $socket->isSendfileSupported()) {
         $sendfile = true;
         $chunk = $size ? '' : null;
     } else {
         if ($request->getProtocolVersion() == '1.0' && $size === null) {
             if (!$body->isCached()) {
                 $body = new BufferedBody((yield $body->getReadableStream()));
             }
             (yield $body->discard());
             $size = (yield $body->getSize());
         }
         $bodyStream = (yield $body->getReadableStream());
         $clen = $size === null ? 4089 : 4096;
         $chunk = (yield $bodyStream->readBuffer($clen));
         $len = \strlen($chunk ?? '');
         if ($chunk === null) {
             $size = 0;
         } elseif ($len < $clen) {
             $size = $len;
         }
     }
     $buffer = $this->serializeHeaders($request, $size);
     $expect = false;
     if ($this->expectContinue && $chunk !== null && $request->getProtocolVersion() == '1.1') {
         $expect = true;
         $buffer .= "Expect: 100-continue\r\n";
     }
     (yield $socket->write($buffer . "\r\n"));
     (yield $socket->flush());
     if ($expect) {
         if (!\preg_match("'^HTTP/1\\.1\\s+100(?:\$|\\s)'i", $line = (yield $socket->readLine()))) {
             try {
                 return $line;
             } finally {
                 if (isset($bodyStream)) {
                     $bodyStream->close();
                 }
             }
         }
     }
     if ($sendfile) {
         if ($size) {
             $sent += (yield LoopConfig::currentFilesystem()->sendfile($body->getFile(), $socket->getSocket(), $size));
         }
     } elseif ($size === null) {
         $sent += (yield $socket->write(\dechex($len) . "\r\n" . $chunk . "\r\n"));
         if ($len === $clen) {
             // Align each chunk with length and line breaks to fit into 4 KB payload.
             $sent += (yield new CopyBytes($bodyStream, $socket, true, null, 4089, function (string $chunk) {
                 return \dechex(\strlen($chunk)) . "\r\n" . $chunk . "\r\n";
             }));
         }
         (yield $socket->write("0\r\n\r\n"));
     } elseif ($size > 0) {
         $sent += (yield $socket->write($chunk));
         if ($len === $clen) {
             $sent += (yield new CopyBytes($bodyStream, $socket, true, $size - $len));
         }
     }
     (yield $socket->flush());
 }
Example #2
0
 protected function sendDeferredResponse(SocketStream $socket, HttpRequest $request, HttpResponse $response, bool $nobody, bool $close) : \Generator
 {
     if ($this->logger) {
         $this->logger->info('{ip} "{method} {target} HTTP/{protocol}" {status} {size}', ['ip' => $request->getClientAddress(), 'method' => $request->getMethod(), 'target' => $request->getRequestTarget(), 'protocol' => $request->getProtocolVersion(), 'status' => $response->getStatusCode(), 'size' => '-']);
     }
     if (!$nobody) {
         $close = true;
     }
     (yield $socket->write($this->serializeHeaders($response, $close, null, $nobody, true) . "\r\n"));
     (yield $socket->flush());
     if ($nobody) {
         $response->getBody()->close(false);
         return !$close;
     }
     $watcher = null;
     $task = new Coroutine(function () use(&$watcher, $socket, $request, $response) {
         $body = $response->getBody();
         $body->start($request, $this->logger);
         $bodyStream = (yield $body->getReadableStream());
         $e = null;
         try {
             while (null !== ($chunk = (yield $bodyStream->read()))) {
                 (yield $socket->write($chunk));
             }
         } catch (StreamClosedException $e) {
             // Client disconnected from server.
         } finally {
             try {
                 $bodyStream->close();
             } finally {
                 $body->close($e ? true : false);
             }
             $watcher->cancel(new \RuntimeException());
         }
         return false;
     }, true);
     $watcher = new Coroutine(function () use($socket, $task) {
         $socket = $socket->getSocket();
         while (\is_resource($socket) && !\feof($socket)) {
             (yield new AwaitRead($socket));
         }
         $task->cancel(new StreamClosedException('Client disconnected'));
     });
     return (yield $task);
 }