diff --git a/routers.go b/routers.go index 85a486a..63e9c03 100644 --- a/routers.go +++ b/routers.go @@ -1,6 +1,8 @@ package main import ( + "bufio" + "bytes" "fmt" "io" "log" @@ -148,7 +150,7 @@ func initRouters(cfg conf) { log.Println(err) break } - go handleHttpClient(conn, handler) + go handleHTTPClient(conn, handler) } }() @@ -269,7 +271,7 @@ func (m myURLMatch) match(r *http.Request, route *mux.RouteMatch) bool { return ret } -func handleHttpClient(c net.Conn, handler http.Handler) { +func handleHTTPClient(c net.Conn, handler http.Handler) { tlsconn := c.(*gnutls.Conn) if err := tlsconn.Handshake(); err != nil { log.Println(err) @@ -277,19 +279,94 @@ func handleHttpClient(c net.Conn, handler http.Handler) { } state := tlsconn.ConnectionState() if state.NegotiatedProtocol == "h2" { - h2conn, err := nghttp2.NewServerConn(tlsconn, handler) + h2conn, err := nghttp2.Server(tlsconn, handler) if err != nil { log.Println(err) } h2conn.Run() return } + defer c.Close() - resp := &http.Response{ - StatusCode: http.StatusHTTPVersionNotSupported, - Status: http.StatusText(http.StatusHTTPVersionNotSupported), - ProtoMajor: 1, - ProtoMinor: 1, + r := bufio.NewReader(tlsconn) + buf := new(bytes.Buffer) + for { + req, err := http.ReadRequest(r) + if err != nil { + return + } + addr := tlsconn.RemoteAddr().String() + req.RemoteAddr = addr + rh := &responseHandler{ + c: tlsconn, + header: http.Header{}, + buf: buf, + } + handler.ServeHTTP(rh, req) + rh.Write(nil) + rh.buf.WriteTo(rh.c) + } +} + +type responseHandler struct { + c net.Conn + statusCode int + header http.Header + responseSend bool + w io.Writer + buf *bytes.Buffer +} + +func (r *responseHandler) WriteHeader(statusCode int) { + if r.responseSend { + return + } + r.buf.Reset() + r.statusCode = statusCode + cl := r.header.Get("content-length") + te := r.header.Get("transfer-encoding") + if cl == "" || te != "" { + if te == "" { + r.header.Set("transfer-encoding", "chunked") + } + r.w = &chunkWriter{r.buf} + } else { + r.w = r.buf + } + fmt.Fprintf(r.buf, "HTTP/1.1 %d %s\r\n", statusCode, + http.StatusText(statusCode)) + for k, v := range r.header { + fmt.Fprintf(r.buf, "%s: %s\r\n", strings.Title(k), strings.Join(v, ",")) + } + fmt.Fprintf(r.buf, "\r\n") + r.responseSend = true +} + +func (r *responseHandler) Header() http.Header { + return r.header +} + +func (r *responseHandler) Write(buf []byte) (int, error) { + if !r.responseSend { + r.WriteHeader(http.StatusOK) + } + n, err := r.w.Write(buf) + if r.buf.Len() > 2048 { + r.buf.WriteTo(r.c) + } + return n, err +} + +var _ http.ResponseWriter = &responseHandler{} + +type chunkWriter struct { + w io.Writer +} + +func (cw *chunkWriter) Write(buf []byte) (int, error) { + n := len(buf) + if n == 0 { + return fmt.Fprintf(cw.w, "0\r\n\r\n") } - resp.Write(tlsconn) + return fmt.Fprintf(cw.w, "%x\r\n%s\r\n", n, string(buf)) }