mirror of
https://github.com/guzzle/guzzle.git
synced 2025-02-24 01:53:58 +01:00
240 lines
6.8 KiB
PHP
240 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace Guzzle\Tests\Http;
|
|
|
|
use Guzzle\Http\Exception\BadResponseException;
|
|
use Guzzle\Common\Exception\RuntimeException;
|
|
use Guzzle\Http\Message\Request;
|
|
use Guzzle\Http\Message\Response;
|
|
use Guzzle\Http\Message\RequestInterface;
|
|
use Guzzle\Http\Message\RequestFactory;
|
|
use Guzzle\Http\Client;
|
|
|
|
/**
|
|
* The Server class is used to control a scripted webserver using node.js that
|
|
* will respond to HTTP requests with queued responses.
|
|
*
|
|
* Queued responses will be served to requests using a FIFO order. All requests
|
|
* received by the server are stored on the node.js server and can be retreived
|
|
* by calling {@see Server::getReceivedRequests()}.
|
|
*
|
|
* Mock responses that don't require data to be transmitted over HTTP a great
|
|
* for testing. Mock respones, however, cannot test the actual sending of an
|
|
* HTTP request using cURL. This test server allows the simulation of any
|
|
* number of HTTP request response transactions to test the actual sending of
|
|
* requests over the wire without having to leave an internal network.
|
|
*/
|
|
class Server
|
|
{
|
|
const DEFAULT_PORT = 8124;
|
|
const REQUEST_DELIMITER = "\n----[request]\n";
|
|
|
|
/**
|
|
* @var int Port that the server will listen on
|
|
*/
|
|
private $port;
|
|
|
|
/**
|
|
* @var bool Is the server running
|
|
*/
|
|
private $running = false;
|
|
|
|
/**
|
|
* @var Client
|
|
*/
|
|
private $client;
|
|
|
|
/**
|
|
* Create a new scripted server
|
|
*
|
|
* @param int $port (optional) Port to listen on (defaults to 8124)
|
|
*/
|
|
public function __construct($port = null)
|
|
{
|
|
$this->port = $port ?: self::DEFAULT_PORT;
|
|
$this->client = new Client($this->getUrl());
|
|
}
|
|
|
|
/**
|
|
* Destructor to safely shutdown the node.js server if it is still running
|
|
*
|
|
* @codeCoverageIgnore
|
|
*/
|
|
public function __destruct()
|
|
{
|
|
// Only shut the server down if the object knows it started the server
|
|
if ($this->running) {
|
|
try {
|
|
$this->stop();
|
|
} catch (\Exception $e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flush the received requests from the server
|
|
*
|
|
* @return bool Returns TRUE on success or FALSE on failure
|
|
* @throws RuntimeException
|
|
*/
|
|
public function flush()
|
|
{
|
|
if (!$this->isRunning()) {
|
|
return false;
|
|
}
|
|
|
|
return $this->client->delete('guzzle-server/requests')
|
|
->send()->getStatusCode() == 200;
|
|
}
|
|
|
|
/**
|
|
* Queue an array of responses or a single response on the server.
|
|
*
|
|
* Any currently queued responses will be overwritten. Subsequent requests
|
|
* on the server will return queued responses in FIFO order.
|
|
*
|
|
* @param array|Response $responses A single or array of Responses to queue
|
|
*
|
|
* @return bool Returns TRUE on success or FALSE on failure
|
|
* @throws BadResponseException
|
|
*/
|
|
public function enqueue($responses)
|
|
{
|
|
$data = array();
|
|
foreach ((array) $responses as $response) {
|
|
|
|
// Create the response object from a string
|
|
if (is_string($response)) {
|
|
$response = Response::fromMessage($response);
|
|
} else if (!($response instanceof Response)) {
|
|
throw new BadResponseException(
|
|
'Responses must be strings or implement Response'
|
|
);
|
|
}
|
|
|
|
$data[] = array(
|
|
'statusCode' => $response->getStatusCode(),
|
|
'reasonPhrase' => $response->getReasonPhrase(),
|
|
'headers' => $response->getHeaders()->getAll(),
|
|
'body' => $response->getBody(true)
|
|
);
|
|
}
|
|
|
|
$request = $this->client->put('guzzle-server/responses', null, json_encode($data));
|
|
$response = $request->send();
|
|
|
|
return $response->getStatusCode() == 200;
|
|
}
|
|
|
|
/**
|
|
* Check if the server is running
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isRunning()
|
|
{
|
|
if ($this->running) {
|
|
return true;
|
|
} else {
|
|
$fp = @fsockopen('127.0.0.1', $this->port, $errno, $errstr, 1);
|
|
if (!$fp) {
|
|
return false;
|
|
} else {
|
|
fclose($fp);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the URL to the server
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getUrl()
|
|
{
|
|
return 'http://127.0.0.1:' . $this->getPort() . '/';
|
|
}
|
|
|
|
/**
|
|
* Get the port that the server is listening on
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getPort()
|
|
{
|
|
return $this->port;
|
|
}
|
|
|
|
/**
|
|
* Get all of the received requests
|
|
*
|
|
* @param bool $hydrate (optional) Set to TRUE to turn the messages into
|
|
* actual {@see RequestInterface} objects. If $hydrate is FALSE,
|
|
* requests will be returned as strings.
|
|
*
|
|
* @return array
|
|
* @throws RuntimeException
|
|
*/
|
|
public function getReceivedRequests($hydrate = false)
|
|
{
|
|
$data = array();
|
|
|
|
if ($this->isRunning()) {
|
|
$response = $this->client->get('guzzle-server/requests')->send();
|
|
$data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true)));
|
|
if ($hydrate) {
|
|
$data = array_map(function($message) {
|
|
return RequestFactory::getInstance()->fromMessage($message);
|
|
}, $data);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Start running the node.js server in the background
|
|
*/
|
|
public function start()
|
|
{
|
|
if (!$this->isRunning()) {
|
|
exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' . $this->port . ' >> /tmp/server.log 2>&1 &');
|
|
// Shut the server down when the script exits unexpectedly
|
|
register_shutdown_function(array($this, 'stop'));
|
|
// Wait at most 5 seconds for the server the setup before proceeding
|
|
$start = time();
|
|
while (!$this->isRunning() && time() - $start < 5);
|
|
// @codeCoverageIgnoreStart
|
|
if (!$this->isRunning()) {
|
|
throw new RuntimeException(
|
|
'Unable to contact server.js. Have you installed node.js '
|
|
. 'v0.5.0+? The node.js executable, node, must also be in '
|
|
. 'your path.'
|
|
);
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
}
|
|
|
|
$this->running = true;
|
|
}
|
|
|
|
/**
|
|
* Stop running the node.js server
|
|
*
|
|
* @return bool Returns TRUE on success or FALSE on failure
|
|
* @throws RuntimeException
|
|
*/
|
|
public function stop()
|
|
{
|
|
if (!$this->isRunning()) {
|
|
return false;
|
|
}
|
|
|
|
$this->running = false;
|
|
|
|
return $this->client->delete('guzzle-server')->send()
|
|
->getStatusCode() == 200;
|
|
}
|
|
}
|