1<?php
2
3function run_server(string $payloadFile): string {
4    $cmd = [getenv("TEST_PHP_EXECUTABLE"), "-n", __DIR__ . "/payload_server.php", $payloadFile];
5    $descriptorspec = array(
6        0 => STDIN,
7        1 => STDOUT,
8        2 => ['pipe', 'w'],
9    );
10    $proc = proc_open($cmd, $descriptorspec, $pipes);
11
12    // First, wait for the payload server to declare itself ready.
13    $bound = null;
14    stream_set_blocking($pipes[2], false);
15    for ($i = 0; $i < 60; $i++) {
16        usleep(50000); // 50ms per try
17        $status = proc_get_status($proc);
18        if (empty($status['running'])) {
19            echo "Server is not running\n";
20            proc_terminate($proc);
21            exit(1);
22        }
23        while (($line = fgets($pipes[2])) !== false) {
24            if (preg_match('/FB payload server listening on (.+)/', $line, $matches)) {
25                $bound = $matches[1];
26                // Now that we've identified the listen address, close STDERR.
27                // Otherwise the pipe may clog up with unread log messages.
28                fclose($pipes[2]);
29                break 2;
30            }
31        }
32    }
33
34    if ($bound === null) {
35        echo "Server did not output startup message";
36        proc_terminate($proc);
37        exit(1);
38    }
39
40    // Now wait for a connection to succeed.
41    // note: even when server prints 'FB payload server listening on localhost:12345'
42    //       it might not be listening yet...need to wait until fsockopen() call returns
43    $error = "Unable to connect to server\n";
44    for ($i=0; $i < 60; $i++) {
45        usleep(50000); // 50ms per try
46        $status = proc_get_status($proc);
47        $fp = fsockopen("tcp://$bound");
48        // Failure, the server is no longer running
49        if (!($status && $status['running'])) {
50            $error = "Server is not running\n";
51            break;
52        }
53        // Success, Connected to servers
54        if ($fp) {
55            $error = '';
56            break;
57        }
58    }
59
60    if ($fp) {
61        fclose($fp);
62    }
63
64    if ($error) {
65        echo $error;
66        proc_terminate($proc);
67        exit(1);
68    }
69
70    register_shutdown_function(
71        function($proc) {
72            proc_terminate($proc);
73            /* Wait for server to shutdown */
74            for ($i = 0; $i < 60; $i++) {
75                $status = proc_get_status($proc);
76                if (!($status && $status['running'])) {
77                    break;
78                }
79                usleep(50000);
80            }
81        },
82        $proc
83    );
84
85    return $bound;
86}
87