xref: /php-src/ext/standard/tests/http/server.inc (revision 95f25837)
1<?php declare(strict_types=1);
2
3function http_server_skipif() {
4
5    if (!function_exists('pcntl_fork')) die('skip pcntl_fork() not available');
6    if (!function_exists('posix_kill')) die('skip posix_kill() not available');
7    if (!stream_socket_server('tcp://localhost:0')) die('skip stream_socket_server() failed');
8}
9
10function http_server_init(&$output = null) {
11    pcntl_alarm(60);
12
13    $server = stream_socket_server('tcp://localhost:0', $errno, $errstr);
14    if (!$server) {
15        return false;
16    }
17
18    if ($output === null) {
19        $output = tmpfile();
20        if ($output === false) {
21            return false;
22        }
23    }
24
25    $pid = pcntl_fork();
26    if ($pid == -1) {
27        die('could not fork');
28    } else if ($pid) {
29        return [
30            'pid' => $pid,
31            'uri' => 'http://' . stream_socket_get_name($server, false),
32        ];
33    }
34
35    return $server;
36}
37
38/* Minimal HTTP server with predefined responses.
39 *
40 * $socket_string is the socket to create and listen on (e.g. tcp://127.0.0.1:1234)
41 * $files is an iterable of files or callable generator yielding files.
42 *        containing N responses for N expected requests. Server dies after N requests.
43 * $output is a stream on which everything sent by clients is written to
44 */
45function http_server($files, &$output = null) {
46
47    if (!is_resource($server = http_server_init($output))) {
48        return $server;
49    }
50
51    if (is_callable($files)) {
52        $files = $files($server);
53    }
54
55    foreach($files as $file) {
56
57        $sock = stream_socket_accept($server);
58        if (!$sock) {
59            exit(1);
60        }
61
62        // read headers
63
64        $content_length = 0;
65
66        stream_set_blocking($sock, false);
67        while (!feof($sock)) {
68
69            list($r, $w, $e) = array(array($sock), null, null);
70            if (!stream_select($r, $w, $e, 1)) continue;
71
72            $line = stream_get_line($sock, 8192, "\r\n");
73            if ($line === '') {
74                fwrite($output, "\r\n");
75                break;
76            } else if ($line !== false) {
77                fwrite($output, "$line\r\n");
78
79                if (preg_match('#^Content-Length\s*:\s*([[:digit:]]+)\s*$#i', $line, $matches)) {
80                    $content_length = (int) $matches[1];
81                }
82            }
83        }
84        stream_set_blocking($sock, true);
85
86        // read content
87
88        if ($content_length > 0) {
89            stream_copy_to_stream($sock, $output, $content_length);
90        }
91
92        // send response
93
94        $fd = fopen($file, 'rb');
95        stream_copy_to_stream($fd, $sock);
96
97        fclose($sock);
98    }
99
100    exit(0);
101}
102
103function http_server_sleep($micro_seconds = 500000)
104{
105    if (!is_resource($server = http_server_init($output))) {
106        return $server;
107    }
108
109    $sock = stream_socket_accept($server);
110    if (!$sock) {
111        exit(1);
112    }
113
114    usleep($micro_seconds);
115
116    fclose($sock);
117
118    exit(0);
119}
120
121function http_server_kill(int $pid) {
122    posix_kill($pid, SIGTERM);
123    pcntl_waitpid($pid, $status);
124}
125