D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
local
/
psa
/
admin
/
plib
/
vendor
/
react
/
http
/
src
/
Io
/
Filename :
Sender.php
back
Copy
<?php namespace React\Http\Io; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use React\EventLoop\LoopInterface; use React\Http\Client\Client as HttpClient; use React\Promise\PromiseInterface; use React\Promise\Deferred; use React\Socket\ConnectorInterface; use React\Stream\ReadableStreamInterface; /** * [Internal] Sends requests and receives responses * * The `Sender` is responsible for passing the [`RequestInterface`](#requestinterface) objects to * the underlying [`HttpClient`](https://github.com/reactphp/http-client) library * and keeps track of its transmission and converts its reponses back to [`ResponseInterface`](#responseinterface) objects. * * It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage) * and the default [`Connector`](https://github.com/reactphp/socket-client) and [DNS `Resolver`](https://github.com/reactphp/dns). * * The `Sender` class mostly exists in order to abstract changes on the underlying * components away from this package in order to provide backwards and forwards * compatibility. * * @internal You SHOULD NOT rely on this API, it is subject to change without prior notice! * @see Browser */ class Sender { /** * create a new default sender attached to the given event loop * * This method is used internally to create the "default sender". * * You may also use this method if you need custom DNS or connector * settings. You can use this method manually like this: * * ```php * $connector = new \React\Socket\Connector(array(), $loop); * $sender = \React\Http\Io\Sender::createFromLoop($loop, $connector); * ``` * * @param LoopInterface $loop * @param ConnectorInterface|null $connector * @return self */ public static function createFromLoop(LoopInterface $loop, ConnectorInterface $connector) { return new self(new HttpClient(new ClientConnectionManager($connector, $loop))); } private $http; /** * [internal] Instantiate Sender * * @param HttpClient $http * @internal */ public function __construct(HttpClient $http) { $this->http = $http; } /** * * @internal * @param RequestInterface $request * @return PromiseInterface Promise<ResponseInterface, Exception> */ 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 && $size !== 0 && $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(); } }