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 (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -626,39 +624,6 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
return &t
|
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 {
|
func borderLines(shape tui.BorderShape) int {
|
||||||
switch shape {
|
switch shape {
|
||||||
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
case tui.BorderHorizontal, tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||||
@ -2535,7 +2500,7 @@ func (t *Terminal) Loop() {
|
|||||||
looping := true
|
looping := true
|
||||||
_, startEvent := t.keymap[tui.Start.AsEvent()]
|
_, startEvent := t.keymap[tui.Start.AsEvent()]
|
||||||
|
|
||||||
t.startServer()
|
startHttpServer(t.listenPort, t.serverChan)
|
||||||
eventChan := make(chan tui.Event)
|
eventChan := make(chan tui.Event)
|
||||||
needBarrier := true
|
needBarrier := true
|
||||||
barrier := make(chan bool)
|
barrier := make(chan bool)
|
||||||
|
Loading…
Reference in New Issue
Block a user