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).
This commit is contained in:
Junegunn Choi
2026-05-25 14:10:04 +09:00
parent 4b23aa45a8
commit 7963a2c658
+4 -3
View File
@@ -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")
}