From 8f75a678f518cab7cb23695296240ec2959671e1 Mon Sep 17 00:00:00 2001 From: Spring MC Date: Sun, 20 Jan 2019 11:20:02 +0800 Subject: [PATCH 1/3] What: fix ghost response issue Why: * ensure response without render How: * nop --- context.go | 13 ++++++++++--- server_test.go | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index cc16ece..378ed81 100644 --- a/context.go +++ b/context.go @@ -337,8 +337,15 @@ func (c *Context) run(w http.ResponseWriter, handler http.Handler, middlewares [ // start chains c.Next() - // invoke http.Handler if existed - if c.cursor >= 0 && c.cursor < math.MaxInt8 && handler != nil { - handler.ServeHTTP(c.Response, c.Request) + // ghost without render + if c.cursor >= 0 && c.cursor < math.MaxInt8 { + c.Abort() + + // invoke http.Handler if existed + if handler != nil { + handler.ServeHTTP(c.Response, c.Request) + } else { + c.Response.FlushHeader() + } } } diff --git a/server_test.go b/server_test.go index e096a11..17a8a44 100644 --- a/server_test.go +++ b/server_test.go @@ -3,6 +3,7 @@ package gogo import ( "encoding/xml" "fmt" + "math" "net/http" "net/http/httptest" "net/url" @@ -49,7 +50,7 @@ func Test_ServerNewContext(t *testing.T) { assertion.Nil(ctx.settings) assertion.Nil(ctx.frozenSettings) assertion.Empty(ctx.middlewares) - assertion.EqualValues(0, ctx.cursor) + assertion.EqualValues(math.MaxInt8, ctx.cursor) // creation newCtx := server.newContext(request, "", "", params) From 2371ca8fa80e15b3c3d59f31da0b8d15417642c6 Mon Sep 17 00:00:00 2001 From: Spring MC Date: Sun, 20 Jan 2019 11:33:02 +0800 Subject: [PATCH 2/3] What: add NewDefaults() Why: * creates defaults *AppServer for quick start How: * nop --- gogo.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gogo.go b/gogo.go index fad018d..ecbc03e 100644 --- a/gogo.go +++ b/gogo.go @@ -48,6 +48,13 @@ var ( } ) +// NewDefaults creates a *AppServer for quick start +func NewDefaults() *AppServer { + cfger, _ := NewAppConfigFromDefault() + + return NewWithConfiger(cfger) +} + // New creates application server with config resolved // from file /config/application[.].json. // NOTE: You can custom resolver by overwriting FindModeConfigFile. From 599eddbf5125470a0d7eed766cb7e5e0d2ce60fb Mon Sep 17 00:00:00 2001 From: Spring MC Date: Sun, 20 Jan 2019 11:37:22 +0800 Subject: [PATCH 3/3] What: update readme Why: * clearly readme How: * nop --- README.md | 179 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index 49e2790..ecc1e76 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![CircleCI](https://circleci.com/gh/dolab/gogo/tree/master.svg?style=svg)](https://circleci.com/gh/dolab/gogo/tree/master) [![Coverage](http://gocover.io/_badge/github.com/dolab/gogo?0)](http://gocover.io/github.com/dolab/gogo) [![GoDoc](https://godoc.org/github.com/dolab/gogo?status.svg)](http://godoc.org/github.com/dolab/gogo) +> **NOTE**: This is `VERSION 1` compactible upgrade! + `gogo` is an open source, high performance RESTful api framework for the [Golang](https://golang.org) programming language. It's heavily inspired from [rails](http://rubyonrails.org/) and [neko](https://github.com/rocwong/neko). @@ -57,14 +59,14 @@ import ( ) func main() { - // load config from config/application.json - // app := gogo.New("development", "/path/to/[config/application.json]") + // load config from config/application.json + // app := gogo.New("development", "/path/to/[config/application.json]") - // load config frome filename - // app := gogo.New("development", "/path/to/config.json") + // load config from filename + // app := gogo.New("development", "/path/to/config.json") - // use default config - app := gogo.New("development", "") + // use default config + app := gogo.NewDefaults() // GET / app.GET("/", func(ctx *gogo.Context) { @@ -94,30 +96,32 @@ func main() { package main import ( - "github.com/dolab/gogo" + "github.com/dolab/gogo" ) func main() { - app := gogo.New("development", "") + app := gogo.NewDefaults() - // avoid server quit by registering a recovery middleware - app.Use(func(ctx *gogo.Context) { - if panicErr := recover(); panicErr != nil { - ctx.Abort() + // avoid server quit by registering a recovery middleware + app.Use(func(ctx *gogo.Context) { + if panicErr := recover(); panicErr != nil { + ctx.Logger.Errorf("[PANICED] %v", panicErr) - ctx.Logger.Errorf("[PANICED] %v", panicErr) - return - } + ctx.Return(map[string]interface{}{ + "panic": panicErr, + }) + return + } - ctx.Next() - }) + ctx.Next() + }) - // GET / - app.GET("/", func(ctx *gogo.Context) { - panic("Oops ~ ~ ~") - }) + // GET / + app.GET("/", func(ctx *gogo.Context) { + panic("Oops ~ ~ ~") + }) - app.Run() + app.Run() } ``` @@ -127,77 +131,80 @@ func main() { package main import ( - "encoding/base64" - "net/http" - "strings" + "encoding/base64" + "net/http" + "strings" - "github.com/dolab/gogo" + "github.com/dolab/gogo" ) func main() { - app := gogo.New("development", "") + app := gogo.NewDefaults() - // avoid server quit by registering recovery func global - app.Use(func(ctx *gogo.Context) { - if panicErr := recover(); panicErr != nil { - ctx.Abort() + // avoid server quit by registering recovery func global + app.Use(func(ctx *gogo.Context) { + if panicErr := recover(); panicErr != nil { + ctx.Logger.Errorf("[PANICED] %v", panicErr) - ctx.Logger.Errorf("[PANICED] %v", panicErr) - } + ctx.Return(map[string]interface{}{ + "panic": panicErr, + }) + } - ctx.Next() - }) + ctx.Next() + }) + - // GET / - app.GET("/", func(ctx *gogo.Context) { - panic("Oops ~ ~ ~") - }) + // GET / + app.GET("/", func(ctx *gogo.Context) { + panic("Oops ~ ~ ~") + }) - // prefix resources with /v1 and apply basic auth middleware for all sub-resources - // NOTE: it combines recovery middleware from previous. - v1 := app.Group("/v1", func(ctx *gogo.Context) { - auth := ctx.Header("Authorization") - if !strings.HasPrefix(auth, "Basic ") { - ctx.Abort() + // prefix resources with /v1 and apply basic auth middleware for all sub-resources + // NOTE: it combines recovery middleware from previous. + v1 := app.Group("/v1", func(ctx *gogo.Context) { + auth := ctx.Header("Authorization") + if !strings.HasPrefix(auth, "Basic ") { + ctx.Abort() - ctx.SetStatus(http.StatusForbidden) - ctx.Return() - return - } + ctx.SetStatus(http.StatusForbidden) + ctx.Return() + return + } - b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) - if err != nil { - ctx.Logger.Errorf("Base64 decode error: %v", err) - ctx.Abort() + b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) + if err != nil { + ctx.Logger.Errorf("Base64 decode error: %v", err) + ctx.Abort() - ctx.SetStatus(http.StatusForbidden) - ctx.Return() - return - } + ctx.SetStatus(http.StatusForbidden) + ctx.Return() + return + } - tmp := strings.SplitN(string(b), ":", 2) - if len(tmp) != 2 || tmp[0] != "gogo" || tmp[1] != "ogog" { - ctx.Abort() + tmp := strings.SplitN(string(b), ":", 2) + if len(tmp) != 2 || tmp[0] != "gogo" || tmp[1] != "ogog" { + ctx.Abort() - ctx.SetStatus(http.StatusForbidden) - ctx.Return() - return - } + ctx.SetStatus(http.StatusForbidden) + ctx.Return() + return + } - // settings which can used by following middlewares and handler - ctx.Set("username", tmp[0]) + // settings which can used by following middlewares and handler + ctx.Set("username", tmp[0]) - ctx.Next() - }) + ctx.Next() + }) - // GET /v1 - v1.GET("/", func(ctx *gogo.Context) { - username := ctx.MustGet("username").(string) + // GET /v1 + v1.GET("/", func(ctx *gogo.Context) { + username := ctx.MustGet("username").(string) - ctx.Text("Hello, " + username + "!") - }) + ctx.Text("Hello, " + username + "!") + }) - app.Run() + app.Run() } ``` @@ -213,7 +220,7 @@ you can overwrite default id key by implementing `ControllerID` interface. package main import ( - "github.com/dolab/gogo" + "github.com/dolab/gogo" ) type GroupController struct{} @@ -241,7 +248,7 @@ func (t *UserController) Show(ctx *gogo.Context) { } func main() { - app := gogo.New("development", "") + app := gogo.NewDefaults() // register group controller with default :group key group := app.Resource("/group", &GroupController{}) @@ -260,16 +267,16 @@ func main() { ```json { - "addr": "localhost", - "port": 9090, - "throttle": 3000, // RPS, throughput of per-seconds - "slowdown": 30000, // TPS, concurrency of server - "request_timeout": 30, - "response_timeout": 30, - "ssl": false, - "ssl_cert": "/path/to/ssl/cert", - "ssl_key": "/path/to/ssl/key", - "request_id": "X-Request-Id" + "addr": "localhost", + "port": 9090, + "throttle": 3000, // RPS, throughput of per-seconds + "slowdown": 30000, // TPS, concurrency of server + "request_timeout": 30, + "response_timeout": 30, + "ssl": false, + "ssl_cert": "/path/to/ssl/cert", + "ssl_key": "/path/to/ssl/key", + "request_id": "X-Request-Id" } ```