func convertResponse(stdreq *http.Request, fastresp *fasthttp.Response) *http.Response { status := fastresp.Header.StatusCode() body := fastresp.Body() stdresp := &http.Response{ Request: stdreq, StatusCode: status, Status: http.StatusText(status), } fastresp.Header.VisitAll(func(k, v []byte) { sk := string(k) sv := string(v) if stdresp.Header == nil { stdresp.Header = make(http.Header) } stdresp.Header.Add(sk, sv) }) if fastresp.Header.ContentLength() == -1 { stdresp.TransferEncoding = []string{"chunked"} } if body != nil { stdresp.Body = ioutil.NopCloser(bytes.NewReader(body)) } else { stdresp.Body = ioutil.NopCloser(bytes.NewReader(nil)) } return stdresp }
func (c *Client) run() { var resp fasthttp.Response r := new(fasthttp.Request) c.request.CopyTo(r) for range c.Jobsch { s := time.Now() err := c.Do(r, &resp) if err != nil { if err == fasthttp.ErrTimeout { timeouts.Inc() } errors.Inc() c.withErrorMessage(err.Error()).Inc() } sc := resp.StatusCode() if c.successStatusCode == sc { requestSuccess.Inc() } c.withStatusCode(sc).Inc() requestDuration.Observe(float64(time.Since(s).Seconds())) requestSum.Inc() } }
func runRequests(b *testing.B, pb *testing.PB, c *fasthttp.HostClient) { var req fasthttp.Request req.SetRequestURI("http://foo.bar/baz") var resp fasthttp.Response for pb.Next() { if err := c.Do(&req, &resp); err != nil { b.Fatalf("unexpected error: %s", err) } if resp.StatusCode() != fasthttp.StatusOK { b.Fatalf("unexpected status code: %d. Expecting %d", resp.StatusCode(), fasthttp.StatusOK) } } }
func ExampleLBClient() { // Requests will be spread among these servers. servers := []string{ "google.com:80", "foobar.com:8080", "127.0.0.1:123", } // Prepare clients for each server var lbc fasthttp.LBClient for _, addr := range servers { c := &fasthttp.HostClient{ Addr: addr, } lbc.Clients = append(lbc.Clients, c) } // Send requests to load-balanced servers var req fasthttp.Request var resp fasthttp.Response for i := 0; i < 10; i++ { url := fmt.Sprintf("http://abcedfg/foo/bar/%d", i) req.SetRequestURI(url) if err := lbc.Do(&req, &resp); err != nil { log.Fatalf("Error when sending request: %s", err) } if resp.StatusCode() != fasthttp.StatusOK { log.Fatalf("unexpected status code: %d. Expecting %d", resp.StatusCode(), fasthttp.StatusOK) } useResponseBody(resp.Body()) } }
func fetchFromUpstream(h *fasthttp.RequestHeader, key []byte) *ybc.Item { upstreamUrl := fmt.Sprintf("%s://%s%s", *upstreamProtocol, *upstreamHost, h.RequestURI()) var req fasthttp.Request req.SetRequestURI(upstreamUrl) var resp fasthttp.Response err := upstreamClient.Do(&req, &resp) if err != nil { logRequestError(h, "Cannot make request for [%s]: [%s]", key, err) return nil } if resp.StatusCode() != fasthttp.StatusOK { logRequestError(h, "Unexpected status code=%d for the response [%s]", resp.StatusCode(), key) return nil } contentType := string(resp.Header.ContentType()) if contentType == "" { contentType = "application/octet-stream" } body := resp.Body() contentLength := len(body) itemSize := contentLength + len(contentType) + 1 txn, err := cache.NewSetTxn(key, itemSize, ybc.MaxTtl) if err != nil { logRequestError(h, "Cannot start set txn for response [%s], itemSize=%d: [%s]", key, itemSize, err) return nil } if err = storeContentType(h, txn, contentType); err != nil { txn.Rollback() return nil } n, err := txn.Write(body) if err != nil { logRequestError(h, "Cannot read response [%s] body with size=%d to cache: [%s]", key, contentLength, err) txn.Rollback() return nil } if n != contentLength { logRequestError(h, "Unexpected number of bytes copied=%d from response [%s] to cache. Expected %d", n, key, contentLength) txn.Rollback() return nil } item, err := txn.CommitItem() if err != nil { logRequestError(h, "Cannot commit set txn for response [%s], size=%d: [%s]", key, contentLength, err) return nil } atomic.AddInt64(&stats.BytesReadFromUpstream, int64(n)) return item }
func TestRouterServeFiles(t *testing.T) { router := New() recv := catchPanic(func() { router.ServeFiles("/noFilepath", os.TempDir()) }) if recv == nil { t.Fatal("registering path not ending with '*filepath' did not panic") } body := []byte("fake ico") ioutil.WriteFile(os.TempDir()+"/favicon.ico", body, 0644) router.ServeFiles("/*filepath", os.TempDir()) s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} ch := make(chan error) rw.r.WriteString(string("GET /favicon.ico HTTP/1.1\r\n\r\n")) go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(500 * time.Millisecond): t.Fatalf("timeout") } br := bufio.NewReader(&rw.w) var resp fasthttp.Response if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != 200 { t.Fatalf("Unexpected status code %d. Expected %d", resp.Header.StatusCode(), 423) } if !bytes.Equal(resp.Body(), body) { t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), string(body)) } }
func TestRouterNotFound(t *testing.T) { handlerFunc := func(_ *fasthttp.RequestCtx, _ Params) {} router := New() router.GET("/path", handlerFunc) router.GET("/dir/", handlerFunc) router.GET("/", handlerFunc) testRoutes := []struct { route string code int }{ {"/path/", 301}, // TSR -/ {"/dir", 301}, // TSR +/ {"/", 200}, // TSR +/ {"/PATH", 301}, // Fixed Case {"/DIR", 301}, // Fixed Case {"/PATH/", 301}, // Fixed Case -/ {"/DIR/", 301}, // Fixed Case +/ {"/../path", 200}, // CleanPath {"/nope", 404}, // NotFound } s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} br := bufio.NewReader(&rw.w) var resp fasthttp.Response ch := make(chan error) for _, tr := range testRoutes { rw.r.WriteString(fmt.Sprintf("GET %s HTTP/1.1\r\n\r\n", tr.route)) go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == tr.code) { t.Errorf("NotFound handling route %s failed: Code=%d want=%d", tr.route, resp.Header.StatusCode(), tr.code) } } // Test custom not found handler var notFound bool router.NotFound = fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(404) notFound = true }) rw.r.WriteString("GET /nope HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 404 && notFound == true) { t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", resp.Header.StatusCode(), string(resp.Header.Peek("Location"))) } // Test other method than GET (want 307 instead of 301) router.PATCH("/path", handlerFunc) rw.r.WriteString("PATCH /path/ HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 307) { t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", resp.Header.StatusCode(), string(resp.Header.Peek("Location"))) } // Test special case where no node for the prefix "/" exists router = New() router.GET("/a", handlerFunc) s.Handler = router.Handler rw.r.WriteString("GET / HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == 404) { t.Errorf("NotFound handling route / failed: Code=%d", resp.Header.StatusCode()) } }
func TestRouterNotAllowed(t *testing.T) { handlerFunc := func(_ *fasthttp.RequestCtx, _ Params) {} router := New() router.POST("/path", handlerFunc) // Test not allowed s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} ch := make(chan error) rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } br := bufio.NewReader(&rw.w) var resp fasthttp.Response if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusMethodNotAllowed) { t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } // add another method router.DELETE("/path", handlerFunc) router.OPTIONS("/path", handlerFunc) // must be ignored // test again rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusMethodNotAllowed) { t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } responseText := "custom method" router.MethodNotAllowed = fasthttp.RequestHandler(func(ctx *fasthttp.RequestCtx) { ctx.SetStatusCode(fasthttp.StatusTeapot) ctx.Write([]byte(responseText)) }) rw.r.WriteString("GET /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !bytes.Equal(resp.Body(), []byte(responseText)) { t.Errorf("unexpected response got %q want %q", string(resp.Body()), responseText) } if resp.Header.StatusCode() != fasthttp.StatusTeapot { t.Errorf("unexpected response code %d want %d", resp.Header.StatusCode(), fasthttp.StatusTeapot) } if allow := string(resp.Header.Peek("Allow")); allow != "POST, DELETE, OPTIONS" && allow != "DELETE, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } }
func TestRouterOPTIONS(t *testing.T) { // TODO: because fasthttp is not support OPTIONS method now, // these test cases will be used in the future. handlerFunc := func(_ *fasthttp.RequestCtx, _ Params) {} router := New() router.POST("/path", handlerFunc) // test not allowed // * (server) s := &fasthttp.Server{ Handler: router.Handler, } rw := &readWriter{} ch := make(chan error) rw.r.WriteString("OPTIONS * HTTP/1.1\r\nHost:\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } br := bufio.NewReader(&rw.w) var resp fasthttp.Response if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } // path rw.r.WriteString("OPTIONS /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } rw.r.WriteString("OPTIONS /doesnotexist HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusNotFound) { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } // add another method router.GET("/path", handlerFunc) // test again // * (server) rw.r.WriteString("OPTIONS * HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } // path rw.r.WriteString("OPTIONS /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } // custom handler var custom bool router.OPTIONS("/path", func(_ *fasthttp.RequestCtx, _ Params) { custom = true }) // test again // * (server) rw.r.WriteString("OPTIONS * HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } else if allow := string(resp.Header.Peek("Allow")); allow != "POST, GET, OPTIONS" && allow != "GET, POST, OPTIONS" { t.Error("unexpected Allow header value: " + allow) } if custom { t.Error("custom handler called on *") } // path rw.r.WriteString("OPTIONS /path HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if resp.Header.StatusCode() != fasthttp.StatusOK { t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", resp.Header.StatusCode(), resp.Header.String()) } if !custom { t.Error("custom handler not called") } }
func TestRouterChaining(t *testing.T) { router1 := New() router2 := New() router1.NotFound = router2.Handler fooHit := false router1.POST("/foo", func(ctx *fasthttp.RequestCtx, _ Params) { fooHit = true ctx.SetStatusCode(fasthttp.StatusOK) }) barHit := false router2.POST("/bar", func(ctx *fasthttp.RequestCtx, _ Params) { barHit = true ctx.SetStatusCode(fasthttp.StatusOK) }) s := &fasthttp.Server{ Handler: router1.Handler, } rw := &readWriter{} ch := make(chan error) rw.r.WriteString("POST /foo HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } br := bufio.NewReader(&rw.w) var resp fasthttp.Response if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusOK && fooHit) { t.Errorf("Regular routing failed with router chaining.") t.FailNow() } rw.r.WriteString("POST /bar HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) { t.Errorf("Chained routing failed with router chaining.") t.FailNow() } rw.r.WriteString("POST /qax HTTP/1.1\r\n\r\n") go func() { ch <- s.ServeConn(rw) }() select { case err := <-ch: if err != nil { t.Fatalf("return error %s", err) } case <-time.After(100 * time.Millisecond): t.Fatalf("timeout") } if err := resp.Read(br); err != nil { t.Fatalf("Unexpected error when reading response: %s", err) } if !(resp.Header.StatusCode() == fasthttp.StatusNotFound) { t.Errorf("NotFound behavior failed with router chaining.") t.FailNow() } }