diff --git a/CHANGELOG.md b/CHANGELOG.md index 802f14a..d1a3816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,20 +4,13 @@ CHANGELOG 0.36.0 ------ - Added `--listen=HTTP_PORT` option to start HTTP server. It allows external - processes to send actions to perform via POST method, or retrieve the - current state of the finder. + processes to send actions to perform via POST method. ```sh # Start HTTP server on port 6266 fzf --listen 6266 - # Send action to the server via POST method - curl localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )' - - # Retrieve the current item - curl localhost:6266 - - # Retrieve the query string - curl localhost:6266/query + # Send actions to the server + curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )' ``` - Added `next-selected` and `prev-selected` actions to move between selected items diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 1bcc392..6c71f14 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -723,21 +723,14 @@ e.g. \fBfzf --multi | fzf --sync\fR .TP .B "--listen=HTTP_PORT" Start HTTP server on the given port. It allows external processes to send -actions to perform via POST method, or retrieve the current state of the -finder. +actions to perform via POST method. e.g. \fB# Start HTTP server on port 6266 fzf --listen 6266 - # Send action to the server via POST method - curl localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )' - - # Retrieve the current item - curl localhost:6266 - - # Retrieve the query string - curl localhost:6266/query + # Send action to the server + curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )' \fR The port number is exported as \fB$FZF_LISTEN_PORT\fR on the child processes. diff --git a/src/options.go b/src/options.go index f5fa24a..1fb649f 100644 --- a/src/options.go +++ b/src/options.go @@ -113,7 +113,7 @@ const usage = `usage: fzf [options] --read0 Read input delimited by ASCII NUL characters --print0 Print output delimited by ASCII NUL characters --sync Synchronous search for multi-staged filtering - --listen=HTTP_PORT Start HTTP server to receive actions + --listen=HTTP_PORT Start HTTP server to receive actions (POST /) --version Display version information and exit Environment variables diff --git a/src/server.go b/src/server.go index cc0a55d..421bc20 100644 --- a/src/server.go +++ b/src/server.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "net" - "regexp" "strconv" "strings" "time" @@ -14,18 +13,13 @@ import ( const ( crlf = "\r\n" - httpPattern = "^(GET|POST) (/[^ ]*) HTTP" httpOk = "HTTP/1.1 200 OK" + crlf httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf httpReadTimeout = 10 * time.Second maxContentLength = 1024 * 1024 ) -var ( - httpRegexp *regexp.Regexp -) - -func startHttpServer(port int, requestChan chan []*action, responseChan chan string) error { +func startHttpServer(port int, channel chan []*action) error { if port == 0 { return nil } @@ -35,7 +29,6 @@ func startHttpServer(port int, requestChan chan []*action, responseChan chan str return fmt.Errorf("port not available: %d", port) } - httpRegexp = regexp.MustCompile(httpPattern) go func() { for { conn, err := listener.Accept() @@ -46,7 +39,7 @@ func startHttpServer(port int, requestChan chan []*action, responseChan chan str continue } } - conn.Write([]byte(handleHttpRequest(conn, requestChan, responseChan))) + conn.Write([]byte(handleHttpRequest(conn, channel))) conn.Close() } listener.Close() @@ -61,14 +54,12 @@ func startHttpServer(port int, requestChan chan []*action, responseChan chan str // * No --listen: 2.8MB // * --listen with net/http: 5.7MB // * --listen w/o net/http: 3.3MB -func handleHttpRequest(conn net.Conn, requestChan chan []*action, responseChan chan string) string { +func handleHttpRequest(conn net.Conn, channel chan []*action) string { contentLength := 0 body := "" - response := func(header string, message string) string { - return header + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message) - } bad := func(message string) string { - return response(httpBadRequest, strings.TrimSpace(message)+"\n") + message += "\n" + return httpBadRequest + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message) } conn.SetReadDeadline(time.Now().Add(httpReadTimeout)) scanner := bufio.NewScanner(conn) @@ -89,13 +80,8 @@ func handleHttpRequest(conn net.Conn, requestChan chan []*action, responseChan c text := scanner.Text() switch section { case 0: - httpMatch := httpRegexp.FindStringSubmatch(text) - if len(httpMatch) != 3 { - return bad("invalid HTTP request: " + text) - } - if httpMatch[1] == "GET" { - requestChan <- []*action{{t: actEvaluate, a: httpMatch[2][1:]}} - return response(httpOk, <-responseChan) + if !strings.HasPrefix(text, "POST / HTTP") { + return bad("invalid request method") } section++ case 1: @@ -134,6 +120,7 @@ func handleHttpRequest(conn net.Conn, requestChan chan []*action, responseChan c if len(actions) == 0 { return bad("no action specified") } - requestChan <- actions + + channel <- actions return httpOk } diff --git a/src/terminal.go b/src/terminal.go index 3ba189f..d0c0a9d 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -201,8 +201,7 @@ type Terminal struct { sigstop bool startChan chan fitpad killChan chan int - serverRequestChan chan []*action - serverResponseChan chan string + serverChan chan []*action slab *util.Slab theme *tui.ColorTheme tui tui.Renderer @@ -277,7 +276,6 @@ const ( actDeleteChar actDeleteCharEOF actEndOfLine - actEvaluate actForwardChar actForwardWord actKillLine @@ -601,8 +599,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { theme: opts.Theme, startChan: make(chan fitpad, 1), killChan: make(chan int), - serverRequestChan: make(chan []*action), - serverResponseChan: make(chan string), + serverChan: make(chan []*action), tui: renderer, initFunc: func() { renderer.Init() }, executing: util.NewAtomicBool(false)} @@ -624,7 +621,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true) } - if err := startHttpServer(t.listenPort, t.serverRequestChan, t.serverResponseChan); err != nil { + if err := startHttpServer(t.listenPort, t.serverChan); err != nil { errorExit(err.Error()) } @@ -2534,7 +2531,7 @@ func (t *Terminal) Loop() { select { case event = <-eventChan: needBarrier = true - case actions = <-t.serverRequestChan: + case actions = <-t.serverChan: event = tui.Invalid.AsEvent() needBarrier = false } @@ -2617,15 +2614,6 @@ func (t *Terminal) Loop() { t.executeCommand(a.a, false, a.t == actExecuteSilent) case actExecuteMulti: t.executeCommand(a.a, true, false) - case actEvaluate: - response := "" - switch a.a { - case "", "current": - response = t.currentItem().AsString(t.ansi) - case "query": - response = string(t.input) - } - t.serverResponseChan <- response case actInvalid: t.mutex.Unlock() return false diff --git a/test/test_go.rb b/test/test_go.rb index 5dcad12..4c42439 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -2440,12 +2440,9 @@ class TestGoFZF < TestBase def test_listen tmux.send_keys 'seq 10 | fzf --listen 6266', :Enter tmux.until { |lines| assert_equal 10, lines.item_count } - Net::HTTP.post(URI('http://localhost:6266'), 'change-query(00)+reload(seq 100)+change-prompt:hundred> ') + Net::HTTP.post(URI('http://localhost:6266'), 'change-query(yo)+reload(seq 100)+change-prompt:hundred> ') tmux.until { |lines| assert_equal 100, lines.item_count } - tmux.until { |lines| assert_equal 'hundred> 00', lines[-1] } - assert_equal '100', Net::HTTP.get(URI('http://localhost:6266')) - assert_equal '100', Net::HTTP.get(URI('http://localhost:6266/current')) - assert_equal '00', Net::HTTP.get(URI('http://localhost:6266/query')) + tmux.until { |lines| assert_equal 'hundred> yo', lines[-1] } end end