aboutsummaryrefslogtreecommitdiff
path: root/fasthttpadaptor
diff options
context:
space:
mode:
authorGravatar Aliaksandr Valialkin <valyala@gmail.com> 2016-01-06 17:11:43 +0200
committerGravatar Aliaksandr Valialkin <valyala@gmail.com> 2016-01-06 17:11:43 +0200
commitd53b8f0f3cf206680db72f7cd55eba6a8e637c80 (patch)
tree01e1d0ea4a1e9314a68f825871181ff989c1fed5 /fasthttpadaptor
parentDo not send response body (and content-length) for 1xx, 204 and 304 responses (diff)
downloadfasthttp-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.go127
-rw-r--r--fasthttpadaptor/adaptor_test.go116
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)
+ }
+}