8889841chttp = $http; } /** * * @internal * @param RequestInterface $request * @return PromiseInterface Promise */ public function send(RequestInterface $request) { // support HTTP/1.1 and HTTP/1.0 only, ensured by `Browser` already assert(\in_array($request->getProtocolVersion(), array('1.0', '1.1'), true)); $body = $request->getBody(); $size = $body->getSize(); if ($size !== null && $size !== 0) { // automatically assign a "Content-Length" request header if the body size is known and non-empty $request = $request->withHeader('Content-Length', (string)$size); } elseif ($size === 0 && \in_array($request->getMethod(), array('POST', 'PUT', 'PATCH'))) { // only assign a "Content-Length: 0" request header if the body is expected for certain methods $request = $request->withHeader('Content-Length', '0'); } elseif ($body instanceof ReadableStreamInterface && $body->isReadable() && !$request->hasHeader('Content-Length')) { // use "Transfer-Encoding: chunked" when this is a streaming body and body size is unknown $request = $request->withHeader('Transfer-Encoding', 'chunked'); } else { // do not use chunked encoding if size is known or if this is an empty request body $size = 0; } // automatically add `Authorization: Basic …` request header if URL includes `user:pass@host` if ($request->getUri()->getUserInfo() !== '' && !$request->hasHeader('Authorization')) { $request = $request->withHeader('Authorization', 'Basic ' . \base64_encode($request->getUri()->getUserInfo())); } $requestStream = $this->http->request($request); $deferred = new Deferred(function ($_, $reject) use ($requestStream) { // close request stream if request is cancelled $reject(new \RuntimeException('Request cancelled')); $requestStream->close(); }); $requestStream->on('error', function($error) use ($deferred) { $deferred->reject($error); }); $requestStream->on('response', function (ResponseInterface $response) use ($deferred, $request) { $deferred->resolve($response); }); if ($body instanceof ReadableStreamInterface) { if ($body->isReadable()) { // length unknown => apply chunked transfer-encoding if ($size === null) { $body = new ChunkedEncoder($body); } // pipe body into request stream // add dummy write to immediately start request even if body does not emit any data yet $body->pipe($requestStream); $requestStream->write(''); $body->on('close', $close = function () use ($deferred, $requestStream) { $deferred->reject(new \RuntimeException('Request failed because request body closed unexpectedly')); $requestStream->close(); }); $body->on('error', function ($e) use ($deferred, $requestStream, $close, $body) { $body->removeListener('close', $close); $deferred->reject(new \RuntimeException('Request failed because request body reported an error', 0, $e)); $requestStream->close(); }); $body->on('end', function () use ($close, $body) { $body->removeListener('close', $close); }); } else { // stream is not readable => end request without body $requestStream->end(); } } else { // body is fully buffered => write as one chunk $requestStream->end((string)$body); } return $deferred->promise(); } }