From 7963a2c6586c0b9eaa89b8995de8f0e08cf8a4ce Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 25 May 2026 14:10:04 +0900 Subject: [PATCH] server: avoid O(n^2) body accumulation in HTTP listener - handleHttpRequest used `body += text` per token, allocating a new backing array on every append (O(n^2) total copy work) - a single ~390 KB POST monopolised the single-threaded server for ~8 s, blocking all other --listen clients - switch to strings.Builder for amortised O(n) Reported with fix by Michal Majchrowicz and Marcin Wyczechowski (AFINE Team). --- src/server.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server.go b/src/server.go index f462c7ee..33e33755 100644 --- a/src/server.go +++ b/src/server.go @@ -153,7 +153,7 @@ func startHttpServer(address listenAddress, actionChannel chan []*action, getHan func (server *httpServer) handleHttpRequest(conn net.Conn) string { contentLength := 0 apiKey := "" - body := "" + var bodyBuilder strings.Builder answer := func(code string, message string) string { message += "\n" return code + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message) @@ -175,7 +175,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string { token := data[:found+len(crlf)] return len(token), token, nil } - if atEOF || len(body)+len(data) >= contentLength { + if atEOF || bodyBuilder.Len()+len(data) >= contentLength { return 0, data, bufio.ErrFinalToken } return 0, nil, nil @@ -218,7 +218,7 @@ Loop: } } case 2: // Request body - body += text + bodyBuilder.WriteString(text) } } @@ -234,6 +234,7 @@ Loop: return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`) } + body := bodyBuilder.String() if len(body) < contentLength { return bad("incomplete request") }