diff options
author | Aliaksandr Valialkin <valyala@gmail.com> | 2016-01-06 17:11:43 +0200 |
---|---|---|
committer | Aliaksandr Valialkin <valyala@gmail.com> | 2016-01-06 17:11:43 +0200 |
commit | d53b8f0f3cf206680db72f7cd55eba6a8e637c80 (patch) | |
tree | 01e1d0ea4a1e9314a68f825871181ff989c1fed5 /fasthttpadaptor | |
parent | Do not send response body (and content-length) for 1xx, 204 and 304 responses (diff) | |
download | fasthttp-d53b8f0f3cf206680db72f7cd55eba6a8e637c80.tar.gz fasthttp-d53b8f0f3cf206680db72f7cd55eba6a8e637c80.tar.bz2 fasthttp-d53b8f0f3cf206680db72f7cd55eba6a8e637c80.zip |
Added net/http -> fasthttp converter for quick and easy switching to fasthttp
Diffstat (limited to 'fasthttpadaptor')
-rw-r--r-- | fasthttpadaptor/adaptor.go | 127 | ||||
-rw-r--r-- | fasthttpadaptor/adaptor_test.go | 116 |
2 files changed, 243 insertions, 0 deletions
diff --git a/fasthttpadaptor/adaptor.go b/fasthttpadaptor/adaptor.go new file mode 100644 index 0000000..2070dfb --- /dev/null +++ b/fasthttpadaptor/adaptor.go @@ -0,0 +1,127 @@ +// Package fasthttpadaptor provides helper functions for converting net/http +// request handlers to fasthttp request handlers. +package fasthttpadaptor + +import ( + "io" + "net/http" + + "github.com/valyala/fasthttp" +) + +// NewFastHTTPHandlerFunc wraps net/http handler func to fasthttp +// request handler, so it can be passed to fasthttp server. +// +// While this function may be used for easy switching from net/http to fasthttp, +// it has the following drawbacks comparing to using manually written fasthttp +// request handler: +// +// * A lot of useful functionality provided by fasthttp is missing +// from net/http handler. +// * net/http -> fasthttp handler conversion has some overhead, +// so the returned handler will be always slower than manually written +// fasthttp handler. +// +// So it is advisable using this function only for quick net/http -> fasthttp +// switching. Then manually convert net/http handlers to fasthttp handlers +// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp . +func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler { + return NewFastHTTPHandler(h) +} + +// NewFastHTTPHandler wraps net/http handler to fasthttp request handler, +// so it can be passed to fasthttp server. +// +// While this function may be used for easy switching from net/http to fasthttp, +// it has the following drawbacks comparing to using manually written fasthttp +// request handler: +// +// * A lot of useful functionality provided by fasthttp is missing +// from net/http handler. +// * net/http -> fasthttp handler conversion has some overhead, +// so the returned handler will be always slower than manually written +// fasthttp handler. +// +// So it is advisable using this function only for quick net/http -> fasthttp +// switching. Then manually convert net/http handlers to fasthttp handlers +// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp . +func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler { + return func(ctx *fasthttp.RequestCtx) { + var r http.Request + + body := ctx.PostBody() + r.Method = string(ctx.Method()) + r.Proto = "HTTP/1.1" + r.ProtoMajor = 1 + r.ProtoMinor = 1 + r.RequestURI = string(ctx.RequestURI()) + r.ContentLength = int64(len(body)) + r.Host = string(ctx.Host()) + r.RemoteAddr = ctx.RemoteAddr().String() + + hdr := make(http.Header) + ctx.Request.Header.VisitAll(func(k, v []byte) { + hdr.Set(string(k), string(v)) + }) + r.Header = hdr + r.Body = &netHTTPBody{body} + + var w netHTTPResponseWriter + h.ServeHTTP(&w, &r) + + ctx.SetStatusCode(w.StatusCode()) + for k, vv := range w.Header() { + for _, v := range vv { + ctx.Response.Header.Set(k, v) + } + } + ctx.Write(w.body) + } +} + +type netHTTPBody struct { + b []byte +} + +func (r *netHTTPBody) Read(p []byte) (int, error) { + if len(r.b) == 0 { + return 0, io.EOF + } + n := copy(p, r.b) + r.b = r.b[n:] + return n, nil +} + +func (r *netHTTPBody) Close() error { + r.b = r.b[:0] + return nil +} + +type netHTTPResponseWriter struct { + statusCode int + h http.Header + body []byte +} + +func (w *netHTTPResponseWriter) StatusCode() int { + if w.statusCode == 0 { + return http.StatusOK + } + return w.statusCode +} + +func (w *netHTTPResponseWriter) Header() http.Header { + if w.h == nil { + w.h = make(http.Header) + } + return w.h +} + +func (w *netHTTPResponseWriter) WriteHeader(statusCode int) { + w.statusCode = statusCode +} + +func (w *netHTTPResponseWriter) Write(p []byte) (int, error) { + w.body = append(w.body, p...) + return len(p), nil +} diff --git a/fasthttpadaptor/adaptor_test.go b/fasthttpadaptor/adaptor_test.go new file mode 100644 index 0000000..13a7ee5 --- /dev/null +++ b/fasthttpadaptor/adaptor_test.go @@ -0,0 +1,116 @@ +package fasthttpadaptor + +import ( + "fmt" + "io/ioutil" + "net" + "net/http" + "testing" + + "github.com/valyala/fasthttp" +) + +func TestNewFastHTTPHandler(t *testing.T) { + expectedMethod := "POST" + expectedProto := "HTTP/1.1" + expectedProtoMajor := 1 + expectedProtoMinor := 1 + expectedRequestURI := "/foo/bar?baz=123" + expectedBody := "body 123 foo bar baz" + expectedContentLength := len(expectedBody) + expectedHost := "foobar.com" + expectedRemoteAddr := "1.2.3.4:6789" + expectedHeader := map[string]string{ + "Foo-Bar": "baz", + "Abc": "defg", + "XXX-Remote-Addr": "123.43.4543.345", + } + + callsCount := 0 + nethttpH := func(w http.ResponseWriter, r *http.Request) { + callsCount++ + if r.Method != expectedMethod { + t.Fatalf("unexpected method %q. Expecting %q", r.Method, expectedMethod) + } + if r.Proto != expectedProto { + t.Fatalf("unexpected proto %q. Expecting %q", r.Proto, expectedProto) + } + if r.ProtoMajor != expectedProtoMajor { + t.Fatalf("unexpected protoMajor %d. Expecting %d", r.ProtoMajor, expectedProtoMajor) + } + if r.ProtoMinor != expectedProtoMinor { + t.Fatalf("unexpected protoMinor %d. Expecting %d", r.ProtoMinor, expectedProtoMinor) + } + if r.RequestURI != expectedRequestURI { + t.Fatalf("unexpected requestURI %q. Expecting %q", r.RequestURI, expectedRequestURI) + } + if r.ContentLength != int64(expectedContentLength) { + t.Fatalf("unexpected contentLength %d. Expecting %d", r.ContentLength, expectedContentLength) + } + if r.Host != expectedHost { + t.Fatalf("unexpected host %q. Expecting %q", r.Host, expectedHost) + } + if r.RemoteAddr != expectedRemoteAddr { + t.Fatalf("unexpected remoteAddr %q. Expecting %q", r.RemoteAddr, expectedRemoteAddr) + } + body, err := ioutil.ReadAll(r.Body) + r.Body.Close() + if err != nil { + t.Fatalf("unexpected error when reading request body: %s", err) + } + if string(body) != expectedBody { + t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) + } + + for k, expectedV := range expectedHeader { + v := r.Header.Get(k) + if v != expectedV { + t.Fatalf("unexpected header value %q for key %q. Expecting %q", v, k, expectedV) + } + } + + w.Header().Set("Header1", "value1") + w.Header().Set("Header2", "value2") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "request body is %q", body) + } + fasthttpH := NewFastHTTPHandler(http.HandlerFunc(nethttpH)) + + var ctx fasthttp.RequestCtx + var req fasthttp.Request + + req.Header.SetMethod(expectedMethod) + req.SetRequestURI(expectedRequestURI) + req.Header.SetHost(expectedHost) + req.BodyWriter().Write([]byte(expectedBody)) + for k, v := range expectedHeader { + req.Header.Set(k, v) + } + + remoteAddr, err := net.ResolveTCPAddr("tcp", expectedRemoteAddr) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + ctx.Init(&req, remoteAddr, nil) + + fasthttpH(&ctx) + + if callsCount != 1 { + t.Fatalf("unexpected callsCount: %d. Expecting 1", callsCount) + } + + resp := &ctx.Response + if resp.StatusCode() != fasthttp.StatusBadRequest { + t.Fatalf("unexpected statusCode: %d. Expecting %d", resp.StatusCode(), fasthttp.StatusBadRequest) + } + if string(resp.Header.Peek("Header1")) != "value1" { + t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header1"), "value1") + } + if string(resp.Header.Peek("Header2")) != "value2" { + t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header2"), "value2") + } + expectedResponseBody := fmt.Sprintf("request body is %q", expectedBody) + if string(resp.Body()) != expectedResponseBody { + t.Fatalf("unexpected response body %q. Expecting %q", resp.Body(), expectedResponseBody) + } +} |