summaryrefslogtreecommitdiff
path: root/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php
blob: 94eb59a4d6205abef321b18d4ed3a75619ee8ff9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<?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 retrieved
 * by calling {@see Server::getReceivedRequests()}.
 *
 * Mock responses that don't require data to be transmitted over HTTP a great
 * for testing.  Mock response, 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 Port to listen on (defaults to 8124)
     */
    public function __construct($port = null)
    {
        $this->port = $port ?: self::DEFAULT_PORT;
        $this->client = new Client($this->getUrl());
        register_shutdown_function(array($this, 'stop'));
    }

    /**
     * Flush the received requests from the server
     * @throws RuntimeException
     */
    public function flush()
    {
        $this->client->delete('guzzle-server/requests')->send();
    }

    /**
     * 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
     * @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);
            } elseif (!($response instanceof Response)) {
                throw new BadResponseException('Responses must be strings or implement Response');
            }

            $data[] = array(
                'statusCode'   => $response->getStatusCode(),
                'reasonPhrase' => $response->getReasonPhrase(),
                'headers'      => $response->getHeaders()->toArray(),
                'body'         => $response->getBody(true)
            );
        }

        $request = $this->client->put('guzzle-server/responses', null, json_encode($data));
        $request->send();
    }

    /**
     * Check if the server is running
     *
     * @return bool
     */
    public function isRunning()
    {
        if ($this->running) {
            return true;
        }

        try {
            $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send();
            $this->running = true;
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * 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 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)
    {
        $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 &');
            // Wait at most 5 seconds for the server the setup before
            // proceeding.
            $start = time();
            while (!$this->isRunning() && time() - $start < 5);
            if (!$this->running) {
                throw new RuntimeException(
                    'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.'
                );
            }
        }
    }

    /**
     * Stop running the node.js server
     */
    public function stop()
    {
        if (!$this->isRunning()) {
            return false;
        }

        $this->running = false;
        $this->client->delete('guzzle-server')->send();
    }
}