3
namespace GuzzleHttp\Tests;
6
use GuzzleHttp\Message\MessageFactory;
7
use GuzzleHttp\Message\ResponseInterface;
10
* The Server class is used to control a scripted webserver using node.js that
11
* will respond to HTTP requests with queued responses.
13
* Queued responses will be served to requests using a FIFO order. All requests
14
* received by the server are stored on the node.js server and can be retrieved
15
* by calling {@see Server::received()}.
17
* Mock responses that don't require data to be transmitted over HTTP a great
18
* for testing. Mock response, however, cannot test the actual sending of an
19
* HTTP request using cURL. This test server allows the simulation of any
20
* number of HTTP request response transactions to test the actual sending of
21
* requests over the wire without having to leave an internal network.
25
const REQUEST_DELIMITER = "\n----[request]\n";
28
private static $client;
30
public static $started;
31
public static $url = 'http://127.0.0.1:8125/';
32
public static $port = 8125;
35
* Flush the received requests from the server
36
* @throws \RuntimeException
38
public static function flush()
42
return self::$client->delete('guzzle-server/requests');
46
* Queue an array of responses or a single response on the server.
48
* Any currently queued responses will be overwritten. Subsequent requests
49
* on the server will return queued responses in FIFO order.
51
* @param array|ResponseInterface $responses A single or array of Responses
55
public static function enqueue($responses)
59
$factory = new MessageFactory();
65
foreach ((array) $responses as $response) {
67
// Create the response object from a string
68
if (is_string($response)) {
69
$response = $factory->fromMessage($response);
70
} elseif (!($response instanceof ResponseInterface)) {
71
throw new \Exception('Responses must be strings or Responses');
74
$headers = array_map(function ($h) {
75
return implode(' ,', $h);
76
}, $response->getHeaders());
79
'statusCode' => $response->getStatusCode(),
80
'reasonPhrase' => $response->getReasonPhrase(),
81
'headers' => $headers,
82
'body' => base64_encode((string) $response->getBody())
86
self::getClient()->put('guzzle-server/responses', [
87
'body' => json_encode($data)
92
* Get all of the received requests
94
* @param bool $hydrate Set to TRUE to turn the messages into
95
* actual {@see RequestInterface} objects. If $hydrate is FALSE,
96
* requests will be returned as strings.
99
* @throws \RuntimeException
101
public static function received($hydrate = false)
103
if (!self::$started) {
107
$response = self::getClient()->get('guzzle-server/requests');
108
$data = array_filter(explode(self::REQUEST_DELIMITER, (string) $response->getBody()));
110
$factory = new MessageFactory();
111
$data = array_map(function ($message) use ($factory) {
112
return $factory->fromMessage($message);
120
* Stop running the node.js server
122
public static function stop()
124
if (self::$started) {
125
self::getClient()->delete('guzzle-server');
128
self::$started = false;
131
public static function wait($maxTries = 5)
134
while (!self::isListening() && ++$tries < $maxTries) {
138
if (!self::isListening()) {
139
throw new \RuntimeException('Unable to contact node.js server');
143
private static function start()
145
if (self::$started) {
149
if (!self::isListening()) {
150
exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js '
151
. self::$port . ' >> /tmp/server.log 2>&1 &');
155
self::$started = true;
158
private static function isListening()
161
self::getClient()->get('guzzle-server/perf', [
162
'connect_timeout' => 5,
166
} catch (\Exception $e) {
171
private static function getClient()
173
if (!self::$client) {
174
self::$client = new Client(['base_url' => self::$url]);
177
return self::$client;