Revert "Add GET endpoints for getting the state of the finder"

This reverts commit 750b2a6313.

This can cause a deadlock if the endpoints are accessed in the core event
loop via execute action.

  fzf --listen 6266 --bind 'space:execute:curl localhost:6266'

Technically, there's no reason to use the API because the information is
already available via `{}` and `{q}`, but I'd like to completely remove
the risk of misuse.
This commit is contained in:
Junegunn Choi 2022-12-25 19:53:53 +09:00
parent 750b2a6313
commit b7bb973118
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
6 changed files with 22 additions and 64 deletions

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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