Skip to content

Commit 20027c7

Browse files
committed
#53: + add logger for http server
1 parent d4da78c commit 20027c7

File tree

1 file changed

+70
-29
lines changed

1 file changed

+70
-29
lines changed

benchmarks/http_server_keepalive.php

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
/**
33
* HTTP Server with Keep-Alive Support
44
* High-performance HTTP server implementation with connection pooling
5-
*
5+
*
66
* Usage:
77
* php http_server_keepalive.php [host] [port]
8-
*
8+
*
99
* Test with wrk:
1010
* wrk -t12 -c400 -d30s --http1.1 http://127.0.0.1:8080/
1111
*/
@@ -15,12 +15,19 @@
1515

1616
use function Async\spawn;
1717
use function Async\await;
18+
use function Async\delay;
1819

1920
// Configuration
2021
$host = $argv[1] ?? '127.0.0.1';
2122
$port = (int)($argv[2] ?? 8080);
2223
$keepaliveTimeout = 30; // seconds
2324

25+
$socketCoroutines = 0;
26+
$socketCoroutinesRun = 0;
27+
$socketCoroutinesFinished = 0;
28+
$requestCount = 0;
29+
$requestHandled = 0;
30+
2431
echo "=== Async HTTP Server with Keep-Alive ===\n";
2532
echo "Starting server on http://$host:$port\n";
2633
echo "Keep-Alive timeout: {$keepaliveTimeout}s\n";
@@ -43,15 +50,15 @@ function parseHttpRequest($request)
4350
// Fast path: find first space and second space to extract URI
4451
$firstSpace = strpos($request, ' ');
4552
if ($firstSpace === false) return '/';
46-
53+
4754
$secondSpace = strpos($request, ' ', $firstSpace + 1);
4855
if ($secondSpace === false) return '/';
49-
56+
5057
$uri = substr($request, $firstSpace + 1, $secondSpace - $firstSpace - 1);
51-
58+
5259
// Check for Connection: close header (simple search)
5360
$connectionClose = stripos($request, 'connection: close') !== false;
54-
61+
5562
return [
5663
'uri' => $uri,
5764
'connection_close' => $connectionClose
@@ -64,11 +71,11 @@ function parseHttpRequest($request)
6471
function processHttpRequest($client, $rawRequest)
6572
{
6673
global $cachedResponses;
67-
74+
6875
$parsedRequest = parseHttpRequest($rawRequest);
6976
$uri = $parsedRequest['uri'];
7077
$shouldKeepAlive = !$parsedRequest['connection_close'];
71-
78+
7279
// Use cached responses for static content
7380
if (isset($cachedResponses[$uri])) {
7481
$responseBody = $cachedResponses[$uri];
@@ -78,11 +85,11 @@ function processHttpRequest($client, $rawRequest)
7885
$responseBody = json_encode(['error' => 'Not Found', 'uri' => $uri], JSON_UNESCAPED_SLASHES);
7986
$statusCode = 404;
8087
}
81-
88+
8289
// Build and send response directly
8390
$contentLength = strlen($responseBody);
8491
$statusText = $statusCode === 200 ? 'OK' : 'Not Found';
85-
92+
8693
if ($shouldKeepAlive) {
8794
$response = 'HTTP/1.1 ' . $statusCode . ' ' . $statusText . "\r\n" .
8895
'Content-Type: application/json' . "\r\n" .
@@ -97,13 +104,13 @@ function processHttpRequest($client, $rawRequest)
97104
'Server: AsyncKeepAlive/1.0' . "\r\n" .
98105
'Connection: close' . "\r\n\r\n" . $responseBody;
99106
}
100-
107+
101108
$written = fwrite($client, $response);
102-
109+
103110
if ($written === false) {
104111
return false; // Write failed
105112
}
106-
113+
107114
return $shouldKeepAlive;
108115
}
109116

@@ -113,53 +120,65 @@ function processHttpRequest($client, $rawRequest)
113120
*/
114121
function handleSocket($client)
115122
{
123+
global $socketCoroutinesRun, $socketCoroutinesFinished;
124+
global $requestCount, $requestHandled;
125+
126+
$socketCoroutinesRun++;
127+
116128
try {
117129
while (true) {
118130
$request = '';
119131
$totalBytes = 0;
120-
132+
121133
// Read HTTP request with byte counting
122134
while (true) {
123135
$chunk = fread($client, 1024);
124-
136+
125137
if ($chunk === false || $chunk === '') {
126138
// Connection closed by client or read error
127139
return;
128140
}
129-
141+
130142
$request .= $chunk;
131143
$totalBytes += strlen($chunk);
132-
144+
133145
// Check for request size limit
134146
if ($totalBytes > 8192) {
135147
// Request too large, close connection immediately
136148
fclose($client);
149+
$requestCount++;
150+
$requestHandled++;
137151
return;
138152
}
139-
153+
140154
// Check if we have complete HTTP request (ends with \r\n\r\n)
141155
if (strpos($request, "\r\n\r\n") !== false) {
142156
break;
143157
}
144158
}
145-
159+
146160
if (empty(trim($request))) {
147161
// Empty request, skip to next iteration
148162
continue;
149163
}
150-
164+
165+
$requestCount++;
166+
151167
// Process request and send response
152168
$shouldKeepAlive = processHttpRequest($client, $request);
153-
169+
170+
$requestHandled++;
171+
154172
if ($shouldKeepAlive === false) {
155173
// Write failed or connection should be closed
156174
return;
157175
}
158-
176+
159177
// Continue to next request in keep-alive connection
160178
}
161-
179+
162180
} finally {
181+
$socketCoroutinesFinished++;
163182
// Always clean up the socket
164183
if (is_resource($client)) {
165184
fclose($client);
@@ -173,37 +192,59 @@ function handleSocket($client)
173192
*/
174193
function startHttpServer($host, $port) {
175194
return spawn(function() use ($host, $port) {
195+
196+
global $socketCoroutines;
197+
176198
// Create server socket
177199
$server = stream_socket_server("tcp://$host:$port", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
178200
if (!$server) {
179201
throw new Exception("Could not create server: $errstr ($errno)");
180202
}
181-
203+
204+
stream_context_set_option($server, 'socket', 'tcp_nodelay', true);
205+
182206
echo "Server listening on $host:$port\n";
183207
echo "Try: curl http://$host:$port/\n";
184208
echo "Benchmark: wrk -t12 -c400 -d30s http://$host:$port/\n\n";
185-
209+
186210
// Simple accept loop - much cleaner!
187211
while (true) {
188212
// Accept new connections
189213
$client = stream_socket_accept($server, 0);
190214
if ($client) {
215+
$socketCoroutines++;
191216
// Spawn a coroutine to handle this client's entire lifecycle
192217
spawn(handleSocket(...), $client);
193218
}
194219
}
195-
220+
196221
fclose($server);
197222
});
198223
}
199224

225+
spawn(function() {
226+
227+
global $socketCoroutines, $socketCoroutinesRun, $socketCoroutinesFinished, $requestCount, $requestHandled;
228+
229+
while(true) {
230+
delay(2000);
231+
echo "Sockets: $socketCoroutines\n";
232+
echo "Coroutines: $socketCoroutinesRun\n";
233+
echo "Finished: $socketCoroutinesFinished\n";
234+
echo "Request: $requestCount\n";
235+
echo "Handled: $requestHandled\n\n";
236+
}
237+
});
200238

201239
// Start server
202240
try {
203241
$serverTask = startHttpServer($host, $port);
204242
await($serverTask);
205-
243+
206244
} catch (Exception $e) {
207245
echo "Server error: " . $e->getMessage() . "\n";
208-
exit(1);
209-
}
246+
} finally {
247+
echo "Sockets: $socketCoroutines\n";
248+
echo "Coroutines: $socketCoroutinesRun\n";
249+
echo "Finished: $socketCoroutinesFinished\n";
250+
}

0 commit comments

Comments
 (0)