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
This commit is contained in:
Gerasimos (Makis) Maropoulos 2019-02-23 07:23:10 +02:00
parent bda36145e5
commit ddec78af0a
31 changed files with 124 additions and 100 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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())
}

View File

@ -134,7 +134,7 @@ func main() {
```
## Built'n Configurators
## Builtin Configurators
```go
// WithoutServerError will cause to ignore the matched "errors"

View File

@ -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())

View File

@ -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)

View File

@ -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 {

View File

@ -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())

View File

@ -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 {

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
@ -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()
@ -139,13 +140,16 @@ func main() {
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,6 +171,7 @@ func connect(wg *sync.WaitGroup, alive time.Duration) {
}
})
if alive > 0 {
go func() {
time.Sleep(alive)
if err := c.Disconnect(); err != nil {
@ -176,14 +181,18 @@ func connect(wg *sync.WaitGroup, alive time.Duration) {
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
}

View File

@ -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())
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

4
doc.go
View File

@ -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())

View File

@ -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`.

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
Built'n Handlers
Builtin Handlers
------------
| Middleware | Example |

View File

@ -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.
//

View File

@ -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" }}

View File

@ -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
}

View File

@ -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)

View File

@ -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)