From ddec78af0a3b7ca17aaad5d9542245c725b7fe8c Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 23 Feb 2019 07:23:10 +0200 Subject: [PATCH] add `Context.ResponseWriter.IsHijacked` to report whether the underline conn is already hijacked and a lot of cleanup and minor ws stress test example improvements Former-commit-id: 444d4f0718d5c6d7544834c5e44dafb872980238 --- HISTORY.md | 14 +++--- HISTORY_GR.md | 2 +- HISTORY_ID.md | 6 +-- README.md | 6 +-- _examples/configuration/README.md | 2 +- _examples/hello-world/main.go | 2 +- _examples/hero/basic/README.md | 2 +- _examples/hero/overview/web/routes/hello.go | 2 +- _examples/mvc/hello-world/main.go | 2 +- .../web/controllers/hello_controller.go | 2 +- _examples/routing/macros/main.go | 4 +- _examples/websocket/README.md | 2 +- _examples/websocket/chat/main.go | 2 +- _examples/websocket/connectionlist/main.go | 2 +- .../go-client-stress-test/client/main.go | 43 +++++++++++------- .../go-client-stress-test/server/main.go | 44 ++++++++++++------- _examples/websocket/secure/main.go | 2 +- cache/client/handler.go | 2 +- context/context.go | 2 +- context/response_writer.go | 12 +++++ doc.go | 4 +- go19.go | 2 +- hero/di/func.go | 2 +- hero/session.go | 2 +- iris.go | 2 +- middleware/README.md | 2 +- sessions/config.go | 2 +- view/README.md | 2 +- websocket/connection.go | 11 ++++- websocket/server.go | 38 ++++------------ websocket/websocket.go | 2 +- 31 files changed, 124 insertions(+), 100 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 340eb0d1..3c9737b0 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -38,7 +38,7 @@ I have some features in-mind but lately I do not have the time to humanize those - fix [#1164](https://github.com/kataras/iris/issues/1164). [701e8e46c20395f87fa34bf9fabd145074c7b78c](https://github.com/kataras/iris/commit/701e8e46c20395f87fa34bf9fabd145074c7b78c) (@kataras) -- `context#ReadForm` can skip unkown fields by `IsErrPath(err)`, fixes: [#1157](https://github.com/kataras/iris/issues/1157). [1607bb5113568af6a34142f23bfa44903205b314](https://github.com/kataras/iris/commit/1607bb5113568af6a34142f23bfa44903205b314) (@kataras) +- `context#ReadForm` can skip unknown fields by `IsErrPath(err)`, fixes: [#1157](https://github.com/kataras/iris/issues/1157). [1607bb5113568af6a34142f23bfa44903205b314](https://github.com/kataras/iris/commit/1607bb5113568af6a34142f23bfa44903205b314) (@kataras) Doc updates: @@ -281,7 +281,7 @@ wsServer := websocket.New(websocket.Config{ // [...] -// serve the javascript built'n client-side library, +// serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(wsServer.ClientSource) @@ -401,7 +401,7 @@ The old `github.com/kataras/iris/core/router/macro` package was moved to `guthub - Add `:uint64` parameter type and `ctx.Params().GetUint64` - Add alias `:bool` for the `:boolean` parameter type -Here is the full list of the built'n parameter types that we support now, including their validations/path segment rules. +Here is the full list of the builtin parameter types that we support now, including their validations/path segment rules. | Param Type | Go Type | Validation | Retrieve Helper | | -----------------|------|-------------|------| @@ -430,7 +430,7 @@ app.Get("/users/{id:uint64}", func(ctx iris.Context){ }) ``` -| Built'n Func | Param Types | +| Builtin Func | Param Types | | -----------|---------------| | `regexp`(expr string) | :string | | `prefix`(prefix string) | :string | @@ -633,7 +633,7 @@ Sincerely, # We, 25 April 2018 | v10.6.1 -- Re-implement the [BoltDB](https://github.com/coreos/bbolt) as built'n back-end storage for sessions(`sessiondb`) using the latest features: [/sessions/sessiondb/boltdb/database.go](sessions/sessiondb/boltdb/database.go), example can be found at [/_examples/sessions/database/boltdb/main.go](_examples/sessions/database/boltdb/main.go). +- Re-implement the [BoltDB](https://github.com/coreos/bbolt) as builtin back-end storage for sessions(`sessiondb`) using the latest features: [/sessions/sessiondb/boltdb/database.go](sessions/sessiondb/boltdb/database.go), example can be found at [/_examples/sessions/database/boltdb/main.go](_examples/sessions/database/boltdb/main.go). - Fix a minor issue on [Badger sessiondb example](_examples/sessions/database/badger/main.go). Its `sessions.Config { Expires }` field was `2 *time.Second`, it's `45 *time.Minute` now. - Other minor improvements to the badger sessiondb. @@ -642,7 +642,7 @@ Sincerely, - Fix open redirect by @wozz via PR: https://github.com/kataras/iris/pull/972. - Fix when destroy session can't remove cookie in subdomain by @Chengyumeng via PR: https://github.com/kataras/iris/pull/964. - Add `OnDestroy(sid string)` on sessions for registering a listener when a session is destroyed with commit: https://github.com/kataras/iris/commit/d17d7fecbe4937476d00af7fda1c138c1ac6f34d. -- Finally, sessions are in full-sync with the registered database now. That required a lot of internal code changed but **zero code change requirements by your side**. We kept only `badger` and `redis` as the back-end built'n supported sessions storages, they are enough. Made with commit: https://github.com/kataras/iris/commit/f2c3a5f0cef62099fd4d77c5ccb14f654ddbfb5c relative to many issues that you've requested it. +- Finally, sessions are in full-sync with the registered database now. That required a lot of internal code changed but **zero code change requirements by your side**. We kept only `badger` and `redis` as the back-end builtin supported sessions storages, they are enough. Made with commit: https://github.com/kataras/iris/commit/f2c3a5f0cef62099fd4d77c5ccb14f654ddbfb5c relative to many issues that you've requested it. # Sa, 24 March 2018 | v10.5.0 @@ -840,7 +840,7 @@ The new package [hero](hero) contains features for binding any object or functio Below you will see some screenshots we prepared for you in order to be easier to understand: -### 1. Path Parameters - Built'n Dependencies +### 1. Path Parameters - Builtin Dependencies ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) diff --git a/HISTORY_GR.md b/HISTORY_GR.md index 34f6a5f2..e3016176 100644 --- a/HISTORY_GR.md +++ b/HISTORY_GR.md @@ -201,7 +201,7 @@ This history entry is not yet translated to Greek. Please read [the english vers Παρακάτω θα δείτε μερικά στιγμιότυπα που ετοιμάσαμε για εσάς, ώστε να γίνουν πιο κατανοητά τα παραπάνω: -### 1. Παράμετροι διαδρομής - Ενσωματωμένες Εξαρτήσεις (Built'n Dependencies) +### 1. Παράμετροι διαδρομής - Ενσωματωμένες Εξαρτήσεις (Builtin Dependencies) ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) diff --git a/HISTORY_ID.md b/HISTORY_ID.md index f69ea630..d28769d2 100644 --- a/HISTORY_ID.md +++ b/HISTORY_ID.md @@ -71,7 +71,7 @@ This history entry is not translated yet to the Bahasa Indonesia language yet, p # We, 25 April 2018 | v10.6.1 -- Re-implement the [BoltDB](https://github.com/coreos/bbolt) as built'n back-end storage for sessions(`sessiondb`) using the latest features: [/sessions/sessiondb/boltdb/database.go](sessions/sessiondb/boltdb/database.go), example can be found at [/_examples/sessions/database/boltdb/main.go](_examples/sessions/database/boltdb/main.go). +- Re-implement the [BoltDB](https://github.com/coreos/bbolt) as builtin back-end storage for sessions(`sessiondb`) using the latest features: [/sessions/sessiondb/boltdb/database.go](sessions/sessiondb/boltdb/database.go), example can be found at [/_examples/sessions/database/boltdb/main.go](_examples/sessions/database/boltdb/main.go). - Fix a minor issue on [Badger sessiondb example](_examples/sessions/database/badger/main.go). Its `sessions.Config { Expires }` field was `2 *time.Second`, it's `45 *time.Minute` now. - Other minor improvements to the badger sessiondb. @@ -80,7 +80,7 @@ This history entry is not translated yet to the Bahasa Indonesia language yet, p - Fix open redirect by @wozz via PR: https://github.com/kataras/iris/pull/972. - Fix when destroy session can't remove cookie in subdomain by @Chengyumeng via PR: https://github.com/kataras/iris/pull/964. - Add `OnDestroy(sid string)` on sessions for registering a listener when a session is destroyed with commit: https://github.com/kataras/iris/commit/d17d7fecbe4937476d00af7fda1c138c1ac6f34d. -- Finally, sessions are in full-sync with the registered database now. That required a lot of internal code changed but **zero code change requirements by your side**. We kept only `badger` and `redis` as the back-end built'n supported sessions storages, they are enough. Made with commit: https://github.com/kataras/iris/commit/f2c3a5f0cef62099fd4d77c5ccb14f654ddbfb5c relative to many issues that you've requested it. +- Finally, sessions are in full-sync with the registered database now. That required a lot of internal code changed but **zero code change requirements by your side**. We kept only `badger` and `redis` as the back-end builtin supported sessions storages, they are enough. Made with commit: https://github.com/kataras/iris/commit/f2c3a5f0cef62099fd4d77c5ccb14f654ddbfb5c relative to many issues that you've requested it. # Sa, 24 March 2018 | v10.5.0 @@ -278,7 +278,7 @@ The new package [hero](hero) contains features for binding any object or functio Below you will see some screenshots we prepared for you in order to be easier to understand: -### 1. Path Parameters - Built'n Dependencies +### 1. Path Parameters - Builtin Dependencies ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) diff --git a/README.md b/README.md index dfb02623..79160bc8 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ app.Get("/users/{id:uint64}", func(ctx iris.Context){ }) ``` -| Built'n Func | Param Types | +| Builtin Func | Param Types | | -----------|---------------| | `regexp`(expr string) | :string | | `prefix`(prefix string) | :string | @@ -300,7 +300,7 @@ With Iris you get truly safe bindings thanks to the [hero](_examples/hero) [pack Below you will see some screenshots I prepared for you in order to be easier to understand: -#### 1. Path Parameters - Built'n Dependencies +#### 1. Path Parameters - Builtin Dependencies ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) @@ -797,7 +797,7 @@ func setupWebsocket(app *iris.Application) { // see the inline javascript code in the websockets.html, // this endpoint is used to connect to the server. app.Get("/echo", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", websocket.ClientHandler()) } diff --git a/_examples/configuration/README.md b/_examples/configuration/README.md index fa54b775..774c0182 100644 --- a/_examples/configuration/README.md +++ b/_examples/configuration/README.md @@ -134,7 +134,7 @@ func main() { ``` -## Built'n Configurators +## Builtin Configurators ```go // WithoutServerError will cause to ignore the matched "errors" diff --git a/_examples/hello-world/main.go b/_examples/hello-world/main.go index f9b4bc7e..706cbc80 100644 --- a/_examples/hello-world/main.go +++ b/_examples/hello-world/main.go @@ -10,7 +10,7 @@ import ( func main() { app := iris.New() app.Logger().SetLevel("debug") - // Optionally, add two built'n handlers + // Optionally, add two builtin handlers // that can recover from any http-relative panics // and log the requests to the terminal. app.Use(recover.New()) diff --git a/_examples/hero/basic/README.md b/_examples/hero/basic/README.md index c039ce97..88309fb5 100644 --- a/_examples/hero/basic/README.md +++ b/_examples/hero/basic/README.md @@ -1,6 +1,6 @@ # hero: basic -## 1. Path Parameters - Built'n Dependencies +## 1. Path Parameters - Builtin Dependencies ![](https://github.com/kataras/explore/raw/master/iris/hero/hero-1-monokai.png) diff --git a/_examples/hero/overview/web/routes/hello.go b/_examples/hero/overview/web/routes/hello.go index 8feeca14..e3bba5fc 100644 --- a/_examples/hero/overview/web/routes/hello.go +++ b/_examples/hero/overview/web/routes/hello.go @@ -19,7 +19,7 @@ var helloView = hero.View{ // Hello will return a predefined view with bind data. // // `hero.Result` is just an interface with a `Dispatch` function. -// `hero.Response` and `hero.View` are the built'n result type dispatchers +// `hero.Response` and `hero.View` are the builtin result type dispatchers // you can even create custom response dispatchers by // implementing the `github.com/kataras/iris/hero#Result` interface. func Hello() hero.Result { diff --git a/_examples/mvc/hello-world/main.go b/_examples/mvc/hello-world/main.go index 6f2ec963..c93ff4b0 100644 --- a/_examples/mvc/hello-world/main.go +++ b/_examples/mvc/hello-world/main.go @@ -32,7 +32,7 @@ import ( // for the main_test.go. func newApp() *iris.Application { app := iris.New() - // Optionally, add two built'n handlers + // Optionally, add two builtin handlers // that can recover from any http-relative panics // and log the requests to the terminal. app.Use(recover.New()) diff --git a/_examples/mvc/overview/web/controllers/hello_controller.go b/_examples/mvc/overview/web/controllers/hello_controller.go index d1ff76ae..2e10dc74 100644 --- a/_examples/mvc/overview/web/controllers/hello_controller.go +++ b/_examples/mvc/overview/web/controllers/hello_controller.go @@ -23,7 +23,7 @@ var helloView = mvc.View{ // Get will return a predefined view with bind data. // // `mvc.Result` is just an interface with a `Dispatch` function. -// `mvc.Response` and `mvc.View` are the built'n result type dispatchers +// `mvc.Response` and `mvc.View` are the builtin result type dispatchers // you can even create custom response dispatchers by // implementing the `github.com/kataras/iris/hero#Result` interface. func (c *HelloController) Get() mvc.Result { diff --git a/_examples/routing/macros/main.go b/_examples/routing/macros/main.go index 3de4e29a..c1943001 100644 --- a/_examples/routing/macros/main.go +++ b/_examples/routing/macros/main.go @@ -52,7 +52,7 @@ func main() { /* http://localhost:8080/test_slice_hero/myvaluei1/myavlue2 -> - myparam's value (a trailing path parameter type) is: []string{"myvaluei1", "myavlue2"} + myparam's value (a trailing path parameter type) is: []string{"myvalue1", "myavlue2"} */ app.Get("/test_slice_hero/{myparam:slice}", hero.Handler(func(myparam []string) string { return fmt.Sprintf("myparam's value (a trailing path parameter type) is: %#v\n", myparam) @@ -66,7 +66,7 @@ func main() { myparam's value (a trailing path parameter type) is: []string{"value1", "value2"} */ app.Get("/test_slice_contains/{myparam:slice contains([value1,value2])}", func(ctx context.Context) { - // When it is not a built'n function available to retrieve your value with the type you want, such as ctx.Params().GetInt + // When it is not a builtin function available to retrieve your value with the type you want, such as ctx.Params().GetInt // then you can use the `GetEntry.ValueRaw` to get the real value, which is set-ed by your macro above. myparam := ctx.Params().GetEntry("myparam").ValueRaw.([]string) ctx.Writef("myparam's value (a trailing path parameter type) is: %#v\n", myparam) diff --git a/_examples/websocket/README.md b/_examples/websocket/README.md index 6d7f41cf..b5cdea05 100644 --- a/_examples/websocket/README.md +++ b/_examples/websocket/README.md @@ -115,7 +115,7 @@ func main() { // see the inline javascript code in the websockets.html, this endpoint is used to connect to the server. app.Get("/echo", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) diff --git a/_examples/websocket/chat/main.go b/_examples/websocket/chat/main.go index e3f18b15..caeba79f 100644 --- a/_examples/websocket/chat/main.go +++ b/_examples/websocket/chat/main.go @@ -40,7 +40,7 @@ func setupWebsocket(app *iris.Application) { // see the inline javascript code in the websockets.html, this endpoint is used to connect to the server. app.Get("/echo", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(ws.ClientSource) diff --git a/_examples/websocket/connectionlist/main.go b/_examples/websocket/connectionlist/main.go index 490f85c6..ef808f8f 100644 --- a/_examples/websocket/connectionlist/main.go +++ b/_examples/websocket/connectionlist/main.go @@ -25,7 +25,7 @@ func main() { // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server. app.Get("/my_endpoint", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) diff --git a/_examples/websocket/go-client-stress-test/client/main.go b/_examples/websocket/go-client-stress-test/client/main.go index fb5d9806..69f50fc3 100644 --- a/_examples/websocket/go-client-stress-test/client/main.go +++ b/_examples/websocket/go-client-stress-test/client/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "context" "log" "math/rand" "net" @@ -19,7 +20,7 @@ var ( ) const totalClients = 16000 // max depends on the OS. -const verbose = true +const verbose = false var connectionFailures uint64 @@ -43,7 +44,7 @@ func collectError(op string, err error) { } func main() { - log.Println("--Running...") + log.Println("-- Running...") var err error f, err = os.Open("./test.data") if err != nil { @@ -63,7 +64,7 @@ func main() { wg.Add(1) waitTime := time.Duration(rand.Intn(5)) * time.Millisecond time.Sleep(waitTime) - go connect(wg, 7*time.Second+waitTime) + go connect(wg, 14*time.Second+waitTime) } for i := 0; i < totalClients/4; i++ { @@ -77,7 +78,7 @@ func main() { wg.Add(1) waitTime := time.Duration(rand.Intn(5)) * time.Millisecond time.Sleep(waitTime) - go connect(wg, 14*time.Second+waitTime) + go connect(wg, 7*time.Second+waitTime) } wg.Wait() @@ -136,16 +137,19 @@ func main() { log.Println("ALL OK.") } - log.Println("--Finished.") + log.Println("-- Finished.") } -func connect(wg *sync.WaitGroup, alive time.Duration) { - c, err := websocket.Dial(nil, url, websocket.ConnectionConfig{}) +func connect(wg *sync.WaitGroup, alive time.Duration) error { + ctx, cancel := context.WithTimeout(context.Background(), alive) + defer cancel() + + c, err := websocket.Dial(ctx, url, websocket.ConnectionConfig{}) if err != nil { atomic.AddUint64(&connectionFailures, 1) collectError("connect", err) wg.Done() - return + return err } c.OnError(func(err error) { @@ -167,23 +171,28 @@ func connect(wg *sync.WaitGroup, alive time.Duration) { } }) - go func() { - time.Sleep(alive) - if err := c.Disconnect(); err != nil { - collectError("disconnect", err) - } + if alive > 0 { + go func() { + time.Sleep(alive) + if err := c.Disconnect(); err != nil { + collectError("disconnect", err) + } - wg.Done() - }() + wg.Done() + }() + + } scanner := bufio.NewScanner(f) for !disconnected { - if !scanner.Scan() || scanner.Err() != nil { - break + if !scanner.Scan() { + return scanner.Err() } if text := scanner.Text(); len(text) > 1 { c.Emit("chat", text) } } + + return nil } diff --git a/_examples/websocket/go-client-stress-test/server/main.go b/_examples/websocket/go-client-stress-test/server/main.go index 37fe7383..b723d4dc 100644 --- a/_examples/websocket/go-client-stress-test/server/main.go +++ b/_examples/websocket/go-client-stress-test/server/main.go @@ -11,21 +11,24 @@ import ( "github.com/kataras/iris/websocket" ) -const totalClients = 16000 // max depends on the OS. -const verbose = true +const ( + endpoint = "localhost:8080" + totalClients = 16000 // max depends on the OS. + verbose = false + maxC = 0 +) func main() { - ws := websocket.New(websocket.Config{}) ws.OnConnection(handleConnection) // websocket.Config{PingPeriod: ((60 * time.Second) * 9) / 10} go func() { - dur := 8 * time.Second + dur := 4 * time.Second if totalClients >= 64000 { - // if more than 64000 then let's no check every 8 seconds, let's do it every 24 seconds, - // just for simplicity, either way works. + // if more than 64000 then let's perform those checks every 24 seconds instead, + // either way works. dur = 24 * time.Second } t := time.NewTicker(dur) @@ -40,12 +43,16 @@ func main() { n := ws.GetTotalConnections() if n > 0 { started = true + if maxC > 0 && n > maxC { + log.Printf("current connections[%d] > MaxConcurrentConnections[%d]", n, maxC) + return + } } if started { - totalConnected := atomic.LoadUint64(&count) - - if totalConnected == totalClients { + disconnectedN := atomic.LoadUint64(&totalDisconnected) + connectedN := atomic.LoadUint64(&totalConnected) + if disconnectedN == totalClients && connectedN == totalClients { if n != 0 { log.Println("ALL CLIENTS DISCONNECTED BUT LEFTOVERS ON CONNECTIONS LIST.") } else { @@ -53,7 +60,7 @@ func main() { } return } else if n == 0 { - log.Printf("%d/%d CLIENTS WERE NOT CONNECTED AT ALL. CHECK YOUR OS NET SETTINGS. ALL OTHER CONNECTED CLIENTS DISCONNECTED SUCCESSFULLY.\n", + log.Printf("%d/%d CLIENTS WERE NOT CONNECTED AT ALL. CHECK YOUR OS NET SETTINGS. THE REST CLIENTS WERE DISCONNECTED SUCCESSFULLY.\n", totalClients-totalConnected, totalClients) return @@ -64,11 +71,18 @@ func main() { app := iris.New() app.Get("/", ws.Handler()) - app.Run(iris.Addr(":8080")) - + app.Run(iris.Addr(endpoint), iris.WithoutServerError(iris.ErrServerClosed)) } +var totalConnected uint64 + func handleConnection(c websocket.Connection) { + if c.Err() != nil { + log.Fatalf("[%d] upgrade failed: %v", atomic.LoadUint64(&totalConnected)+1, c.Err()) + return + } + + atomic.AddUint64(&totalConnected, 1) c.OnError(func(err error) { handleErr(c, err) }) c.OnDisconnect(func() { handleDisconnect(c) }) c.On("chat", func(message string) { @@ -76,12 +90,12 @@ func handleConnection(c websocket.Connection) { }) } -var count uint64 +var totalDisconnected uint64 func handleDisconnect(c websocket.Connection) { - atomic.AddUint64(&count, 1) + newC := atomic.AddUint64(&totalDisconnected, 1) if verbose { - log.Printf("client [%s] disconnected!\n", c.ID()) + log.Printf("[%d] client [%s] disconnected!\n", newC, c.ID()) } } diff --git a/_examples/websocket/secure/main.go b/_examples/websocket/secure/main.go index 3d289114..5bedbb5e 100644 --- a/_examples/websocket/secure/main.go +++ b/_examples/websocket/secure/main.go @@ -26,7 +26,7 @@ func main() { // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server. app.Get("/my_endpoint", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) diff --git a/cache/client/handler.go b/cache/client/handler.go index 6cc35345..d0a7b893 100644 --- a/cache/client/handler.go +++ b/cache/client/handler.go @@ -125,7 +125,7 @@ func (h *Handler) ServeHTTP(ctx context.Context) { // if it's expired, then execute the original handler // with our custom response recorder response writer // because the net/http doesn't give us - // a built'n way to get the status code & body + // a builtin way to get the status code & body recorder := ctx.Recorder() bodyHandler(ctx) diff --git a/context/context.go b/context/context.go index fd751eb2..46badde5 100644 --- a/context/context.go +++ b/context/context.go @@ -3177,7 +3177,7 @@ func (ctx *context) SendFile(filename string, destinationName string) error { // context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie` // as their (last) variadic input argument to amend the end cookie's form. // -// Any custom or built'n `CookieOption` is valid, +// Any custom or builtin `CookieOption` is valid, // see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more. type CookieOption func(*http.Cookie) diff --git a/context/response_writer.go b/context/response_writer.go index b2afc3bf..419be652 100644 --- a/context/response_writer.go +++ b/context/response_writer.go @@ -41,6 +41,9 @@ type ResponseWriter interface { // Here is the place which we can make the last checks or do a cleanup. EndResponse() + // IsHijacked reports whether this response writer's connection is hijacked. + IsHijacked() bool + // Writef formats according to a format specifier and writes to the response. // // Returns the number of bytes written and any write error encountered. @@ -195,6 +198,15 @@ func (w *responseWriter) tryWriteHeader() { } } +// IsHijacked reports whether this response writer's connection is hijacked. +func (w *responseWriter) IsHijacked() bool { + // Note: + // A zero-byte `ResponseWriter.Write` on a hijacked connection will + // return `http.ErrHijacked` without any other side effects. + _, err := w.ResponseWriter.Write(nil) + return err == http.ErrHijacked +} + // Write writes to the client // If WriteHeader has not yet been called, Write calls // WriteHeader(http.StatusOK) before writing the data. If the Header diff --git a/doc.go b/doc.go index b40c8aa3..61c48f8a 100644 --- a/doc.go +++ b/doc.go @@ -1471,7 +1471,7 @@ Example Server Code: // see the inline javascript code i the websockets.html, this endpoint is used to connect to the server. app.Get("/echo", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(websocket.ClientSource) @@ -1554,7 +1554,7 @@ Example Code: func main() { app := iris.New() - // Optionally, add two built'n handlers + // Optionally, add two builtin handlers // that can recover from any http-relative panics // and log the requests to the terminal. app.Use(recover.New()) diff --git a/go19.go b/go19.go index 6d6c979e..aadcb525 100644 --- a/go19.go +++ b/go19.go @@ -82,7 +82,7 @@ type ( // context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie` // as their (last) variadic input argument to amend the end cookie's form. // - // Any custom or built'n `CookieOption` is valid, + // Any custom or builtin `CookieOption` is valid, // see `CookiePath`, `CookieCleanPath`, `CookieExpires` and `CookieHTTPOnly` for more. // // An alias for the `context/Context#CookieOption`. diff --git a/hero/di/func.go b/hero/di/func.go index 3c417d8f..1f30e307 100644 --- a/hero/di/func.go +++ b/hero/di/func.go @@ -144,7 +144,7 @@ func (s *FuncInjector) addValue(inputIndex int, value reflect.Value) bool { return false } -// Retry used to add missing dependencies, i.e path parameter built'n bindings if not already exists +// Retry used to add missing dependencies, i.e path parameter builtin bindings if not already exists // in the `hero.Handler`, once, only for that func injector. func (s *FuncInjector) Retry(retryFn func(inIndex int, inTyp reflect.Type) (reflect.Value, bool)) bool { for _, missing := range s.lost { diff --git a/hero/session.go b/hero/session.go index 8d008653..acc91913 100644 --- a/hero/session.go +++ b/hero/session.go @@ -1,6 +1,6 @@ package hero -// It's so easy, no need to be lived anywhere as built'n.. users should understand +// It's so easy, no need to be lived anywhere as builtin.. users should understand // how easy it's by using it. // // Session is a binder that will fill a *sessions.Session function input argument diff --git a/iris.go b/iris.go index 496c5eb1..dd788361 100644 --- a/iris.go +++ b/iris.go @@ -613,7 +613,7 @@ func (app *Application) Shutdown(ctx stdContext.Context) error { // It can be used to register a custom runner with `Run` in order // to set the framework's server listen action. // -// Currently Runner is being used to declare the built'n server listeners. +// Currently `Runner` is being used to declare the builtin server listeners. // // See `Run` for more. type Runner func(*Application) error diff --git a/middleware/README.md b/middleware/README.md index 9e09ecc2..b4402aac 100644 --- a/middleware/README.md +++ b/middleware/README.md @@ -1,4 +1,4 @@ -Built'n Handlers +Builtin Handlers ------------ | Middleware | Example | diff --git a/sessions/config.go b/sessions/config.go index 018ec139..d6880e18 100644 --- a/sessions/config.go +++ b/sessions/config.go @@ -49,7 +49,7 @@ type ( // CookieSecureTLS set to true if server is running over TLS // and you need the session's cookie "Secure" field to be setted true. // - // Note: The user should fill the Decode configuation field in order for this to work. + // Note: The user should fill the Decode configuration field in order for this to work. // Recommendation: You don't need this to be setted to true, just fill the Encode and Decode fields // with a third-party library like secure cookie, example is provided at the _examples folder. // diff --git a/view/README.md b/view/README.md index 0ce6e74f..48a900b3 100644 --- a/view/README.md +++ b/view/README.md @@ -77,7 +77,7 @@ func main() { // - amber | iris.Amber(...) tmpl := iris.HTML("./templates", ".html") - // built'n template funcs are: + // builtin template funcs are: // // - {{ urlpath "mynamedroute" "pathParameter_ifneeded" }} // - {{ render "header.html" }} diff --git a/websocket/connection.go b/websocket/connection.go index 2203e399..c6d23c20 100644 --- a/websocket/connection.go +++ b/websocket/connection.go @@ -192,8 +192,9 @@ type ( // Wait starts the pinger and the messages reader, // it's named as "Wait" because it should be called LAST, // after the "On" events IF server's `Upgrade` is used, - // otherise you don't have to call it because the `Handler()` does it automatically. + // otherwise you don't have to call it because the `Handler()` does it automatically. Wait() + // UnderlyingConn returns the underline gorilla websocket connection. UnderlyingConn() *websocket.Conn } @@ -592,6 +593,14 @@ func (c *connection) fireOnLeave(roomName string) { // after the "On" events IF server's `Upgrade` is used, // otherise you don't have to call it because the `Handler()` does it automatically. func (c *connection) Wait() { + // if c.server != nil && c.server.config.MaxConcurrentConnections > 0 { + // defer func() { + // go func() { + // c.server.threads <- struct{}{} + // }() + // }() + // } + if c.started { return } diff --git a/websocket/server.go b/websocket/server.go index da747e3e..6aaaccac 100644 --- a/websocket/server.go +++ b/websocket/server.go @@ -21,7 +21,7 @@ type ( // See `OnConnection` , to register a single event which will handle all incoming connections and // the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint. // - // To serve the built'n javascript client-side library look the `websocket.ClientHandler`. + // To serve the builtin javascript client-side library look the `websocket.ClientHandler`. Server struct { config Config // ClientSource contains the javascript side code @@ -44,10 +44,11 @@ type ( // See `OnConnection` , to register a single event which will handle all incoming connections and // the `Handler` which builds the upgrader handler that you can register to a route based on an Endpoint. // -// To serve the built'n javascript client-side library look the `websocket.ClientHandler`. +// To serve the builtin javascript client-side library look the `websocket.ClientHandler`. func New(cfg Config) *Server { cfg = cfg.Validate() - return &Server{ + + s := &Server{ config: cfg, ClientSource: bytes.Replace(ClientSource, []byte(DefaultEvtMessageKey), cfg.EvtMessagePrefix, -1), connections: sync.Map{}, // ready-to-use, this is not necessary. @@ -63,6 +64,8 @@ func New(cfg Config) *Server { EnableCompression: cfg.EnableCompression, }, } + + return s } // Handler builds the handler based on the configuration and returns it. @@ -72,7 +75,7 @@ func New(cfg Config) *Server { // // Endpoint is the path which the websocket Server will listen for clients/connections. // -// To serve the built'n javascript client-side library look the `websocket.ClientHandler`. +// To serve the builtin javascript client-side library look the `websocket.ClientHandler`. func (s *Server) Handler() context.Handler { return func(ctx context.Context) { c := s.Upgrade(ctx) @@ -104,14 +107,14 @@ func (s *Server) Handler() context.Handler { // If the upgrade fails, then Upgrade replies to the client with an HTTP error // response and the return `Connection.Err()` is filled with that error. // -// For a more high-level function use the `Handler()` and `OnConnecton` events. +// For a more high-level function use the `Handler()` and `OnConnection` events. // This one does not starts the connection's writer and reader, so after your `On/OnMessage` events registration // the caller has to call the `Connection#Wait` function, otherwise the connection will be not handled. func (s *Server) Upgrade(ctx context.Context) Connection { conn, err := s.upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), ctx.ResponseWriter().Header()) if err != nil { ctx.Application().Logger().Warnf("websocket error: %v\n", err) - ctx.StatusCode(503) // Status Service Unavailable + // ctx.StatusCode(503) // Status Service Unavailable return &connection{err: err} } @@ -150,29 +153,6 @@ func (s *Server) handleConnection(ctx context.Context, websocketConn *websocket. return c } -/* Notes: - We use the id as the signature of the connection because with the custom IDGenerator - the developer can share this ID with a database field, so we want to give the oportunnity to handle - his/her websocket connections without even use the connection itself. - - Another question may be: - Q: Why you use Server as the main actioner for all of the connection actions? - For example the Server.Disconnect(connID) manages the connection internal fields, is this code-style correct? - A: It's the correct code-style for these type of applications and libraries, Server manages all, the connnection's functions - should just do some internal checks (if needed) and push the action to its parent, which is the Server, the Server is able to - remove a connection, the rooms of its connected and all these things, so in order to not split the logic, we have the main logic - here, in the Server, and let the connection with some exported functions whose exists for the per-connection action user's code-style. - - Ok my english are s** I can feel it, but these comments are mostly for me. -*/ - -/* - connection actions, same as the connection's method, - but these methods accept the connection ID, - which is useful when the developer maps - this id with a database field (using config.IDGenerator). -*/ - // OnConnection is the main event you, as developer, will work with each of the websocket connections. func (s *Server) OnConnection(cb ConnectionFunc) { s.onConnectionListeners = append(s.onConnectionListeners, cb) diff --git a/websocket/websocket.go b/websocket/websocket.go index 1792e0dc..fff59634 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -47,7 +47,7 @@ Example code: // this endpoint is used to connect to the server. app.Get("/echo", ws.Handler()) - // serve the javascript built'n client-side library, + // serve the javascript builtin client-side library, // see websockets.html script tags, this path is used. app.Any("/iris-ws.js", func(ctx context.Context) { ctx.Write(websocket.ClientSource)