package nbhttp import ( "errors" "fmt" "io" "math/rand" "net/http" "testing" "time" ) func TestServerParserContentLength(t *testing.T) { data := []byte("POST /echo HTTP/1.1\r\nEmpty:\r\n Empty2:\r\nHost : localhost:8080 \r\nConnection: close \r\n Accept-Encoding : gzip , deflate ,br \r\n\r\n") err := testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("POST /echo HTTP/1.1\r\nHost: localhost:8080\r\n Connection: close \r\nContent-Length : 0\r\nAccept-Encoding : gzip \r\n\r\n") err = testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("POST /echo HTTP/1.1\r\nHost: localhost:8080\r\n Connection: close \r\nContent-Length : 5\r\nAccept-Encoding : gzip \r\n\r\nhello") err = testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } } func TestServerParserChunks(t *testing.T) { data := []byte("POST / HTTP/1.1\r\nHost: localhost:1235\r\n User-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n4 \r\nbody\r\n0 \r\n\r\n") err := testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("POST / HTTP/1.1\r\nHost: localhost:1235\r\n User-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n0\r\n\r\n") err = testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } } func TestServerParserTrailer(t *testing.T) { data := []byte("POST / HTTP/1.1\r\nHost : localhost:1235\r\n User-Agent : Go-http-client/1.1 \r\nTransfer-Encoding: chunked\r\nTrailer: Md5,Size\r\nAccept-Encoding: gzip \r\n\r\n4\r\nbody\r\n0\r\n Md5 : 841a2d689ad86bd1611447453c22c6fc \r\n Size : 4 \r\n\r\n") err := testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("POST / HTTP/1.1\r\nHost: localhost:1235\r\nUser-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nTrailer: Md5,Size\r\nAccept-Encoding: gzip \r\n\r\n0\r\nMd5 : 841a2d689ad86bd1611447453c22c6fc \r\n Size: 4 \r\n\r\n") err = testParser(t, false, data) if err != nil { t.Fatalf("test failed: %v", err) } } func TestClientParserContentLength(t *testing.T) { data := []byte("HTTP/1.1 200 OK\r\nHost: localhost:8080\r\n Connection: close \r\n Accept-Encoding : gzip \r\n\r\n") err := testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("HTTP/1.1 200 OK\r\nHost: localhost:8080\r\n Connection: close \r\n Content-Length : 0\r\nAccept-Encoding : gzip \r\n\r\n") err = testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("HTTP/1.1 200 OK\r\nHost: localhost:8080\r\n Connection: close \r\n Content-Length : 5\r\nAccept-Encoding : gzip \r\n\r\nhello") err = testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } } func TestClientParserChunks(t *testing.T) { data := []byte("HTTP/1.1 200 OK\r\nHost: localhost:1235\r\n User-Agent: Go-http-client/1.1\r\n Transfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n4\r\nbody\r\n0\r\n\r\n") err := testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("HTTP/1.1 200 OK\r\nHost: localhost:1235\r\nUser-Agent: Go-http-client/1.1\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n0\r\n\r\n") err = testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } } func TestClientParserTrailer(t *testing.T) { data := []byte("HTTP/1.1 200 OK\r\nHost: localhost:1235\r\n User-Agent: Go-http-client/1.1\r\n Transfer-Encoding: chunked\r\nTrailer: Md5,Size\r\nAccept-Encoding: gzip\r\n\r\n4\r\nbody\r\n0\r\nMd5: 841a2d689ad86bd1611447453c22c6fc\r\nSize: 4\r\n\r\n") err := testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } data = []byte("HTTP/1.1 200 OK\r\nHost: localhost:1235\r\nUser-Agent: Go-http-client/1.1\r\nTransfer-Encoding : chunked\r\nTrailer: Md5,Size\r\nAccept-Encoding: gzip\r\n\r\n0\r\nMd5: 841a2d689ad86bd1611447453c22c6fc\r\nSize: 4\r\n\r\n") err = testParser(t, true, data) if err != nil { t.Fatalf("test failed: %v", err) } } func testParser(t *testing.T, isClient bool, data []byte) error { parser := newParser(isClient) err := parser.Read(data) if err != nil { t.Fatal(err) } for i := 0; i < len(data)-1; i++ { err = parser.Read(append([]byte{}, data[i:i+1]...)) if err != nil { t.Fatal(err) } } err = parser.Read(append([]byte{}, data[len(data)-1:]...)) if err != nil { t.Fatal(err) } nRequest := 0 data = append(data, data...) maxReadSize := 1024 * 1024 * 4 mux := &http.ServeMux{} mux.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) { nRequest++ }) processor := NewServerProcessor(nil, mux, DefaultKeepaliveTime, false) if isClient { processor = NewClientProcessor(nil, func(*http.Response, error) { nRequest++ }) } svr := &Server{ // Malloc: mempool.Malloc, // Realloc: mempool.Realloc, // Free: mempool.Free, } parser = NewParser(processor, isClient, maxReadSize, nil) parser.Engine = svr.Engine tBegin := time.Now() loop := 10000 for i := 0; i < loop; i++ { tmp := data reads := [][]byte{} for len(tmp) > 0 { nRead := rand.Intn(len(tmp)) + 1 readBuf := append([]byte{}, tmp[:nRead]...) reads = append(reads, readBuf) tmp = tmp[nRead:] err = parser.Read(readBuf) if err != nil { t.Fatalf("nRead: %v, numOne: %v, reads: %v, error: %v", len(data)-len(tmp), len(data), reads, err) } } if nRequest != (i+1)*2 { return fmt.Errorf("nRequest: %v, %v", i, nRequest) } } tUsed := time.Since(tBegin) fmt.Printf("%v loops, %v s used, %v ns/op, %v req/s\n", loop, tUsed.Seconds(), tUsed.Nanoseconds()/int64(loop), float64(loop)/tUsed.Seconds()) return nil } func newParser(isClient bool) *Parser { svr := &Server{ // Malloc: mempool.Malloc, // Realloc: mempool.Realloc, // Free: mempool.Free, } maxReadSize := 1024 * 1024 * 4 if isClient { processor := NewClientProcessor(nil, func(*http.Response, error) {}) parser := NewParser(processor, isClient, maxReadSize, nil) parser.Engine = svr.Engine return parser } mux := &http.ServeMux{} mux.HandleFunc("/", pirntMessage) processor := NewServerProcessor(nil, mux, DefaultKeepaliveTime, false) parser := NewParser(processor, isClient, maxReadSize, nil) parser.Engine = svr.Engine return parser } func pirntMessage(w http.ResponseWriter, request *http.Request) { fmt.Printf("----------------------------------------------------------------\n") fmt.Println("OnRequest") fmt.Println("Method:", request.Method) fmt.Println("Path:", request.URL.Path) fmt.Println("Proto:", request.Proto) fmt.Println("Host:", request.URL.Host) fmt.Println("Rawpath:", request.URL.RawPath) fmt.Println("Content-Length:", request.ContentLength) for k, v := range request.Header { fmt.Printf("Header: [\"%v\": \"%v\"]\n", k, v) } for k, v := range request.Trailer { fmt.Printf("Trailer: [\"%v\": \"%v\"]\n", k, v) } body := request.Body if body != nil { nread := 0 buffer := make([]byte, 1024) for { n, err := body.Read(buffer) if n > 0 { nread += n } if errors.Is(err, io.EOF) { break } } fmt.Println("body:", string(buffer[:nread])) } else { fmt.Println("body: null") } } var benchData = []byte("POST /joyent/http-parser HTTP/1.1\r\n" + "Host: github.com\r\n" + "DNT: 1\r\n" + "Accept-Encoding: gzip, deflate, sdch\r\n" + "Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" + "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/39.0.2171.65 Safari/537.36\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9," + "image/webp,*/*;q=0.8\r\n" + "Referer: https://github.com/joyent/http-parser\r\n" + "Connection: keep-alive\r\n" + "Transfer-Encoding: chunked\r\n" + "Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n\r\n") func BenchmarkServerProcessor(b *testing.B) { maxReadSize := 1024 * 1024 * 4 isClient := false mux := &http.ServeMux{} mux.HandleFunc("/", func(http.ResponseWriter, *http.Request) {}) processor := NewServerProcessor(nil, mux, DefaultKeepaliveTime, false) parser := NewParser(processor, isClient, maxReadSize, nil) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { if err := parser.Read(benchData); err != nil { b.Fatal(err) } } } func BenchmarkEmpryProcessor(b *testing.B) { maxReadSize := 1024 * 1024 * 4 isClient := false // processor := NewEmptyProcessor() parser := NewParser(nil, isClient, maxReadSize, nil) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { if err := parser.Read(benchData); err != nil { b.Fatal(err) } } }