From 3e9b4444411446aa6c9e32a10194a9355fad5f98 Mon Sep 17 00:00:00 2001 From: MARiASoCute <1056159381@qq.com> Date: Thu, 8 Oct 2026 19:55:50 +0800 Subject: [PATCH] Fix Http ip memory leak --- ext-src/php_swoole_http_server.h | 1 + ext-src/swoole_http_server.cc | 11 +++- ext-src/swoole_server.cc | 2 +- .../server_addr_released_on_close.phpt | 66 +++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 tests/swoole_http_server/server_addr_released_on_close.phpt diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h index e12a2af3fe7..eb0d5a50bb9 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_can_cache_session_addr(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 640ed808286..1de3e21e711 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -448,6 +448,14 @@ static void http_server_session_track_ip(swoole::Server *server, http_server_add_server_array(ht, known_string, iter->second.ptr()); } +/** + * 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_can_cache_session_addr(const Server *server) { + return server->is_base_mode() || (server->is_process_mode() && server->dispatch_mode == Server::DISPATCH_FDMOD); +} + void swoole_http_server_populate_ip_and_port( Server *server, HashTable *ht, Connection *conn, SessionId session_id, bool keepalive) { http_server_add_server_array(ht, SW_ZSTR_KNOWN(SW_ZEND_STR_SERVER_PORT), (zend_long) conn->local_port); @@ -458,8 +466,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_can_cache_session_addr(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 b0f5006a071..7e46f388eff 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -1005,7 +1005,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_can_cache_session_addr(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 00000000000..715f75b5235 --- /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