1+ <?php
2+ /**
3+ * Simple synchronous HTTP server for async tests
4+ * This server runs in a separate process to avoid interference with async tests
5+ * Usage: Start this server before running tests, or use start_test_server_process()
6+ */
7+
8+ function start_test_server_process ($ port = 8088 ) {
9+ $ server_script = __FILE__ ;
10+ $ cmd = PHP_BINARY . " $ server_script $ port > /dev/null 2>&1 & echo $! " ;
11+ $ pid = exec ($ cmd );
12+
13+ // Wait a bit for server to start
14+ usleep (100000 ); // 100ms
15+
16+ return (int )$ pid ;
17+ }
18+
19+ function stop_test_server_process ($ pid ) {
20+ if (PHP_OS_FAMILY === 'Windows ' ) {
21+ exec ("taskkill /PID $ pid /F 2>NUL " );
22+ } else {
23+ exec ("kill $ pid 2>/dev/null " );
24+ }
25+ }
26+
27+ function run_test_server ($ port ) {
28+ $ server = stream_socket_server ("tcp://127.0.0.1: $ port " , $ errno , $ errstr );
29+ if (!$ server ) {
30+ die ("Failed to create server: $ errstr ( $ errno) \n" );
31+ }
32+
33+ echo "Test HTTP server running on 127.0.0.1: $ port \n" ;
34+
35+ while (true ) {
36+ $ client = stream_socket_accept ($ server );
37+ if (!$ client ) continue ;
38+
39+ // Read the request
40+ $ request = '' ;
41+ while (!feof ($ client )) {
42+ $ line = fgets ($ client );
43+ $ request .= $ line ;
44+ if (trim ($ line ) === '' ) break ; // End of headers
45+ }
46+
47+ // Parse request line
48+ $ lines = explode ("\n" , $ request );
49+ $ request_line = trim ($ lines [0 ]);
50+ preg_match ('/^(\S+)\s+(\S+)\s+(\S+)$/ ' , $ request_line , $ matches );
51+
52+ if (count ($ matches ) >= 3 ) {
53+ $ method = $ matches [1 ];
54+ $ path = $ matches [2 ];
55+
56+ // Route requests
57+ $ response = route_test_request ($ method , $ path , $ request );
58+ } else {
59+ $ response = http_test_response (400 , "Bad Request " );
60+ }
61+
62+ fwrite ($ client , $ response );
63+ fclose ($ client );
64+ }
65+
66+ fclose ($ server );
67+ }
68+
69+ function route_test_request ($ method , $ path , $ full_request ) {
70+ switch ($ path ) {
71+ case '/ ' :
72+ return http_test_response (200 , "Hello World " );
73+
74+ case '/json ' :
75+ return http_test_response (200 , '{"message":"Hello JSON","status":"ok"} ' , 'application/json ' );
76+
77+ case '/slow ' :
78+ // Simulate slow response (useful for timeout tests)
79+ usleep (500000 ); // 500ms
80+ return http_test_response (200 , "Slow Response " );
81+
82+ case '/very-slow ' :
83+ // Very slow response (for timeout tests)
84+ sleep (2 );
85+ return http_test_response (200 , "Very Slow Response " );
86+
87+ case '/error ' :
88+ return http_test_response (500 , "Internal Server Error " );
89+
90+ case '/not-found ' :
91+ return http_test_response (404 , "Not Found " );
92+
93+ case '/large ' :
94+ // Large response for testing data transfer
95+ return http_test_response (200 , str_repeat ("ABCDEFGHIJ " , 1000 )); // 10KB
96+
97+ case '/post ' :
98+ if ($ method === 'POST ' ) {
99+ // Extract body if present
100+ $ body_start = strpos ($ full_request , "\r\n\r\n" );
101+ $ body = $ body_start !== false ? substr ($ full_request , $ body_start + 4 ) : '' ;
102+ return http_test_response (200 , "POST received: " . strlen ($ body ) . " bytes " );
103+ } else {
104+ return http_test_response (405 , "Method Not Allowed " );
105+ }
106+
107+ case '/headers ' :
108+ // Return request headers as response
109+ $ headers = [];
110+ $ lines = explode ("\n" , $ full_request );
111+ foreach ($ lines as $ line ) {
112+ $ line = trim ($ line );
113+ if ($ line && strpos ($ line , ': ' ) !== false ) {
114+ $ headers [] = $ line ;
115+ }
116+ }
117+ return http_test_response (200 , implode ("\n" , $ headers ));
118+
119+ case '/echo ' :
120+ // Echo back the entire request
121+ return http_test_response (200 , $ full_request , 'text/plain ' );
122+
123+ case '/redirect ' :
124+ // Simple redirect
125+ return "HTTP/1.1 302 Found \r\n" .
126+ "Location: / \r\n" .
127+ "Content-Length: 0 \r\n" .
128+ "Connection: close \r\n" .
129+ "\r\n" ;
130+
131+ default :
132+ return http_test_response (404 , "Not Found " );
133+ }
134+ }
135+
136+ function http_test_response ($ code , $ body , $ content_type = 'text/plain ' ) {
137+ $ status_text = [
138+ 200 => 'OK ' ,
139+ 302 => 'Found ' ,
140+ 400 => 'Bad Request ' ,
141+ 404 => 'Not Found ' ,
142+ 405 => 'Method Not Allowed ' ,
143+ 500 => 'Internal Server Error '
144+ ][$ code ] ?? 'Unknown ' ;
145+
146+ $ length = strlen ($ body );
147+
148+ return "HTTP/1.1 $ code $ status_text \r\n" .
149+ "Content-Type: $ content_type \r\n" .
150+ "Content-Length: $ length \r\n" .
151+ "Connection: close \r\n" .
152+ "Server: AsyncTestServer/1.0 \r\n" .
153+ "\r\n" .
154+ $ body ;
155+ }
156+
157+ // Helper functions for tests
158+ function get_test_server_address ($ port = 8088 ) {
159+ return "127.0.0.1: $ port " ;
160+ }
161+
162+ function get_test_server_url ($ path = '/ ' , $ port = 8088 ) {
163+ return "http://127.0.0.1: $ port$ path " ;
164+ }
165+
166+ // If called directly, run the server
167+ if (basename (__FILE__ ) === basename ($ _SERVER ['SCRIPT_NAME ' ] ?? '' )) {
168+ $ port = $ argv [1 ] ?? 8088 ;
169+ run_test_server ($ port );
170+ }
0 commit comments