From 680b5a09231f451a6551d2d1d5314f740fd3a537 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 25 Jan 2019 23:47:31 +0200 Subject: [PATCH] websocket: replace sync.Map with custom map[string]*connection. Add translate template function example. Fix ctx.HandlerName() does not return the end-dev-defined current route's name, this will give better warnings when using MVC in a wrong way Former-commit-id: 38fda8a20da9bc7665cdd209b7b367c1337dbd94 --- _examples/miscellaneous/i18n/main.go | 13 +++ .../miscellaneous/i18n/templates/index.html | 1 + _examples/mvc/basic/main.go | 2 + _examples/websocket/custom-go-client/run.bat | 4 + context/context.go | 9 +- websocket/server.go | 91 +++++++------------ 6 files changed, 59 insertions(+), 61 deletions(-) create mode 100644 _examples/miscellaneous/i18n/templates/index.html create mode 100644 _examples/websocket/custom-go-client/run.bat diff --git a/_examples/miscellaneous/i18n/main.go b/_examples/miscellaneous/i18n/main.go index ab7b53cc..8b0e93d9 100644 --- a/_examples/miscellaneous/i18n/main.go +++ b/_examples/miscellaneous/i18n/main.go @@ -59,6 +59,19 @@ func newApp() *iris.Application { "key2", fromSecondFileValue) }) + // using in inside your templates: + view := iris.HTML("./templates", ".html") + app.RegisterView(view) + + app.Get("/templates", func(ctx iris.Context) { + ctx.View("index.html", iris.Map{ + "tr": ctx.Translate, + }) + // it will return "hello, iris" + // when {{call .tr "hi" "iris"}} + }) + // + return app } diff --git a/_examples/miscellaneous/i18n/templates/index.html b/_examples/miscellaneous/i18n/templates/index.html new file mode 100644 index 00000000..7fc7cae8 --- /dev/null +++ b/_examples/miscellaneous/i18n/templates/index.html @@ -0,0 +1 @@ +{{call .tr "hi" "iris"}} \ No newline at end of file diff --git a/_examples/mvc/basic/main.go b/_examples/mvc/basic/main.go index b5299221..c28b06af 100644 --- a/_examples/mvc/basic/main.go +++ b/_examples/mvc/basic/main.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/kataras/iris" + "github.com/kataras/iris/middleware/recover" "github.com/kataras/iris/sessions" "github.com/kataras/iris/mvc" @@ -11,6 +12,7 @@ import ( func main() { app := iris.New() + app.Use(recover.New()) app.Logger().SetLevel("debug") mvc.Configure(app.Party("/basic"), basicMVC) diff --git a/_examples/websocket/custom-go-client/run.bat b/_examples/websocket/custom-go-client/run.bat new file mode 100644 index 00000000..3e483188 --- /dev/null +++ b/_examples/websocket/custom-go-client/run.bat @@ -0,0 +1,4 @@ +@echo off +REM run.bat 30 +start go run main.go server +for /L %%n in (1,1,%1) do start go run main.go client \ No newline at end of file diff --git a/context/context.go b/context/context.go index 0b37b8d8..7fa1a95e 100644 --- a/context/context.go +++ b/context/context.go @@ -275,7 +275,8 @@ type Context interface { // that can be used to share information between handlers and middleware. Values() *memstore.Store // Translate is the i18n (localization) middleware's function, - // it calls the Get("translate") to return the translated value. + // it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()) + // to execute the translate function and return the localized text value. // // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n Translate(format string, args ...interface{}) string @@ -1180,6 +1181,9 @@ func (ctx *context) Proceed(h Handler) bool { // HandlerName returns the current handler's name, helpful for debugging. func (ctx *context) HandlerName() string { + if name := ctx.currentRouteName; name != "" { + return name + } return HandlerName(ctx.handlers[ctx.currentHandlerIndex]) } @@ -1380,7 +1384,8 @@ func (ctx *context) Values() *memstore.Store { } // Translate is the i18n (localization) middleware's function, -// it calls the Get("translate") to return the translated value. +// it calls the Values().Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()) +// to execute the translate function and return the localized text value. // // Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n func (ctx *context) Translate(format string, args ...interface{}) string { diff --git a/websocket/server.go b/websocket/server.go index 28a25992..9bf323ad 100644 --- a/websocket/server.go +++ b/websocket/server.go @@ -44,9 +44,9 @@ type ( // app.Any("/iris-ws.js", func(ctx iris.Context) { ctx.Write(mywebsocketServer.ClientSource) }) ClientSource []byte messageSerializer *messageSerializer - connections sync.Map // key = the Connection ID. - rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name - mu sync.RWMutex // for rooms. + connections map[string]*connection // key = the Connection ID. + rooms map[string][]string // by default a connection is joined to a room which has the connection id as its name + mu sync.RWMutex // for rooms and connections. onConnectionListeners []ConnectionFunc //connectionPool sync.Pool // sadly we can't make this because the websocket connection is live until is closed. upgrader websocket.Upgrader @@ -64,7 +64,7 @@ func New(cfg Config) *Server { config: cfg, ClientSource: bytes.Replace(ClientSource, []byte(DefaultEvtMessageKey), cfg.EvtMessagePrefix, -1), messageSerializer: newMessageSerializer(cfg.EvtMessagePrefix), - connections: sync.Map{}, // ready-to-use, this is not necessary. + connections: make(map[string]*connection), rooms: make(map[string][]string), onConnectionListeners: make([]ConnectionFunc, 0), upgrader: websocket.Upgrader{ @@ -133,19 +133,14 @@ func (s *Server) Upgrade(ctx context.Context) Connection { } func (s *Server) addConnection(c *connection) { - s.connections.Store(c.id, c) + s.mu.Lock() + s.connections[c.id] = c + s.mu.Unlock() } func (s *Server) getConnection(connID string) (*connection, bool) { - if cValue, ok := s.connections.Load(connID); ok { - // this cast is not necessary, - // we know that we always save a connection, but for good or worse let it be here. - if conn, ok := cValue.(*connection); ok { - return conn, ok - } - } - - return nil, false + c, ok := s.connections[connID] + return c, ok } // wrapConnection wraps an underline connection to an iris websocket connection. @@ -290,34 +285,24 @@ func (s *Server) leave(roomName string, connID string) (left bool) { // GetTotalConnections returns the number of total connections func (s *Server) GetTotalConnections() (n int) { - s.connections.Range(func(k, v interface{}) bool { - n++ - return true - }) + s.mu.RLock() + n = len(s.connections) + s.mu.RUnlock() - return n + return } // GetConnections returns all connections func (s *Server) GetConnections() []Connection { - // first call of Range to get the total length, we don't want to use append or manually grow the list here for many reasons. - length := s.GetTotalConnections() - conns := make([]Connection, length, length) + s.mu.RLock() + conns := make([]Connection, len(s.connections)) i := 0 - // second call of Range. - s.connections.Range(func(k, v interface{}) bool { - conn, ok := v.(*connection) - if !ok { - // if for some reason (should never happen), the value is not stored as *connection - // then stop the iteration and don't continue insertion of the result connections - // in order to avoid any issues while end-dev will try to iterate a nil entry. - return false - } - conns[i] = conn + for _, c := range s.connections { + conns[i] = c i++ - return true - }) + } + s.mu.RUnlock() return conns } @@ -339,10 +324,8 @@ func (s *Server) GetConnectionsByRoom(roomName string) []Connection { if connIDs, found := s.rooms[roomName]; found { for _, connID := range connIDs { // existence check is not necessary here. - if cValue, ok := s.connections.Load(connID); ok { - if conn, ok := cValue.(*connection); ok { - conns = append(conns, conn) - } + if conn, ok := s.connections[connID]; ok { + conns = append(conns, conn) } } } @@ -382,32 +365,20 @@ func (s *Server) emitMessage(from, to string, data []byte) { } } } else { + s.mu.RLock() // it suppose to send the message to all opened connections or to all except the sender. - s.connections.Range(func(k, v interface{}) bool { - connID, ok := k.(string) - if !ok { - // should never happen. - return true - } - - if to != All && to != connID { // if it's not suppose to send to all connections (including itself) - if to == Broadcast && from == connID { // if broadcast to other connections except this + for _, conn := range s.connections { + if to != All && to != conn.id { // if it's not suppose to send to all connections (including itself) + if to == Broadcast && from == conn.id { // if broadcast to other connections except this // here we do the opossite of previous block, // just skip this connection when it's suppose to send the message to all connections except the sender. - return true + continue } - } - // not necessary cast. - conn, ok := v.(*connection) - if ok { - // send to the client(s) when the top validators passed - conn.writeDefault(data) - } - - return ok - }) + conn.writeDefault(data) + } + s.mu.RUnlock() } } @@ -432,7 +403,9 @@ func (s *Server) Disconnect(connID string) (err error) { // close the underline connection and return its error, if any. err = conn.underline.Close() - s.connections.Delete(connID) + s.mu.Lock() + delete(s.connections, conn.id) + s.mu.Unlock() } return