diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 2a528e2..d5fc96f 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -831,12 +831,14 @@ e.g. \fB# Start HTTP server on port 6266 fzf --listen 6266 - # Get program state in JSON format (experimental) - curl localhost:6266 - # Send action to the server curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )' + # Get program state in JSON format (experimental) + # * Make sure NOT to access this endpoint from execute/transform actions + # as it will result in a timeout + curl localhost:6266 + # Start HTTP server on port 6266 with remote connections allowed # * Listening on non-localhost address requires using an API key export FZF_API_KEY="$(head -c 32 /dev/urandom | base64)" diff --git a/src/server.go b/src/server.go index e655896..d5a148d 100644 --- a/src/server.go +++ b/src/server.go @@ -30,7 +30,9 @@ const ( httpOk = "HTTP/1.1 200 OK" + crlf httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf httpUnauthorized = "HTTP/1.1 401 Unauthorized" + crlf + httpUnavailable = "HTTP/1.1 503 Service Unavailable" + crlf httpReadTimeout = 10 * time.Second + jsonContentType = "Content-Type: application/json" + crlf maxContentLength = 1024 * 1024 ) @@ -141,7 +143,7 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string { return answer(httpBadRequest, message) } good := func(message string) string { - return answer(httpOk+"Content-Type: application/json"+crlf, message) + return answer(httpOk+jsonContentType, message) } conn.SetReadDeadline(time.Now().Add(httpReadTimeout)) scanner := bufio.NewScanner(conn) @@ -165,8 +167,16 @@ func (server *httpServer) handleHttpRequest(conn net.Conn) string { getMatch := getRegex.FindStringSubmatch(text) if len(getMatch) > 0 { server.actionChannel <- []*action{{t: actResponse, a: getMatch[1]}} - response := <-server.responseChannel - return good(response) + select { + case response := <-server.responseChannel: + return good(response) + case <-time.After(2 * time.Second): + go func() { + // Drain the channel + <-server.responseChannel + }() + return answer(httpUnavailable+jsonContentType, `{"error":"timeout"}`) + } } else if !strings.HasPrefix(text, "POST / HTTP") { return bad("invalid request method") }