Giter Club home page Giter Club logo

http-client's Introduction

Deprecation notice

This package has now been migrated over to react/http and only exists for BC reasons.

$ composer require react/http

If you've previously used this package, upgrading may take a moment or two. The new API has been updated to use Promises and PSR-7 message abstractions. This means it's now more powerful and easier to use than ever:

// old
$client = new React\HttpClient\Client($loop);
$request = $client->request('GET', 'https://example.com/');
$request->on('response', function ($response) {
    $response->on('data', function ($chunk) {
        echo $chunk;
    });
});
$request->end();

// new
$browser = new React\Http\Browser($loop);
$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) {
    echo $response->getBody();
});

See react/http for more details.

The below documentation applies to the last release of this package. Further development will take place in the updated react/http, so you're highly recommended to upgrade as soon as possible.

Deprecated HttpClient

Build Status

Event-driven, streaming HTTP client for ReactPHP.

Table of Contents

Basic usage

Client

The Client is responsible for communicating with HTTP servers, managing the connection state and sending your HTTP requests. It also registers everything with the main EventLoop.

$loop = React\EventLoop\Factory::create();
$client = new Client($loop);

If you need custom connector settings (DNS resolution, TLS parameters, timeouts, proxy servers etc.), you can explicitly pass a custom instance of the ConnectorInterface:

$connector = new \React\Socket\Connector($loop, array(
    'dns' => '127.0.0.1',
    'tcp' => array(
        'bindto' => '192.168.10.1:0'
    ),
    'tls' => array(
        'verify_peer' => false,
        'verify_peer_name' => false
    )
));

$client = new Client($loop, $connector);

The request(string $method, string $uri, array $headers = array(), string $version = '1.0'): Request method can be used to prepare new Request objects.

The optional $headers parameter can be used to pass additional request headers. You can use an associative array (key=value) or an array for each header value (key=values). The Request will automatically include an appropriate Host, User-Agent: react/alpha and Connection: close header if applicable. You can pass custom header values or use an empty array to omit any of these.

The Request#write(string $data) method can be used to write data to the request body. Data will be buffered until the underlying connection is established, at which point buffered data will be sent and all further data will be passed to the underlying connection immediately.

The Request#end(?string $data = null) method can be used to finish sending the request. You may optionally pass a last request body data chunk that will be sent just like a write() call. Calling this method finalizes the outgoing request body (which may be empty). Data will be buffered until the underlying connection is established, at which point buffered data will be sent and all further data will be ignored.

The Request#close() method can be used to forefully close sending the request. Unlike the end() method, this method discards any buffers and closes the underlying connection if it is already established or cancels the pending connection attempt otherwise.

Request implements WritableStreamInterface, so a Stream can be piped to it. Interesting events emitted by Request:

  • response: The response headers were received from the server and successfully parsed. The first argument is a Response instance.
  • drain: The outgoing buffer drained and the response is ready to accept more data for the next write() call.
  • error: An error occurred, an Exception is passed as first argument. If the response emits an error event, this will also be emitted here.
  • close: The request is closed. If an error occurred, this event will be preceeded by an error event. For a successful response, this will be emitted only once the response emits the close event.

Response implements ReadableStreamInterface. Interesting events emitted by Response:

  • data: Passes a chunk of the response body as first argument. When a response encounters a chunked encoded response it will parse it transparently for the user and removing the Transfer-Encoding header.
  • error: An error occurred, an Exception is passed as first argument. This will also be forwarded to the request and emit an error event there.
  • end: The response has been fully received.
  • close: The response is closed. If an error occured, this event will be preceeded by an error event. This will also be forwarded to the request and emit a close event there.

Example

<?php

$loop = React\EventLoop\Factory::create();
$client = new React\HttpClient\Client($loop);

$request = $client->request('GET', 'https://github.com/');
$request->on('response', function ($response) {
    $response->on('data', function ($chunk) {
        echo $chunk;
    });
    $response->on('end', function() {
        echo 'DONE';
    });
});
$request->on('error', function (\Exception $e) {
    echo $e;
});
$request->end();
$loop->run();

See also the examples.

Advanced Usage

Unix domain sockets

By default, this library supports transport over plaintext TCP/IP and secure TLS connections for the http:// and https:// URI schemes respectively. This library also supports Unix domain sockets (UDS) when explicitly configured.

In order to use a UDS path, you have to explicitly configure the connector to override the destination URI so that the hostname given in the request URI will no longer be used to establish the connection:

$connector = new FixedUriConnector(
    'unix:///var/run/docker.sock',
    new UnixConnector($loop)
);

$client = new Client($loop, $connector);

$request = $client->request('GET', 'http://localhost/info');

See also example #11.

Install

The recommended way to install this library is through Composer. New to Composer?

This will install the latest supported version:

$ composer require react/http-client:^0.5.10

See also the CHANGELOG for details about version upgrades.

This project aims to run on any platform and thus does not require any PHP extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM. It's highly recommended to use PHP 7+ for this project.

Tests

To run the test suite, you first need to clone this repo and then install all dependencies through Composer:

$ composer install

To run the test suite, go to the project root and run:

$ php vendor/bin/phpunit

The test suite also contains a number of functional integration tests that send test HTTP requests against the online service http://httpbin.org and thus rely on a stable internet connection. If you do not want to run these, they can simply be skipped like this:

$ php vendor/bin/phpunit --exclude-group internet

License

MIT, see LICENSE file.

http-client's People

Contributors

arnaud-lb avatar carusogabriel avatar cboden avatar clue avatar cursedcoder avatar djagya avatar dpovshed avatar e3betht avatar igorw avatar jmalloc avatar jsor avatar mbonneau avatar mdrost avatar mmelvin0 avatar reedy avatar remicollet avatar th3mouk avatar weichenlin avatar wyrihaximus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

http-client's Issues

client pool

if i have thousands of requests needed to send,how can i set a http client pool?

Simplify acessing response body

Accessing the HTTP response body is likely one of the main use cases for this library, however it's actually unnecessarily cumbersome.

This has come up several times already, so we way want to look into providing a simpler API.

Possibly related to #41.

Response->on('end') signature weirdness?

I've got the following simple code which sends a request and displays the response.

Problem: the echoed buffer will always be string(0) unless I declare it as &buffer in the on('end'). string(0) is how the buffer is initialized:

$request = $client->request(
	'POST',
	$url,
	array(
		'Content-Length' => strlen($data),
		'Content-Type' => 'application/json'
	)
);

$request->on('response', function ($response) {
	$buffer = '';

	$response->on('data', function ($data, $response) use (&$buffer) {
		$buffer .= (string) $data;
	});

	$response->on('end', function ($error) use ($response, $buffer /* < by ref matters */) {
		echo(sprintf("HTTP %d\n", $response->getCode()));
		if ($buffer) {
			echo($buffer . PHP_EOL);
		}
	});
});

$request->end($data);

Might this be a problem with PHP-internal optimization?

Response can sometimes emits data twice with incompatible arguments

I'm struggling to reproduce this and I can't anymore, but it's fairly clear in the code this is possible. There are two places where the data event for a Response can be triggered.

In Response::handleData the arguments are string, Response.

In Request::handleData the arguments are Psr\Http\Message\StreamInterface, Response.

The latter seems to be correct, happens all the time, and what I want to incrementally read from the stream, so I just wrap my own handleData logic in $stream instanceof StreamInterface but that feels smelly.

The thing I can't reproduce is when Response::handleData gets called. It was happening to me every single time the responding server threw a fatal PHP error, now I can't make it happen. Also of interesting note it was returning only part of the PHP error text, although the stream itself yielded the entire text.

Also of note this is with v0.4.10 (I'll update to the latest) but the code is identical in your master branch.

Roadmap to stable v1.0.0

Let's face it, this project is stable and has been used in production for years :shipit:

However, we're currently following a v0.X.Y release scheme (http://sentimentalversioning.org/).

We should finally make this explicit and fully adhere to SemVer and release a stable v1.0.0.

To a large extend, a stable v1.0.0 helps making BC breaks more explicit and thus the whole project more reliable from a consumer perspective. This project is actively maintained and has received some major updates in the last weeks and has some major updates planned in the next weeks. Given our current versioning scheme, we'd like to ensure all anticipated BC breaks will be merged before the planned v1.0.0 release.

As such, I've set up a roadmap that enlists only the major changes for each version among with planned release dates towards a stable v1.0.0 release:

v0.4.11 ✅

  • Released 2016-09-15
  • Decode chunked transfer encoding

v0.4.x ✅

v0.4.17 ✅

  • Released 2017-03-20
  • Uppercase chunk headers

v0.5.0 ✅

  • Released 2017-05-22
  • Stream semantics
  • Replace SocketClient with Socket
  • Cancellation support
  • No longer considered beta quality

v0.6.0

  • Planned 2019-??
  • Request promise

v0.7.0

  • Planned 2019-??
  • PSR-7 response
  • Reduce public API

v0.8.0

  • Planned 2019-??
  • Merge this component into Http

v1.0.0

  • Planned 2019-??
  • No new changes planned, this should merely mark the previous release as "stable"

This ticket aims to serve as a basic overview and does not contain every single change. Please also see the milestone links and the CHANGELOG for more details.

Obviously, this roadmap is subject to change and I'll try to keep it updated as we progress. In order to avoid cluttering this, please keep discussion in this ticket to a minimum and consider reaching out to us through new tickets or Twitter etc.

Support for Expect: 100 continue

Now when we support Transfer-Encoding: chunked it would be great to have at least basic continue support. I'll file example MR soon

In-built request timeout functionality

Currently, request timeouts can be implemented somewhat messily by specifying loop timers and attempting to manually close the request. They should ideally be in-built.

question regarding writing

Hello,
Just a question, using the function $connection->write($data); is it blocking or sync?
I mean if i have to write 10000 times on a connection and one of these times it takes a while for some reasons, will it delay all the other writings or looping the write function and then $loop->run(); , everything starts as unique thread?

Thanks

Allow client creation without DNS resolver

According to https://github.com/reactphp/socket-client/blob/master/src/Connector.php#L9 Connector is deprecated. This could allow to change https://github.com/reactphp/http-client/blob/master/src/Factory.php#L12 by providing a default NULL resolver and in that case use a TcpConnector instead of a DnsConnector.

This would require upping socket-client dependency to 0.5 from 0.3 but would allow reducing some boilerplate code especially for simple test apps and local installations.

Add docs for request error handling

Examples miss a really important point: error handly for a request like a simple timeout.
simply like this:

$request = $client->request('GET', $url);
$request->on('error', function ($e) {
  $socketerror = $e->getPrevious();
  echo "request failed with error: " . $e->getMessage() . PHP_EOL;
});

I spend too much time just for this so i think it should be added in the README or the exemple section.

Unable to validate "27C" as chunk length header

I don't know how to reproduce problem using only this library, but with WyriHaximus/react-guzzle-psr7 this code

<?php

require_once __DIR__ . '/vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$handler = new WyriHaximus\React\GuzzlePsr7\HttpClientAdapter($loop);
$client = new GuzzleHttp\Client([
    'handler' => GuzzleHttp\HandlerStack::create($handler),
]);

$request = new GuzzleHttp\Psr7\Request('GET', 'https://wizzair.com/');
$promise = $client->sendAsync($request)->then(function (Psr\Http\Message\ResponseInterface $response) {
    echo (string)$response->getBody();
});

$promise->wait();

produces

PHP Fatal error:  Uncaught Exception: Unable to validate "27C" as chunk length header in /home/mateusz/git/react-guzzle-test/vendor/react/http-client/src/ChunkedStreamDecoder.php:122
Stack trace:
#0 /home/mateusz/git/react-guzzle-test/vendor/react/http-client/src/ChunkedStreamDecoder.php(70): React\HttpClient\ChunkedStreamDecoder->iterateBuffer()
#1 [internal function]: React\HttpClient\ChunkedStreamDecoder->handleData('="Original" src...', Object(React\Stream\Stream))
#2 /home/mateusz/git/react-guzzle-test/vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php(64): call_user_func_array(Array, Array)
#3 /home/mateusz/git/react-guzzle-test/vendor/react/stream/src/Stream.php(173): Evenement\EventEmitter->emit('data', Array)
#4 [internal function]: React\Stream\Stream->handleData(Resource id #96, Object(React\EventLoop\StreamSelectLoop))
#5 /home/mateusz/git/react-guzzle-test/vendor/react/event-loop/src/StreamSelectLoop.php(232): call_user_func(Array, Resource id #96, Object(React\EventLoop\StreamSelectLoop))
#6 /home/mat in /home/mateusz/git/react-guzzle-test/vendor/react/http-client/src/Request.php on line 150

Time Out Question

http-client How to set time out
I am Chinese, My English is not good Please understand

Error on "close" event

i wanted to reject the promise based on timer to cancel the request.
Kindly advise

PHP Fatal error: Uncaught TypeError: Argument 1 passed to {closure}() must be an instance of Exception, none given, called in /vendor/evenement/evenement/src/Evenement/EventEmitterTrait.php on line 123

public function call()
    {
        $this->request = $this->client->request($this->method, $this->url);
        $deferred = new Deferred();
        $this->request->on('response', function (\React\HttpClient\Response $response)use ($deferred) {
            $response->on('data', function ($chunk) use ($deferred){
                    $deferred->resolve($chunk);
            });
        });
        $this->request->on('error', function (\Exception $e) use ($deferred){
            $deferred->reject($e->getMessage());
        });

Error thrown here
        // $this->request->on('close', function (\Exception $e) use ($deferred){
        //    $deferred->reject($e->getMessage());
        // });
        
        $this->request->end();
        return $deferred->promise();
    }

    public function close(){
        $this->request->close();
    }
$mainLoop->addTimer(15.0, function () use ($requestEndpoints) {
        $requestEndpoints->close();
    });

Failed to access a TLS server

Trying to access TLS servers, I keep getting errors. For example when trying to access https://www.google.com , I get this:

An error occurred in the underlying stream:
Unable to complete SSL/TLS handshake:
stream_socket_enable_crypto():
Peer certificate CN=`www.google.com' did not match expected CN=`74.125.140.106'

My code is:

        $request = $this->client->request('GET', 'https://www.google.com', [ ]);
        $request->on('error', function ($err) use ($callback) { $callback($err, NULL); });
        $request->on('response', function ($response) {
            $response->on('data', function ($data, $response) {
                echo "Got response data";
                var_dump([$data, $response]);
            });
        });
        $request->end();

Unclear event semantics

The Response is a ReadableStreamInterface, however it does not currently obey its event semantics:

  • It will never emit a close event
  • It will emit an end event if the underlying stream closes
  • It will emit an end event with an error if the underlying stream ends

We should obey the stream semantics here, see also reactphp/stream#59.

Note that this will result in a BC break, so we should target the v0.5.0 release here. Even the examples (#69) highlight how the end event should actually be a close event instead.

Error when sending large messages over secure HTTPS with older PHP versions

When sending large contents via POST, specifically images (~90kb+) the connection simply hangs. I have already filed the same bug in for the WyriHaximus guzzle adapter here: WyriHaximus/react-guzzle-psr7#10.

Here is some short example code:

<?php

require_once 'vendor/autoload.php';


$mp = new p3k\Multipart;
$mp->addPart('field', 'value');
$mp->addFile('file', 'large-image', 'image/jpeg');

$url = "https://example.com";

$loop = React\EventLoop\Factory::create();
$dnsFactory = new React\Dns\Resolver\Factory();
$dns = $dnsFactory->createCached('192.168.2.1', $loop);
$factory = new React\HttpClient\Factory();
$client = $factory->create($loop, $dns);

$loop->nextTick(function() use ($client, $url, $mp) {

    $request = $client->request("POST", $url, [
        'Content-Type'      => $mp->contentType(),
        'Content-Length'    => strlen($mp->data())
    ]);

    $request->on('response', function($response) {
        print "WE HAVE RESPONSE: ".$response->getCode()."\n";
        print_r($response->getHeaders());
        $response->on('data', function($data, $response) {
            print "DATA ...\n";
            print $data;
        });
        $response->on('end', function() {
            die("IT'S OVER!\n");
        });
    });

    $request->end($mp->data());
});

while (1) $loop->run();
print "DONE!\n";

You will need a URL to POST to as well as an image file to send. Both should not be hard to get a hold of to test this.

Incomplete control code while parsing chunked stream

Hi,

I'm using wyrihaximus/react-guzzle-psr7 to send async requests with Guzzle HTTP, however, the server I am requesting has recently started using chunked HTTP requests. I saw that chunked requests was recently introduced into the HTTP client by the creator of the package I am using however I am still getting the error Stream ended with incomplete control code. Is this a client side problem?

Thanks

POST method doesn't working

I'm using this code to send a POST request but it's not working.

<?php

require 'vendor/autoload.php';

$loop = React\EventLoop\Factory::create();

$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dnsResolver = $dnsResolverFactory->createCached('8.8.8.8', $loop);

$factory = new React\HttpClient\Factory();
$client = $factory->create($loop, $dnsResolver);

$request = $client->request('POST', 'http://respondto.it/bomber');

$request->write("test=12345");

$request->on(
    'response',
    function ($response) {
        echo "recived".PHP_EOL;
        $response->on(
            'data',
            function ($data) {
                var_dump($data);
            }
        );
    }
);
$request->end();
$loop->run();

Result is

POST /bomber
Headers

Connect-Time: 1
Connection: close
Host: respondto.it
Total-Route-Time: 0
User-Agent: React/alpha
Version: HTTP/1.1
Via: 1.1 vegur
X-Forwarded-Port: 80
X-Forwarded-Proto: http
X-Request-Id: 6596bd3e-1505-45a9-adbd-a0a5f4c7ea52

I know that i'm doing something wrong but i couldn't find solution.

Hanging connector

require_once __DIR__.'/vendor/autoload.php';

$inner = count($argv) > 1;

$loop = new \React\EventLoop\ExtEventLoop();

$connector = new React\Socket\Connector($loop);

runme($loop, $connector, $inner);

if (!$inner) {
    echo "Outer start\n";
    $loop->run();
}

function runme(\React\EventLoop\LoopInterface $loop, React\Socket\Connector $connector, $inner)
{
    $connector->connect('tcp://127.0.0.1:3306')->
    then(function (\React\Socket\ConnectionInterface $conn) {
        echo ("Hello MySQL!\n");
        $conn->close();
    },function ($e) {
        echo ("Bye MySQL!\n");
    })->done();

    if ($inner) {
        echo "Inner start\n";
        $loop->run();
    }

    echo "Exit runme\n";
}

it works proper and returns:

$ php ./testMysql.php 
Exit runme
Outer start
Hello MySQL!
$ php ./testMysql.php  --inner
Inner start
Hello MySQL!
Exit runme

But if you go into \React\Socket\TcpConnector::waitForStreamOnce() and remove $canceller function in new Promise object like below, than it hangs again. Looks like it works in latest version of react a kind of accidentally as socket not stored obvious way, and in fact similar to code in v0.4.6.

private function waitForStreamOnce($stream)
    {
        $loop = $this->loop;

        return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream) {
            $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject) {
                $loop->removeWriteStream($stream);

                // The following hack looks like the only way to
                // detect connection refused errors with PHP's stream sockets.
                if (false === stream_socket_get_name($stream, true)) {
                    fclose($stream);

                    $reject(new \RuntimeException('Connection refused'));
                } else {
                    $resolve(new Connection($stream, $loop));
                }
            });
        });
    }

$ php ./testMysql.php  --inner
Inner start
.....HANGING
$ php ./testMysql.php 
Exit runme
Outer start
...HANGING

Initial discussion here: https://stackoverflow.com/questions/47551054/php-7-1-pecl-event-libevent-is-hanging-in-weird-case

[bug]Concurrent with more than 1000 requests.

Hello there

I met two problem.

  • Concurrent with more than 1000 requests.

I find that if I send more than 1000 requests at the same time, some requests will get timeout exception.

  • unable handle http message with bad separator:

Some http header and body are concatenated by "\n\n", not "\r\n\r\n". this package cannnot handle theme correctly, but browser can.
ps: I encountered this problem on win, I do not know Linux will not have this problem

Call to undefined method React\HttpClient\ChunkedStreamDecoder::end()

Hello,

I encountered the following error using the react http client (v0.4.11) :

Call to undefined method React\HttpClient\ChunkedStreamDecoder::end()

Indeed in Response.php it seem that $this->stream->end()is not (always?) resolved

Fixed my issue by getting back to 0.4.10.

Cheers.

Delay Request Connection

I am working on a project which will make a lot of connections to external APIs and I need to queue the requests so that I can limit the number of active requests. This is needed to manage the load on the API (also may be useful for APIs that enforce a request limit).

For this reason I want to build the request and attach all event handlers, also write some data, but not yet send it.

Currently write() will directly open the connection and send the data.

http-client/src/Request.php

Lines 103 to 107 in d779a3b

// otherwise buffer and try to establish connection
$this->pendingWrites .= $data;
if (self::STATE_WRITING_HEAD > $this->state) {
$this->writeHead();
}

I suggest to add a method that allows adding data to $pendingWrites without sending the request.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.