Browse Source

Refine the HTTP parser of gnet (#5561)

* Refine the HTTP parser of gnet

* Update HTTP example

* Refine HTTP parser of gnet

* Refactor the logic of parsing HTTP requests

* Refine the logic to send responses

* Rename variable
Andy Pan 5 years ago
parent
commit
652378a1e3
1 changed files with 12 additions and 91 deletions
  1. 12 91
      frameworks/Go/gnet/src/main.go

+ 12 - 91
frameworks/Go/gnet/src/main.go

@@ -1,29 +1,22 @@
 package main
 
 import (
+	"bytes"
 	"flag"
 	"fmt"
 	"log"
 	"runtime"
 	"time"
-	"unsafe"
 
 	"github.com/panjf2000/gnet"
 )
 
-type request struct {
-	proto, method string
-	path, query   string
-	head, body    string
-	remoteAddr    string
-}
-
 type httpServer struct {
 	*gnet.EventServer
 }
 
 type httpCodec struct {
-	req request
+	delimiter []byte
 }
 
 func (hc *httpCodec) Encode(c gnet.Conn, buf []byte) (out []byte, err error) {
@@ -35,15 +28,17 @@ func (hc *httpCodec) Decode(c gnet.Conn) (out []byte, err error) {
 	c.ResetBuffer()
 
 	// process the pipeline
-	var leftover []byte
+	var i int
 pipeline:
-	if leftover, _ = parseReq(buf, &hc.req); len(leftover) == len(buf) {
-		// request not ready, yet
-		return
+	if i = bytes.Index(buf, hc.delimiter); i != -1 {
+		out = append(out, "HTTP/1.1 200 OK\r\nServer: gnet\r\nContent-Type: text/plain\r\nDate: "...)
+		out = time.Now().AppendFormat(out, "Mon, 02 Jan 2006 15:04:05 GMT")
+		out = append(out, "\r\nContent-Length: 13\r\n\r\nHello, World!"...)
+		buf = buf[i+4:]
+		goto pipeline
 	}
-	out = appendResp(out)
-	buf = leftover
-	goto pipeline
+	// request not ready, yet
+	return
 }
 
 func (hs *httpServer) OnInitComplete(srv gnet.Server) (action gnet.Action) {
@@ -72,82 +67,8 @@ func main() {
 	flag.Parse()
 
 	http := new(httpServer)
-	hc := new(httpCodec)
+	hc := &httpCodec{delimiter: []byte("\r\n\r\n")}
 
 	// Start serving!
 	log.Fatal(gnet.Serve(http, fmt.Sprintf("tcp://:%d", port), gnet.WithMulticore(multicore), gnet.WithCodec(hc)))
 }
-
-// appendResp will append a valid http response to the provide bytes.
-// The status param should be the code plus text such as "200 OK".
-// The head parameter should be a series of lines ending with "\r\n" or empty.
-func appendResp(b []byte) []byte {
-	b = append(b, "HTTP/1.1 200 OK\r\nServer: gnet\r\nContent-Type: text/plain\r\nDate: "...)
-
-	b = time.Now().AppendFormat(b, "Mon, 02 Jan 2006 15:04:05 GMT")
-
-	b = append(b, "\r\nContent-Length: 13\r\n\r\nHello, World!"...)
-
-	return b
-}
-
-func b2s(b []byte) string {
-	return *(*string)(unsafe.Pointer(&b))
-}
-
-// parseReq is a very simple http request parser. This operation
-// waits for the entire payload to be buffered before returning a
-// valid request.
-func parseReq(data []byte, req *request) (leftover []byte, err error) {
-	sdata := b2s(data)
-	dlen := len(sdata)
-	var (
-		i, s int
-		head string
-		q    = -1
-	)
-	// method, path, proto line
-	for ; i < dlen; i++ {
-		if sdata[i] == ' ' {
-			req.method = sdata[s:i]
-			for i, s = i+1, i+1; i < dlen; i++ {
-				if sdata[i] == '?' && q == -1 {
-					q = i - s
-				} else if sdata[i] == ' ' {
-					if q != -1 {
-						req.path = sdata[s:q]
-						req.query = req.path[q+1 : i]
-					} else {
-						req.path = sdata[s:i]
-					}
-					for i, s = i+1, i+1; i < dlen; i++ {
-						if sdata[i] == '\n' && sdata[i-1] == '\r' {
-							req.proto = sdata[s:i]
-							i, s = i+1, i+1
-							break
-						}
-					}
-					break
-				}
-			}
-			break
-		}
-	}
-	if req.proto == "" {
-		return data, fmt.Errorf("malformed request")
-	}
-	head = sdata[:s]
-	for ; i < dlen; i++ {
-		if i > 1 && sdata[i] == '\n' && sdata[i-1] == '\r' {
-			line := sdata[s : i-1]
-			s = i + 1
-			if line == "" {
-				req.head = sdata[len(head)+2 : i+1]
-				i++
-				return data[i:], nil
-			}
-		}
-	}
-	// not enough data
-	return data, nil
-}