|
|
|
@ -6,6 +6,7 @@ import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
@ -13,13 +14,18 @@ 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
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func startHttpServer(port int, channel chan []*action) error {
|
|
|
|
|
var (
|
|
|
|
|
httpRegexp *regexp.Regexp
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func startHttpServer(port int, requestChan chan []*action, responseChan chan string) error {
|
|
|
|
|
if port == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -29,6 +35,7 @@ func startHttpServer(port int, channel chan []*action) error {
|
|
|
|
|
return fmt.Errorf("port not available: %d", port)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
httpRegexp = regexp.MustCompile(httpPattern)
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
conn, err := listener.Accept()
|
|
|
|
@ -39,7 +46,7 @@ func startHttpServer(port int, channel chan []*action) error {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
conn.Write([]byte(handleHttpRequest(conn, channel)))
|
|
|
|
|
conn.Write([]byte(handleHttpRequest(conn, requestChan, responseChan)))
|
|
|
|
|
conn.Close()
|
|
|
|
|
}
|
|
|
|
|
listener.Close()
|
|
|
|
@ -54,12 +61,14 @@ func startHttpServer(port int, channel chan []*action) error {
|
|
|
|
|
// * 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 {
|
|
|
|
|
func handleHttpRequest(conn net.Conn, requestChan chan []*action, responseChan chan string) 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 {
|
|
|
|
|
message += "\n"
|
|
|
|
|
return httpBadRequest + fmt.Sprintf("Content-Length: %d%s", len(message), crlf+crlf+message)
|
|
|
|
|
return response(httpBadRequest, strings.TrimSpace(message)+"\n")
|
|
|
|
|
}
|
|
|
|
|
conn.SetReadDeadline(time.Now().Add(httpReadTimeout))
|
|
|
|
|
scanner := bufio.NewScanner(conn)
|
|
|
|
@ -80,8 +89,13 @@ func handleHttpRequest(conn net.Conn, channel chan []*action) string {
|
|
|
|
|
text := scanner.Text()
|
|
|
|
|
switch section {
|
|
|
|
|
case 0:
|
|
|
|
|
if !strings.HasPrefix(text, "POST / HTTP") {
|
|
|
|
|
return bad("invalid request method")
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
section++
|
|
|
|
|
case 1:
|
|
|
|
@ -120,7 +134,6 @@ func handleHttpRequest(conn net.Conn, channel chan []*action) string {
|
|
|
|
|
if len(actions) == 0 {
|
|
|
|
|
return bad("no action specified")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
channel <- actions
|
|
|
|
|
requestChan <- actions
|
|
|
|
|
return httpOk
|
|
|
|
|
}
|
|
|
|
|