mirror of https://github.com/gofiber/fiber.git
Merge remote-tracking branch 'upstream/master'
commit
0a3eb08530
|
@ -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)
|
||||
|
|
5
app.go
5
app.go
|
@ -425,6 +425,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 {
|
||||
|
@ -684,7 +685,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"
|
||||
|
@ -711,7 +712,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()
|
||||
|
|
37
app_test.go
37
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)
|
||||
}
|
||||
|
|
40
ctx.go
40
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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
|
139
ctx_test.go
139
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")))
|
||||
}
|
||||
|
||||
|
@ -356,24 +378,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, "<p>Hello, World!</p>", 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, `<string>Hello, World!</string>`, 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 +533,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 +835,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 +881,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 +1098,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 +1149,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 +1197,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 +1268,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 +1529,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 +1663,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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
7
utils.go
7
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)))
|
||||
|
@ -76,7 +77,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 +102,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 +112,7 @@ func setETag(ctx *Ctx, weak bool) {
|
|||
}
|
||||
if strings.Contains(clientEtag, etag) {
|
||||
// 1 == 1
|
||||
ctx.SendStatus(304)
|
||||
ctx.SendStatus(StatusNotModified)
|
||||
ctx.Fasthttp.ResetBody()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue