diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index 775df28ab3..d032288724 100644 --- a/ext-src/php_swoole_http_server.h +++ b/ext-src/php_swoole_http_server.h @@ -25,6 +25,7 @@ bool swoole_http_server_onBeforeRequest(swoole::http::Context *ctx); void swoole_http_server_onAfterResponse(swoole::http::Context *ctx); +bool swoole_http_server_is_ip_cache_enabled(const swoole::Server *server); void swoole_http_server_populate_ip_and_port( swoole::Server *server, HashTable *ht, swoole::Connection *conn, swoole::SessionId session_id, bool keepalive); diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 7ebc6a4cbc..b38c20ee47 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -442,6 +442,12 @@ void swoole_http_server_onAfterResponse(HttpContext *ctx) { } } +// the addresses of a keep-alive session stay cached until php_swoole_http_server_onClose() erases them +// so cache them only when the close event of the session reaches the same worker +bool swoole_http_server_is_ip_cache_enabled(const Server *server) { + return server->is_base_mode() || (server->is_process_mode() && server->dispatch_mode == Server::DISPATCH_FDMOD); +} + /** * When calculating the server-side IP and client-side IP, since these two calculations * share the same memory block, they cannot be performed simultaneously; otherwise, @@ -481,8 +487,7 @@ void swoole_http_server_populate_ip_and_port( http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), SW_ZSTR_KNOWN(key)); http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_REMOTE_ADDR), SW_ZSTR_KNOWN(key)); } else { - if (keepalive && (server->is_base_mode() || - (server->is_process_mode() && server->dispatch_mode == Server::DISPATCH_FDMOD))) { + if (keepalive && swoole_http_server_is_ip_cache_enabled(server)) { http_server_session_track_ip( server, ht, conn, session_id, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_ADDR), server_ips); http_server_session_track_ip( diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index f97f6d5684..5e9ee672dd 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1037,7 +1037,7 @@ void ServerObject::on_before_start() { if (find_http_port) { serv->onReceive = php_swoole_http_server_onReceive; - if (serv->is_base_mode()) { + if (swoole_http_server_is_ip_cache_enabled(serv)) { serv->onClose = php_swoole_http_server_onClose; } } diff --git a/tests/swoole_http_server/server_addr_released_on_close.phpt b/tests/swoole_http_server/server_addr_released_on_close.phpt new file mode 100644 index 0000000000..715f75b523 --- /dev/null +++ b/tests/swoole_http_server/server_addr_released_on_close.phpt @@ -0,0 +1,66 @@ +--TEST-- +swoole_http_server: release the cached addresses of keep-alive connections closed by the client in process mode +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $ips) { + Co\run(function () use ($pm, $ips) { + // the server closes these connections so it does not cache their addresses + $memory = function () use ($pm, $ips): int { + return (int) httpGetBody("http://{$ips[1]}:{$pm->getFreePort()}/memory", [ + 'headers' => ['Connection' => 'close'], + ]); + }; + $before = $memory(); + for ($i = 0; $i < N; $i++) { + $client = new Client($ips[1], $pm->getFreePort()); + Assert::true($client->get('/')); + Assert::eq($client->body, $ips[1]); + $client->close(); + } + // let the worker handle the close events + Co::sleep(0.5); + $after = $memory(); + // a connection that keeps both cached addresses holds at least 64 bytes + Assert::lessThan($after - $before, N * 16); + }); + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + if ($request->server['request_uri'] === '/memory') { + $response->end((string) memory_get_usage()); + } else { + $response->end($request->server['remote_addr']); + } + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE