mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-18 02:55:11 +00:00
Rewrite HTTP server without net/http
This cuts down the binary size from 5.7MB to 3.3MB.
This commit is contained in:
parent
1ba7484d60
commit
4b055bf260
112
src/server.go
Normal file
112
src/server.go
Normal file
@ -0,0 +1,112 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
crlf = "\r\n"
|
||||
httpOk = "HTTP/1.1 200 OK" + crlf
|
||||
httpBadRequest = "HTTP/1.1 400 Bad Request" + crlf
|
||||
)
|
||||
|
||||
func startHttpServer(port int, channel chan []*action) {
|
||||
if port == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
break
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
conn.Write([]byte(handleHttpRequest(conn, channel)))
|
||||
conn.Close()
|
||||
}
|
||||
listener.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
// Here we are writing a simplistic HTTP server without using net/http
|
||||
// package to reduce the size of the binary.
|
||||
//
|
||||
// * No --listen: 2.8MB
|
||||
// * --listen with net/http: 5.7MB
|
||||
// * --listen w/o net/http: 3.3MB
|
||||
func handleHttpRequest(conn net.Conn, channel chan []*action) string {
|
||||
line := 0
|
||||
headerRead := false
|
||||
contentLength := 0
|
||||
body := ""
|
||||
bad := func(message string) string {
|
||||
message += "\n"
|
||||
return httpBadRequest + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message)
|
||||
}
|
||||
scanner := bufio.NewScanner(conn)
|
||||
scanner.Split(func(data []byte, atEOF bool) (int, []byte, error) {
|
||||
found := bytes.Index(data, []byte(crlf))
|
||||
if found >= 0 {
|
||||
token := data[:found+len(crlf)]
|
||||
return len(token), token, nil
|
||||
}
|
||||
if atEOF || len(body)+len(data) >= contentLength {
|
||||
return 0, data, bufio.ErrFinalToken
|
||||
}
|
||||
return 0, nil, nil
|
||||
})
|
||||
|
||||
for scanner.Scan() {
|
||||
text := scanner.Text()
|
||||
if line == 0 && !strings.HasPrefix(text, "POST / HTTP") {
|
||||
return bad("invalid request method")
|
||||
}
|
||||
if text == crlf {
|
||||
headerRead = true
|
||||
}
|
||||
if !headerRead {
|
||||
pair := strings.SplitN(text, ":", 2)
|
||||
if len(pair) == 2 && strings.ToLower(pair[0]) == "content-length" {
|
||||
length, err := strconv.Atoi(strings.TrimSpace(pair[1]))
|
||||
if err != nil {
|
||||
return bad("invalid content length")
|
||||
}
|
||||
contentLength = length
|
||||
}
|
||||
} else if contentLength <= 0 {
|
||||
break
|
||||
} else {
|
||||
body += text
|
||||
}
|
||||
line++
|
||||
}
|
||||
|
||||
errorMessage := ""
|
||||
actions := parseSingleActionList(strings.TrimSpace(string(body)), func(message string) {
|
||||
errorMessage = message
|
||||
})
|
||||
if len(errorMessage) > 0 {
|
||||
return bad(errorMessage)
|
||||
}
|
||||
if len(actions) == 0 {
|
||||
return bad("no action specified")
|
||||
}
|
||||
|
||||
channel <- actions
|
||||
return httpOk
|
||||
}
|
@ -3,10 +3,8 @@ package fzf
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
@ -626,39 +624,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
||||
return &t
|
||||
}
|
||||
|
||||
func (t *Terminal) startServer() {
|
||||
if t.listenPort == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
response := ""
|
||||
actions := parseSingleActionList(string(body), func(message string) {
|
||||
response = message
|
||||
})
|
||||
|
||||
if len(response) > 0 {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
fmt.Fprintln(w, response)
|
||||
return
|
||||
}
|
||||
t.serverChan <- actions
|
||||
})
|
||||
go func() {
|
||||
http.ListenAndServe(fmt.Sprintf(":%d", t.listenPort), nil)
|
||||
}()
|
||||
}
|
||||
|
||||
func borderLines(shape tui.BorderShape) int {
|
||||
switch shape {
|
||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||
@ -2535,7 +2500,7 @@ func (t *Terminal) Loop() {
|
||||
looping := true
|
||||
_, startEvent := t.keymap[tui.Start.AsEvent()]
|
||||
|
||||
t.startServer()
|
||||
startHttpServer(t.listenPort, t.serverChan)
|
||||
eventChan := make(chan tui.Event)
|
||||
needBarrier := true
|
||||
barrier := make(chan bool)
|
||||
|
Loading…
Reference in New Issue
Block a user