From fad2cc6152087466bc514bc7aaa50a56f3485f88 Mon Sep 17 00:00:00 2001 From: kiyon Date: Sat, 11 Jul 2020 12:04:24 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=8E=A8=20change=20ppid=20to=20pid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 0c03d8ec..3bfdbc75 100644 --- a/app.go +++ b/app.go @@ -669,7 +669,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { osName = utils.ToUpper(runtime.GOOS) memTotal = utils.ByteSize(utils.MemoryTotal()) cpuCores = runtime.NumCPU() - ppid = os.Getppid() + pid = os.Getpid() ) if host == "" { host = "0.0.0.0" @@ -696,7 +696,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { cCyan, cBlack, fmt.Sprintf(" HOST %s\tOS %s", cyan(host), cyan(osName)), cCyan, cBlack, fmt.Sprintf(" PORT %s\tCORES %s", cyan(port), cyan(cpuCores)), cCyan, cBlack, fmt.Sprintf(" TLS %s\tMEM %s", cyan(tlsStr), cyan(memTotal)), - cBlack, cyan(Version), fmt.Sprintf(" ROUTES %s\t\t\t PPID %s%s%s\n", cyan(routesLen), cyan(ppid), pids, cReset), + cBlack, cyan(Version), fmt.Sprintf(" ROUTES %s\t\t\t PID %s%s%s\n", cyan(routesLen), cyan(pid), pids, cReset), ) // Write to io.write _ = out.Flush() From e070968d2d0fd21430cc50245a2d6a76454c543d Mon Sep 17 00:00:00 2001 From: renanbastos93 Date: Tue, 14 Jul 2020 00:05:48 -0300 Subject: [PATCH 2/9] :rocket: refactor: change status code number to constant --- router.go | 8 ++++---- utils.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/router.go b/router.go index dcd72aa1..09aad11e 100644 --- a/router.go +++ b/router.go @@ -114,7 +114,7 @@ func (app *App) next(ctx *Ctx) bool { return true } // If c.Next() does not match, return 404 - ctx.SendStatus(404) + ctx.SendStatus(StatusNotFound) ctx.SendString("Cannot " + ctx.method + " " + ctx.pathOriginal) // Scan stack for other methods @@ -279,7 +279,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) *Route { return path }, PathNotFound: func(ctx *fasthttp.RequestCtx) { - ctx.Response.SetStatusCode(404) + ctx.Response.SetStatusCode(StatusNotFound) }, } // Set config if provided @@ -297,12 +297,12 @@ func (app *App) registerStatic(prefix, root string, config ...Static) *Route { fileHandler(c.Fasthttp) // Return request if found and not forbidden status := c.Fasthttp.Response.StatusCode() - if status != 404 && status != 403 { + if status != StatusNotFound && status != StatusForbidden { return } // Reset response to default c.Fasthttp.SetContentType("") // Issue #420 - c.Fasthttp.Response.SetStatusCode(200) + c.Fasthttp.Response.SetStatusCode(StatusOK) c.Fasthttp.Response.SetBodyString("") // Next middleware c.Next() diff --git a/utils.go b/utils.go index f89c4c5c..0cbe54be 100644 --- a/utils.go +++ b/utils.go @@ -76,7 +76,7 @@ func defaultString(value string, defaultValue []string) string { // Generate and set ETag header to response func setETag(ctx *Ctx, weak bool) { // Don't generate ETags for invalid responses - if ctx.Fasthttp.Response.StatusCode() != 200 { + if ctx.Fasthttp.Response.StatusCode() != StatusOK { return } body := ctx.Fasthttp.Response.Body() @@ -101,7 +101,7 @@ func setETag(ctx *Ctx, weak bool) { // Check if server's ETag is weak if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] { // W/1 == 1 || W/1 == W/1 - ctx.SendStatus(304) + ctx.SendStatus(StatusNotModified) ctx.Fasthttp.ResetBody() return } @@ -111,7 +111,7 @@ func setETag(ctx *Ctx, weak bool) { } if strings.Contains(clientEtag, etag) { // 1 == 1 - ctx.SendStatus(304) + ctx.SendStatus(StatusNotModified) ctx.Fasthttp.ResetBody() return } From e86c123e6f276aada862344c7a81f228e956229f Mon Sep 17 00:00:00 2001 From: kiyon Date: Tue, 14 Jul 2020 15:21:47 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=8D=A4=20improve=20request=5Fid=20tes?= =?UTF-8?q?t=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware/request_id_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/middleware/request_id_test.go b/middleware/request_id_test.go index f2b580d8..a50b7897 100644 --- a/middleware/request_id_test.go +++ b/middleware/request_id_test.go @@ -82,6 +82,11 @@ func Test_Middleware_RequestID_Options_And_WithConfig(t *testing.T) { header: RequestIDConfigDefault.Header, handler: RequestID(func() string { return "fake-id" }), }, + { + idLen: UUIDLen, + header: RequestIDConfigDefault.Header, + handler: RequestID(RequestIDConfig{}), + }, } for _, testCase := range testCases { From 80e222093df013e7a16d5cfaed0241cdbc358a19 Mon Sep 17 00:00:00 2001 From: kiyon Date: Tue, 14 Jul 2020 15:23:41 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=8D=A4=20improve=20app=20test=20cover?= =?UTF-8?q?age?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 1 + app_test.go | 37 +++++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app.go b/app.go index 4bb43fd3..a862f997 100644 --- a/app.go +++ b/app.go @@ -419,6 +419,7 @@ func (app *App) Routes() []*Route { for i := range routes { if routes[i].Method == methodUse && routes[i].Name == app.stack[m][r].Name { duplicate = true + break } } if !duplicate { diff --git a/app_test.go b/app_test.go index c6ad4ba0..60a4b626 100644 --- a/app_test.go +++ b/app_test.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "io/ioutil" - "net" "net/http/httptest" "regexp" "strings" @@ -17,6 +16,7 @@ import ( utils "github.com/gofiber/utils" fasthttp "github.com/valyala/fasthttp" + fasthttputil "github.com/valyala/fasthttp/fasthttputil" ) func testStatus200(t *testing.T, app *App, url string, method string) { @@ -97,10 +97,11 @@ func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) func Test_App_Routes(t *testing.T) { app := New() h := func(c *Ctx) {} + app.Use("/", h) app.Get("/Get", h) app.Head("/Head", h) app.Post("/post", h) - utils.AssertEqual(t, 3, len(app.Routes())) + utils.AssertEqual(t, 4, len(app.Routes())) } func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { @@ -360,18 +361,28 @@ func Test_App_Static_Index_Default(t *testing.T) { app := New() app.Static("/prefix", "./.github/workflows") - app.Static("/", "./.github") + app.Static("", "./.github/") + app.Static("test", "", Static{Index: "index.html"}) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get("Content-Length") == "") - utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get("Content-Type")) + utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") + utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := ioutil.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) + resp, err = app.Test(httptest.NewRequest("GET", "/not-found", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") + utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) + + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "Cannot GET /not-found", string(body)) } // go test -run Test_App_Static_Index @@ -649,14 +660,14 @@ func Test_App_Listen(t *testing.T) { utils.AssertEqual(t, nil, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(4003)) + utils.AssertEqual(t, nil, app.Listen("127.0.0.1:")) go func() { time.Sleep(1000 * time.Millisecond) utils.AssertEqual(t, nil, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen("4010")) + utils.AssertEqual(t, nil, app.Listen("127.0.0.1:")) } // go test -run Test_App_Listener @@ -665,14 +676,13 @@ func Test_App_Listener(t *testing.T) { DisableStartupMessage: true, Prefork: true, }) - ln, err := net.Listen("tcp4", ":4020") - utils.AssertEqual(t, nil, err) go func() { - time.Sleep(1000 * time.Millisecond) + time.Sleep(500 * time.Millisecond) utils.AssertEqual(t, nil, app.Shutdown()) }() + ln := fasthttputil.NewInmemoryListener() utils.AssertEqual(t, nil, app.Listener(ln)) } @@ -699,3 +709,10 @@ func Benchmark_App_ETag_Weak(b *testing.B) { } utils.AssertEqual(b, `W/"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) } + +// go test -run Test_NewError +func Test_NewError(t *testing.T) { + e := NewError(StatusForbidden, "permission denied") + utils.AssertEqual(t, StatusForbidden, e.Code) + utils.AssertEqual(t, "permission denied", e.Message) +} From 3156fcca3db0eb51cb7762685948f95c5adf0b78 Mon Sep 17 00:00:00 2001 From: kiyon Date: Tue, 14 Jul 2020 15:24:24 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=8D=A4=20improve=20ctx=20test=20cover?= =?UTF-8?q?age?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx.go | 36 ++++++++++------ ctx_test.go | 117 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 129 insertions(+), 24 deletions(-) diff --git a/ctx.go b/ctx.go index f68eda34..19eb1f48 100644 --- a/ctx.go +++ b/ctx.go @@ -388,7 +388,7 @@ func (ctx *Ctx) Format(body interface{}) { ctx.Send(body) // Fallback log.Println("Format: error serializing json ", err) } - case "text": + case "txt": ctx.SendString(b) case "xml": raw, err := xml.Marshal(body) @@ -415,7 +415,7 @@ func (ctx *Ctx) FormValue(key string) (value string) { return getString(ctx.Fasthttp.FormValue(key)) } -var cacheControlNoCacheRegexp, _ = regexp.Compile(`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`) +var cacheControlNoCacheRegexp, _ = regexp.Compile(`(?:^|,)\s*?no-cache\s*?(?:,|$)`) // Fresh returns true when the response is still “fresh” in the client's cache, // otherwise false is returned to indicate that the client cache is now stale @@ -436,7 +436,7 @@ func (ctx *Ctx) Fresh() bool { // Always return stale when Cache-Control: no-cache // to support end-to-end reload requests // https://tools.ietf.org/html/rfc2616#section-14.9.4 - var cacheControl = ctx.Get(HeaderCacheControl) + cacheControl := ctx.Get(HeaderCacheControl) if cacheControl != "" && cacheControlNoCacheRegexp.MatchString(cacheControl) { return false } @@ -447,16 +447,15 @@ func (ctx *Ctx) Fresh() bool { if etag == "" { return false } - var etagStal = true + var etagStale = true var matches = parseTokenList(getBytes(noneMatch)) - for i := range matches { - match := matches[i] + for _, match := range matches { if match == etag || match == "W/"+etag || "W/"+match == etag { - etagStal = false + etagStale = false break } } - if etagStal { + if etagStale { return false } @@ -716,19 +715,30 @@ func (ctx *Ctx) Query(key string, defaultValue ...string) string { return defaultString(getString(ctx.Fasthttp.QueryArgs().Peek(key)), defaultValue) } +var ( + ErrRangeMalformed = errors.New("range: malformed range header string") + ErrRangeUnsatisfiable = errors.New("range: unsatisfiable range") +) + // Range returns a struct containing the type and a slice of ranges. func (ctx *Ctx) Range(size int) (rangeData Range, err error) { rangeStr := ctx.Get(HeaderRange) if rangeStr == "" || !strings.Contains(rangeStr, "=") { - return rangeData, fmt.Errorf("range: malformed range header string") + err = ErrRangeMalformed + return } data := strings.Split(rangeStr, "=") + if len(data) != 2 { + err = ErrRangeMalformed + return + } rangeData.Type = data[0] arr := strings.Split(data[1], ",") for i := 0; i < len(arr); i++ { item := strings.Split(arr[i], "-") if len(item) == 1 { - return rangeData, fmt.Errorf("range: malformed range header string") + err = ErrRangeMalformed + return } start, startErr := strconv.Atoi(item[0]) end, endErr := strconv.Atoi(item[1]) @@ -753,9 +763,11 @@ func (ctx *Ctx) Range(size int) (rangeData Range, err error) { }) } if len(rangeData.Ranges) < 1 { - return rangeData, fmt.Errorf("range: unsatisfiable range") + err = ErrRangeUnsatisfiable + return } - return rangeData, nil + + return } // Redirect to the URL derived from the specified path, with specified status. diff --git a/ctx_test.go b/ctx_test.go index 75072114..710193f5 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -356,24 +356,29 @@ func Test_Ctx_Format(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) - ctx.Fasthttp.Request.Header.Set(HeaderAccept, "plain/text") + ctx.Fasthttp.Request.Header.Set(HeaderAccept, MIMETextPlain) ctx.Format([]byte("Hello, World!")) utils.AssertEqual(t, "Hello, World!", string(ctx.Fasthttp.Response.Body())) - ctx.Fasthttp.Request.Header.Set(HeaderAccept, "text/html") + ctx.Fasthttp.Request.Header.Set(HeaderAccept, MIMETextHTML) ctx.Format("Hello, World!") utils.AssertEqual(t, "

Hello, World!

", string(ctx.Fasthttp.Response.Body())) - ctx.Fasthttp.Request.Header.Set(HeaderAccept, "application/json") + ctx.Fasthttp.Request.Header.Set(HeaderAccept, MIMEApplicationJSON) ctx.Format("Hello, World!") utils.AssertEqual(t, `"Hello, World!"`, string(ctx.Fasthttp.Response.Body())) + ctx.Format(complex(1, 1)) + utils.AssertEqual(t, "(1+1i)", string(ctx.Fasthttp.Response.Body())) - ctx.Fasthttp.Request.Header.Set(HeaderAccept, "application/xml") + ctx.Fasthttp.Request.Header.Set(HeaderAccept, MIMEApplicationXML) ctx.Format("Hello, World!") utils.AssertEqual(t, `Hello, World!`, string(ctx.Fasthttp.Response.Body())) + ctx.Format(Map{}) + utils.AssertEqual(t, "map[]", string(ctx.Fasthttp.Response.Body())) + type broken string ctx.Fasthttp.Request.Header.Set(HeaderAccept, "broken/accept") - ctx.Format("Hello, World!") + ctx.Format(broken("Hello, World!")) utils.AssertEqual(t, `Hello, World!`, string(ctx.Fasthttp.Response.Body())) } @@ -506,6 +511,31 @@ func Test_Ctx_Fresh(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, "*") + ctx.Fasthttp.Request.Header.Set(HeaderCacheControl, "no-cache") + utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, "675af34563dc-tr34") + ctx.Fasthttp.Request.Header.Set(HeaderCacheControl, "public") + utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, "a, b") + ctx.Fasthttp.Response.Header.Set(HeaderETag, "c") + utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Response.Header.Set(HeaderETag, "a") + utils.AssertEqual(t, true, ctx.Fresh()) + + ctx.Fasthttp.Request.Header.Set(HeaderIfModifiedSince, "xxWed, 21 Oct 2015 07:28:00 GMT") + ctx.Fasthttp.Response.Header.Set(HeaderLastModified, "xxWed, 21 Oct 2015 07:28:00 GMT") + utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Response.Header.Set(HeaderLastModified, "Wed, 21 Oct 2015 07:28:00 GMT") + utils.AssertEqual(t, false, ctx.Fresh()) + + ctx.Fasthttp.Request.Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:28:00 GMT") + utils.AssertEqual(t, false, ctx.Fresh()) } // go test -run Test_Ctx_Get @@ -783,12 +813,40 @@ func Test_Ctx_Range(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) - ctx.Fasthttp.Request.Header.Set(HeaderRange, "bytes=500-700") - result, err := ctx.Range(1000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "bytes", result.Type) - utils.AssertEqual(t, 500, result.Ranges[0].Start) - utils.AssertEqual(t, 700, result.Ranges[0].End) + + var ( + result Range + err error + ) + + result, err = ctx.Range(1000) + utils.AssertEqual(t, true, err != nil) + + ctx.Fasthttp.Request.Header.Set(HeaderRange, "bytes=500") + result, err = ctx.Range(1000) + utils.AssertEqual(t, true, err != nil) + + ctx.Fasthttp.Request.Header.Set(HeaderRange, "bytes=500=") + result, err = ctx.Range(1000) + utils.AssertEqual(t, true, err != nil) + + ctx.Fasthttp.Request.Header.Set(HeaderRange, "bytes=500-300") + result, err = ctx.Range(1000) + utils.AssertEqual(t, true, err != nil) + + testRange := func(header string, start, end int) { + ctx.Fasthttp.Request.Header.Set(HeaderRange, header) + result, err = ctx.Range(1000) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "bytes", result.Type) + utils.AssertEqual(t, start, result.Ranges[0].Start) + utils.AssertEqual(t, end, result.Ranges[0].End) + } + + testRange("bytes=a-700", 300, 999) + testRange("bytes=500-b", 500, 999) + testRange("bytes=500-1000", 500, 999) + testRange("bytes=500-700", 500, 700) } // go test -run Test_Ctx_Route @@ -801,6 +859,13 @@ func Test_Ctx_Route(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) + + utils.AssertEqual(t, "/", ctx.Route().Path) + utils.AssertEqual(t, MethodGet, ctx.Route().Method) + utils.AssertEqual(t, 0, len(ctx.Route().Handlers)) } // go test -run Test_Ctx_RouteNormalized @@ -1011,6 +1076,9 @@ func Test_Ctx_JSON(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) + + utils.AssertEqual(t, true, ctx.JSON(complex(1, 1)) != nil) + ctx.JSON(Map{ // map has no order "Name": "Grame", "Age": 20, @@ -1059,7 +1127,17 @@ func Test_Ctx_JSONP(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) - ctx.JSONP(Map{ // map has no order + + utils.AssertEqual(t, true, ctx.JSONP(complex(1, 1)) != nil) + + ctx.JSONP(Map{ + "Name": "Grame", + "Age": 20, + }) + utils.AssertEqual(t, `callback({"Age":20,"Name":"Grame"});`, string(ctx.Fasthttp.Response.Body())) + utils.AssertEqual(t, "application/javascript; charset=utf-8", string(ctx.Fasthttp.Response.Header.Peek("content-type"))) + + ctx.JSONP(Map{ "Name": "Grame", "Age": 20, }, "john") @@ -1097,6 +1175,10 @@ func Test_Ctx_Links(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) + + ctx.Links() + utils.AssertEqual(t, "", string(ctx.Fasthttp.Response.Header.Peek(HeaderLink))) + ctx.Links( "http://api.example.com/users?page=2", "next", "http://api.example.com/users?page=5", "last", @@ -1164,6 +1246,11 @@ func Test_Ctx_Redirect(t *testing.T) { app := New() ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) + + ctx.Redirect("http://default.com") + utils.AssertEqual(t, 302, ctx.Fasthttp.Response.StatusCode()) + utils.AssertEqual(t, "http://default.com", string(ctx.Fasthttp.Response.Header.Peek(HeaderLocation))) + ctx.Redirect("http://example.com", 301) utils.AssertEqual(t, 301, ctx.Fasthttp.Response.StatusCode()) utils.AssertEqual(t, "http://example.com", string(ctx.Fasthttp.Response.Header.Peek(HeaderLocation))) @@ -1420,6 +1507,7 @@ func Test_Ctx_Write(t *testing.T) { ctx.Write("Hello, ") ctx.Write([]byte("World! ")) ctx.Write(123) + ctx.Write(123.321) ctx.Write(true) ctx.Write(bytes.NewReader([]byte("Don't crash please"))) utils.AssertEqual(t, "Don't crash please", string(ctx.Fasthttp.Response.Body())) @@ -1553,6 +1641,11 @@ func Test_Ctx_QueryParser(t *testing.T) { q := new(Query) utils.AssertEqual(t, nil, ctx.QueryParser(q)) utils.AssertEqual(t, 2, len(q.Hobby)) + + empty := new(Query) + ctx.Fasthttp.Request.URI().SetQueryString("") + utils.AssertEqual(t, nil, ctx.QueryParser(empty)) + utils.AssertEqual(t, 0, len(empty.Hobby)) } // go test -v -run=^$ -bench=Benchmark_Ctx_QueryParser -benchmem -count=4 From d406e2c5078e0062025bc4e2279b15cc6d7a586a Mon Sep 17 00:00:00 2001 From: kiyon Date: Tue, 14 Jul 2020 15:24:42 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=8D=A4=20improve=20utils=20test=20cov?= =?UTF-8?q?erage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/utils_test.go b/utils_test.go index b39f4771..c17d89a7 100644 --- a/utils_test.go +++ b/utils_test.go @@ -15,11 +15,40 @@ import ( func Test_Utils_ETag(t *testing.T) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}) - defer app.ReleaseCtx(c) - c.Send("Hello, World!") - setETag(c, false) - utils.AssertEqual(t, `"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + t.Run("Not Status OK", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + c.Status(201) + setETag(c, false) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + }) + + t.Run("No Body", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + setETag(c, false) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + }) + + t.Run("Has HeaderIfNoneMatch", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + c.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, `"13-1831710635"`) + setETag(c, false) + utils.AssertEqual(t, 304, c.Fasthttp.Response.StatusCode()) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Body())) + }) + + t.Run("No HeaderIfNoneMatch", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + setETag(c, false) + utils.AssertEqual(t, `"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + }) } // go test -v -run=^$ -bench=Benchmark_App_ETag -benchmem -count=4 @@ -36,11 +65,33 @@ func Benchmark_Utils_ETag(b *testing.B) { func Test_Utils_ETag_Weak(t *testing.T) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}) - defer app.ReleaseCtx(c) - c.Send("Hello, World!") - setETag(c, true) - utils.AssertEqual(t, `W/"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + t.Run("Set Weak", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + setETag(c, true) + utils.AssertEqual(t, `W/"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + }) + + t.Run("Match Weak ETag", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + c.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, `W/"13-1831710635"`) + setETag(c, true) + utils.AssertEqual(t, 304, c.Fasthttp.Response.StatusCode()) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + utils.AssertEqual(t, "", string(c.Fasthttp.Response.Body())) + }) + + t.Run("Not Match Weak ETag", func(t *testing.T) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Send("Hello, World!") + c.Fasthttp.Request.Header.Set(HeaderIfNoneMatch, `W/"13-1831710635xx"`) + setETag(c, true) + utils.AssertEqual(t, `W/"13-1831710635"`, string(c.Fasthttp.Response.Header.Peek(HeaderETag))) + }) } // go test -v -run=^$ -bench=Benchmark_App_ETag_Weak -benchmem -count=4 From a92bcf75003ec4d5f9ffdc7a7e8176132befd425 Mon Sep 17 00:00:00 2001 From: kiyon Date: Tue, 14 Jul 2020 15:49:42 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=87=A8=F0=9F=87=B3=20improve=20README?= =?UTF-8?q?=5Fzh-CN.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/README_zh-CN.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index d6558f0a..cd2abc44 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -124,18 +124,18 @@ go get -u github.com/gofiber/fiber - [内存占用低](https://docs.gofiber.io/benchmarks) - [API接口](https://docs.gofiber.io/context) - [中间件](https://docs.gofiber.io/middleware)和[Next](https://docs.gofiber.io/context#next)支持 -- [快速的](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497)服务器端编程 +- [快速](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497)服务器端编程 - [模版引擎](https://github.com/gofiber/template) - [WebSocket支持](https://docs.gofiber.io/middleware#websocket) - [频率限制器](https://docs.gofiber.io/middleware#limiter) -- [15国语言](https://docs.gofiber.io/) +- [15种语言](https://docs.gofiber.io/) - 以及更多请[探索文档](https://docs.gofiber.io/) ## 💡 哲学 从[Node.js](https://nodejs.org/en/about/)切换到[Go](https://golang.org/doc/)的新`gopher`在开始构建`Web`应用程序或微服务之前正在应对学习曲线。 `Fiber`作为一个**Web框架** ,是按照**极简主义**的思想并遵循**UNIX方式**创建的,因此新的`gopher`可以在热烈和可信赖的欢迎中迅速进入`Go`的世界。 -`Fiber`受到了互联网上最流行的`Web`框架`Express`的**启发** 。我们结合了`Express`的**易用性**和`Go`的**原始性能** 。如果您曾经在`Node.js`上实现过`Web`应用程序(*使用Express.js或类似工具*),那么许多方法和原理对您来说应该**非常易懂**。 +`Fiber`受到了互联网上最流行的`Web`框架`Express`的**启发** 。我们结合了`Express`的**易用性**和`Go`的**原始性能** 。如果您曾经在`Node.js`上实现过`Web`应用程序(*使用Express或类似工具*),那么许多方法和原理对您来说应该**非常易懂**。 我们**关注** _整个互联网_ 用户在[issues](https://github.com/gofiber/fiber/issues)和Discord [channel](https://gofiber.io/discord)的消息,为了创建一个**迅速**,**灵活**以及**友好**的`Go web`框架,满足**任何**任务,**最后期限**和开发者**技能**。就像`Express`在`JavaScript`世界中一样。 @@ -243,9 +243,9 @@ func main() { 如果未设置模版引擎,则`Fiber`默认使用[html/template](https://golang.org/pkg/html/template/)。 -如果您要执行部分模版或使用其他引擎,例如[amber](https://github.com/eknkc/amber),[handlebars](https://github.com/aymerick/raymond),[mustache](https://github.com/cbroglie/mustache)或者[pug](https://github.com/Joker/jade)等等 +如果您要执行部分模版或使用其他引擎,例如[amber](https://github.com/eknkc/amber),[handlebars](https://github.com/aymerick/raymond),[mustache](https://github.com/cbroglie/mustache)或者[pug](https://github.com/Joker/jade)等等... -查看我们的[Template](https://github.com/gofiber/template)包,该包支持多个模版引擎。 +请查看我们的[Template](https://github.com/gofiber/template)包,该包支持多个模版引擎。 ```go import ( @@ -299,7 +299,7 @@ func main() { } ``` -### 访问日志中间件 +### 日志中间件 📖 [Logger](https://github.com/gofiber/fiber/blob/master/middleware/logger.md) @@ -330,7 +330,7 @@ func main() { app.Listen(3000) } ``` -### 跨域资源共享(CORS) +### 跨域资源共享(CORS)中间件 📖 [CORS](https://docs.gofiber.io/middleware#cors) @@ -356,7 +356,7 @@ func main() { curl -H "Origin: http://example.com" --verbose http://localhost:3000 ``` -### 自定义404相应 +### 自定义404响应 📖 [HTTP Methods](https://docs.gofiber.io/application#http-methods) @@ -414,7 +414,7 @@ func main() { } ``` -### WebSocket支持 +### 升级到WebSocket 📖 [Websocket](https://docs.gofiber.io/middleware#websocket) @@ -500,7 +500,7 @@ func main() { ## 🌱 第三方中间件 -这是由`Fiber`社区创建的中间件的列表,如果您想看到自己的中间件,请创建`PR`。 +这是由`Fiber`社区创建的中间件列表,如果您想看到自己的中间件,请创建`PR`。 - [arsmn/fiber-swagger](https://github.com/arsmn/fiber-swagger) - [arsmn/fiber-casbin](https://github.com/arsmn/fiber-casbin) - [arsmn/fiber-introspect](https://github.com/arsmn/fiber-introspect) From fa9128b7d6bb15b0edd5ccda94f097d061a09e27 Mon Sep 17 00:00:00 2001 From: ReneWerner87 Date: Tue, 14 Jul 2020 12:21:55 +0200 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=90=9E=20prevent=20duplicates=20in=20?= =?UTF-8?q?ctx.Append=20for=20similar=20wording=20of=20the=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx_test.go | 22 ++++++++++++++++++++++ utils.go | 1 + 2 files changed, 23 insertions(+) diff --git a/ctx_test.go b/ctx_test.go index 75072114..a71f5906 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -172,8 +172,30 @@ func Test_Ctx_Append(t *testing.T) { ctx.Append("X-Test", "Hello") ctx.Append("X-Test", "World") ctx.Append("X-Test", "Hello", "World") + // similar value in the middle + ctx.Append("X2-Test", "World") + ctx.Append("X2-Test", "XHello") + ctx.Append("X2-Test", "Hello", "World") + // similar value at the start + ctx.Append("X3-Test", "XHello") + ctx.Append("X3-Test", "World") + ctx.Append("X3-Test", "Hello", "World") + // try it with multiple similar values + ctx.Append("X4-Test", "XHello") + ctx.Append("X4-Test", "Hello") + ctx.Append("X4-Test", "HelloZ") + ctx.Append("X4-Test", "YHello") + ctx.Append("X4-Test", "Hello") + ctx.Append("X4-Test", "YHello") + ctx.Append("X4-Test", "HelloZ") + ctx.Append("X4-Test", "XHello") + // without append value ctx.Append("X-Custom-Header") + utils.AssertEqual(t, "Hello, World", string(ctx.Fasthttp.Response.Header.Peek("X-Test"))) + utils.AssertEqual(t, "World, XHello, Hello", string(ctx.Fasthttp.Response.Header.Peek("X2-Test"))) + utils.AssertEqual(t, "XHello, World, Hello", string(ctx.Fasthttp.Response.Header.Peek("X3-Test"))) + utils.AssertEqual(t, "XHello, Hello, HelloZ, YHello", string(ctx.Fasthttp.Response.Header.Peek("X4-Test"))) utils.AssertEqual(t, "", string(ctx.Fasthttp.Response.Header.Peek("x-custom-header"))) } diff --git a/utils.go b/utils.go index 0cbe54be..797041ab 100644 --- a/utils.go +++ b/utils.go @@ -17,6 +17,7 @@ import ( fasthttp "github.com/valyala/fasthttp" ) +// quoteString escape special characters in a given string func quoteString(raw string) string { bb := bytebufferpool.Get() quoted := string(fasthttp.AppendQuotedArg(bb.B, getBytes(raw))) From 4035b17ee0a63bdb429334dfb1f7081bc4a2d28f Mon Sep 17 00:00:00 2001 From: ReneWerner87 Date: Tue, 14 Jul 2020 12:23:11 +0200 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=90=9E=20prevent=20duplicates=20in=20?= =?UTF-8?q?ctx.Append=20for=20similar=20wording=20of=20the=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index f68eda34..fec7bada 100644 --- a/ctx.go +++ b/ctx.go @@ -181,8 +181,8 @@ func (ctx *Ctx) Append(field string, values ...string) { for _, value := range values { if len(h) == 0 { h = value - } else if h != value && !strings.HasSuffix(h, " "+value) && - !strings.Contains(h, value+",") { + } else if h != value && !strings.HasPrefix(h, value+",") && !strings.HasSuffix(h, " "+value) && + !strings.Contains(h, " "+value+",") { h += ", " + value } }