diff --git a/_examples/README.md b/_examples/README.md index f9b1c3c5..44db87de 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -363,9 +363,9 @@ You can serve [quicktemplate](https://github.com/valyala/quicktemplate) and [her - [Basic Authentication](authentication/basicauth/main.go) - [OAUth2](authentication/oauth2/main.go) -- [JWT](experimental-handlers/jwt/main.go) +- [Request Auth](request/main.go) **NEW** +- [Request Auth(JWT)](experimental-handlers/jwt/main.go) - [Sessions](#sessions) -- [Request Authentication](authentication/request/main.go) **NEW** ### File Server diff --git a/_examples/authentication/README.md b/_examples/authentication/README.md index c7c132ca..4072c7b9 100644 --- a/_examples/authentication/README.md +++ b/_examples/authentication/README.md @@ -2,5 +2,6 @@ - [Basic Authentication](basicauth/main.go) - [OAUth2](oauth2/main.go) -- [JWT](https://github.com/kataras/iris/blob/master/_examples/experimental-handlers/jwt/main.go) +- [Request Auth](request/main.go) +- [Request Auth(JWT)](https://github.com/kataras/iris/blob/master/_examples/experimental-handlers/jwt/main.go) - [Sessions](https://github.com/kataras/iris/tree/master/_examples/#sessions) \ No newline at end of file diff --git a/_examples/authentication/request/main.go b/_examples/authentication/request/main.go index c097dff8..d273eb70 100644 --- a/_examples/authentication/request/main.go +++ b/_examples/authentication/request/main.go @@ -134,3 +134,5 @@ func main() { // You can read more examples and run testable code // at the `iris/crypto`, `iris/crypto/sign` // and `iris/crypto/gcm` packages themselves. +// +// See experimental-handlers/jwt example to use JWT for request authentication instead. diff --git a/_examples/experimental-handlers/jwt/main.go b/_examples/experimental-handlers/jwt/main.go index c0109268..58b945f6 100644 --- a/_examples/experimental-handlers/jwt/main.go +++ b/_examples/experimental-handlers/jwt/main.go @@ -23,7 +23,7 @@ func myHandler(ctx iris.Context) { ctx.Writef("This is an authenticated request\n") ctx.Writef("Claim content:\n") - ctx.Writef("%s", user.Signature) + ctx.Writef("%#+v\n", user.Claims) } func main() { @@ -42,5 +42,12 @@ func main() { app.Use(jwtHandler.Serve) app.Get("/ping", myHandler) + + // Example request: + // curl -X GET -H\ + // "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjozMjEzMjF9.8waEX7-vPKACa-Soi1pQvW3Rl8QY-SUFcHKTLZI4mvU"\ + // http://localhost:3001/ping + // + //Read more at: https://jwt.io/ app.Run(iris.Addr("localhost:3001")) -} // don't forget to look ../jwt_test.go to see how to set your own custom claims +} diff --git a/_examples/mvc/websocket/browser/index.html b/_examples/mvc/websocket/browser/index.html new file mode 100644 index 00000000..c7a2f910 --- /dev/null +++ b/_examples/mvc/websocket/browser/index.html @@ -0,0 +1,106 @@ + + + + Online visitors MVC example + + + + +
+ 1 online visitor +
+ + + + + + + + +

+    
+    
+    
+
+
+
+
\ No newline at end of file
diff --git a/_examples/mvc/websocket/main.go b/_examples/mvc/websocket/main.go
index c94ec5f9..9e611e38 100644
--- a/_examples/mvc/websocket/main.go
+++ b/_examples/mvc/websocket/main.go
@@ -1,41 +1,45 @@
 package main
 
 import (
+	"fmt"
 	"sync/atomic"
 
 	"github.com/kataras/iris"
 	"github.com/kataras/iris/mvc"
 	"github.com/kataras/iris/websocket"
+
+	"github.com/kataras/neffos"
 )
 
 func main() {
 	app := iris.New()
+	app.Logger().SetLevel("debug")
+
+	// optionally enable debug messages to the neffos real-time framework
+	// and print them through the iris' logger.
+	neffos.EnableDebug(app.Logger())
+
 	// load templates.
 	app.RegisterView(iris.HTML("./views", ".html"))
 
-	// render the ./views/index.html.
-	app.Get("/", func(ctx iris.Context) {
-		ctx.View("index.html")
-	})
+	// render the ./browser/index.html.
+	app.HandleDir("/", "./browser")
 
-	mvc.Configure(app.Party("/websocket"), configureMVC)
-	// Or mvc.New(app.Party(...)).Configure(configureMVC)
+	websocketAPI := app.Party("/websocket")
 
+	m := mvc.New(websocketAPI)
+	m.Register(
+		&prefixedLogger{prefix: "DEV"},
+	)
+	m.HandleWebsocket(&websocketController{Namespace: "default", Age: 42, Otherstring: "other string"})
+
+	websocketServer := neffos.New(websocket.DefaultGorillaUpgrader, m)
+
+	websocketAPI.Get("/", websocket.Handler(websocketServer))
 	// http://localhost:8080
 	app.Run(iris.Addr(":8080"))
 }
 
-func configureMVC(m *mvc.Application) {
-	ws := websocket.New(websocket.Config{})
-	// http://localhost:8080/websocket/iris-ws.js
-	m.Router.Any("/iris-ws.js", websocket.ClientHandler())
-
-	// This will bind the result of ws.Upgrade which is a websocket.Connection
-	// to the controller(s) served by the `m.Handle`.
-	m.Register(ws.Upgrade)
-	m.Handle(new(websocketController))
-}
-
 var visits uint64
 
 func increment() uint64 {
@@ -47,36 +51,74 @@ func decrement() uint64 {
 }
 
 type websocketController struct {
-	// Note that you could use an anonymous field as well, it doesn't matter, binder will find it.
-	//
-	// This is the current websocket connection, each client has its own instance of the *websocketController.
-	Conn websocket.Connection
+	*neffos.NSConn `stateless:"true"`
+	Namespace      string
+	Age            int
+	Otherstring    string
+
+	Logger LoggerService
 }
 
-func (c *websocketController) onLeave(roomName string) {
+// or
+// func (c *websocketController) Namespace() string {
+// 	return "default"
+// }
+
+func (c *websocketController) OnNamespaceDisconnect(msg neffos.Message) error {
+	c.Logger.Log("Disconnected")
 	// visits--
 	newCount := decrement()
-	// This will call the "visit" event on all clients, except the current one,
+	// This will call the "OnVisit" event on all clients, except the current one,
 	// (it can't because it's left but for any case use this type of design)
-	c.Conn.To(websocket.Broadcast).Emit("visit", newCount)
+	c.Conn.Server().Broadcast(nil, neffos.Message{
+		Namespace: msg.Namespace,
+		Event:     "OnVisit",
+		Body:      []byte(fmt.Sprintf("%d", newCount)),
+	})
+
+	return nil
 }
 
-func (c *websocketController) update() {
+func (c *websocketController) OnNamespaceConnected(msg neffos.Message) error {
+	// println("Broadcast prefix is: " + c.BroadcastPrefix)
+	c.Logger.Log("Connected")
+
 	// visits++
 	newCount := increment()
 
-	// This will call the "visit" event on all clients, including the current
+	// This will call the "OnVisit" event on all clients, including the current
 	// with the 'newCount' variable.
 	//
 	// There are many ways that u can do it and faster, for example u can just send a new visitor
 	// and client can increment itself, but here we are just "showcasing" the websocket controller.
-	c.Conn.To(websocket.All).Emit("visit", newCount)
+	c.Conn.Server().Broadcast(c, neffos.Message{
+		Namespace: msg.Namespace,
+		Event:     "OnVisit",
+		Body:      []byte(fmt.Sprintf("%d", newCount)),
+	})
+
+	return nil
 }
 
-func (c *websocketController) Get( /* websocket.Connection could be lived here as well, it doesn't matter */ ) {
-	c.Conn.OnLeave(c.onLeave)
-	c.Conn.On("visit", c.update)
+func (c *websocketController) OnChat(msg neffos.Message) error {
+	ctx := websocket.GetContext(c.Conn)
 
-	// call it after all event callbacks registration.
-	c.Conn.Wait()
+	ctx.Application().Logger().Infof("[IP: %s] [ID: %s]  broadcast to other clients the message [%s]",
+		ctx.RemoteAddr(), c, string(msg.Body))
+
+	c.Conn.Server().Broadcast(c, msg)
+
+	return nil
+}
+
+type LoggerService interface {
+	Log(string)
+}
+
+type prefixedLogger struct {
+	prefix string
+}
+
+func (s *prefixedLogger) Log(msg string) {
+	fmt.Printf("%s: %s\n", s.prefix, msg)
 }
diff --git a/_examples/mvc/websocket/views/index.html b/_examples/mvc/websocket/views/index.html
deleted file mode 100644
index ecedcff2..00000000
--- a/_examples/mvc/websocket/views/index.html
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-    Online visitors MVC example
-    
-
-
-
-    
- 1 online visitor -
- - - - - - - - \ No newline at end of file diff --git a/_examples/websocket/basic/server.go b/_examples/websocket/basic/server.go index fa8150b0..ab028ca6 100644 --- a/_examples/websocket/basic/server.go +++ b/_examples/websocket/basic/server.go @@ -68,7 +68,7 @@ func main() { // serves the endpoint of ws://localhost:8080/echo websocketRoute := app.Get("/echo", websocket.Handler(websocketServer)) - + j.Get() if enableJWT { // Register the jwt middleware (on handshake): websocketRoute.Use(j.Serve) @@ -76,20 +76,25 @@ func main() { // // Check for token through the jwt middleware // on websocket connection or on any event: - /* - websocketServer.OnConnect = func(c *neffos.Conn) error { - ctx := websocket.GetContext(c) - if err := j.CheckJWT(ctx); err != nil { - // will send the above error on the client - // and will not allow it to connect to the websocket server at all. - return err - } + /* websocketServer.OnConnect = func(c *neffos.Conn) error { + ctx := websocket.GetContext(c) + if err := j.CheckJWT(ctx); err != nil { + // will send the above error on the client + // and will not allow it to connect to the websocket server at all. + return err + } - log.Printf("[%s] connected to the server", c.ID()) + user := ctx.Values().Get("jwt").(*jwt.Token) + // or just: user := j.Get(ctx) - return nil - } - */ + log.Printf("This is an authenticated request\n") + log.Printf("Claim content:") + log.Printf("%#+v\n", user.Claims) + + log.Printf("[%s] connected to the server", c.ID()) + + return nil + } */ } // serves the browser-based websocket client. diff --git a/configuration.go b/configuration.go index e31a9c76..2a6f42f0 100644 --- a/configuration.go +++ b/configuration.go @@ -401,7 +401,7 @@ type Configuration struct { // DisablePathCorrectionRedirection works whenever configuration.DisablePathCorrection is set to false // and if DisablePathCorrectionRedirection set to true then it will fire the handler of the matching route without - // the last slash ("/") instead of send a redirection status. + // the trailing slash ("/") instead of send a redirection status. // // Defaults to false. DisablePathCorrectionRedirection bool `json:"disablePathCorrectionRedirection,omitempty" yaml:"DisablePathCorrectionRedirection" toml:"DisablePathCorrectionRedirection"` diff --git a/go.mod b/go.mod index b364ad4c..90ac4507 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/iris-contrib/go.uuid v2.0.0+incompatible github.com/json-iterator/go v1.1.6 // vendor removed. github.com/kataras/golog v0.0.0-20180321173939-03be10146386 - github.com/kataras/neffos v0.0.3 + github.com/kataras/neffos v0.0.5 github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect github.com/microcosm-cc/bluemonday v1.0.2 github.com/ryanuber/columnize v2.1.0+incompatible diff --git a/hero/di/reflect.go b/hero/di/reflect.go index e9ae06af..48fbc708 100644 --- a/hero/di/reflect.go +++ b/hero/di/reflect.go @@ -1,6 +1,8 @@ package di -import "reflect" +import ( + "reflect" +) // EmptyIn is just an empty slice of reflect.Value. var EmptyIn = []reflect.Value{} @@ -164,6 +166,13 @@ func structFieldIgnored(f reflect.StructField) bool { return s == "true" // if has an ignore tag then ignore it. } +// for controller's fields only. Explicit set a stateless to a field +// in order to make the controller a Stateless one even if no other dynamic dependencies exist. +func structFieldStateless(f reflect.StructField) bool { + s := f.Tag.Get("stateless") + return s == "true" +} + type field struct { Type reflect.Type Name string // the actual name. @@ -190,7 +199,7 @@ func lookupFields(elemTyp reflect.Type, skipUnexported bool, parentIndex []int) for i, n := 0, elemTyp.NumField(); i < n; i++ { f := elemTyp.Field(i) if IndirectType(f.Type).Kind() == reflect.Struct && - !structFieldIgnored(f) { + !structFieldIgnored(f) && !structFieldStateless(f) { fields = append(fields, lookupFields(f.Type, skipUnexported, append(parentIndex, i))...) continue } @@ -230,7 +239,8 @@ func LookupNonZeroFieldsValues(v reflect.Value, skipUnexported bool) (bindValues for _, f := range fields { if fieldVal := elem.FieldByIndex(f.Index); /*f.Type.Kind() == reflect.Ptr &&*/ - !IsZero(fieldVal) { + goodVal(fieldVal) && !IsZero(fieldVal) { + // fmt.Printf("[%d][field index = %d] append to bindValues: %s = %s\n", i, f.Index[0], f.Name, fieldVal.String()) bindValues = append(bindValues, fieldVal) } } diff --git a/hero/di/struct.go b/hero/di/struct.go index b2779765..53384c01 100644 --- a/hero/di/struct.go +++ b/hero/di/struct.go @@ -20,6 +20,22 @@ const ( Singleton ) +// read-only on runtime. +var scopeNames = map[Scope]string{ + Stateless: "Stateless", + Singleton: "Singleton", +} + +// Return "Stateless" for 0 or "Singleton" for 1. +func (scope Scope) String() string { + name, ok := scopeNames[scope] + if !ok { + return "Unknown" + } + + return name +} + type ( targetStructField struct { Object *BindObject @@ -65,6 +81,20 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, elemType: IndirectType(v.Type()), } + // Optionally check and keep good values only here, + // but not required because they are already checked by users of this function. + // + // for i, v := range values { + // if !goodVal(v) || IsZero(v) { + // if last := len(values) - 1; last > i { + // values = append(values[:i], values[i+1:]...) + // } else { + // values = values[0:last] + // } + // } + // } + + visited := make(map[int]struct{}, 0) // add a visited to not add twice a single value (09-Jul-2019). fields := lookupFields(s.elemType, true, nil) for _, f := range fields { if hijack != nil { @@ -78,15 +108,19 @@ func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, } } - for _, val := range values { + for idx, val := range values { + if _, alreadySet := visited[idx]; alreadySet { + continue + } + // the binded values to the struct's fields. b, err := MakeBindObject(val, goodFunc) - if err != nil { return s // if error stop here. } if b.IsAssignable(f.Type) { + visited[idx] = struct{}{} // fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String()) s.fields = append(s.fields, &targetStructField{ FieldIndex: f.Index, @@ -126,13 +160,14 @@ func (s *StructInjector) setState() { // if so then set the temp staticBindingsFieldsLength to that number, so for example: // if static binding length is 0 // but an unexported field is set-ed then act that as singleton. + if allStructFieldsLength > staticBindingsFieldsLength { structFieldsUnexportedNonZero := LookupNonZeroFieldsValues(s.initRef, false) staticBindingsFieldsLength = len(structFieldsUnexportedNonZero) } - // println("staticBindingsFieldsLength: ", staticBindingsFieldsLength) // println("allStructFieldsLength: ", allStructFieldsLength) + // println("staticBindingsFieldsLength: ", staticBindingsFieldsLength) // if the number of static values binded is equal to the // total struct's fields(including unexported fields this time) then set as singleton. @@ -169,9 +204,23 @@ func (s *StructInjector) fillStruct() { func (s *StructInjector) String() (trace string) { for i, f := range s.fields { elemField := s.elemType.FieldByIndex(f.FieldIndex) - trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n", - i+1, bindTypeString(f.Object.BindType), f.Object.Type.String(), - elemField.Name, elemField.Type.String()) + + format := "\t[%d] %s binding: %#+v for field '%s %s'" + if len(s.fields) > i+1 { + format += "\n" + } + + if !f.Object.Value.IsValid() { + continue // probably a Context. + } + + valuePresent := f.Object.Value.Interface() + + if f.Object.BindType == Dynamic { + valuePresent = f.Object.Type.String() + } + + trace += fmt.Sprintf(format, i+1, bindTypeString(f.Object.BindType), valuePresent, elemField.Name, elemField.Type.String()) } return diff --git a/hero/di/values.go b/hero/di/values.go index 1033b957..0ffdd23f 100644 --- a/hero/di/values.go +++ b/hero/di/values.go @@ -29,7 +29,22 @@ func (bv Values) CloneWithFieldsOf(s interface{}) Values { // add the manual filled fields to the dependencies. filledFieldValues := LookupNonZeroFieldsValues(ValueOf(s), true) + + for i, filled := range filledFieldValues { + for _, v := range values { + // do NOT keep duplicate equal values (09-Jul-2019). + if reflect.DeepEqual(v, filled) { + if last := len(filledFieldValues) - 1; last > i { + filledFieldValues = append(filledFieldValues[:i], filledFieldValues[i+1:]...) + } else { + filledFieldValues = filledFieldValues[0:last] + } + break + } + } + } values = append(values, filledFieldValues...) + return values } diff --git a/mvc/controller.go b/mvc/controller.go index c2121dc4..ef3813a7 100644 --- a/mvc/controller.go +++ b/mvc/controller.go @@ -87,8 +87,11 @@ type ControllerActivator struct { errorHandler hero.ErrorHandler - // initialized on the first `Handle`. + // initialized on the first `Handle` or immediately when "servesWebsocket" is true. injector *di.StructInjector + + // true if this controller listens and serves to websocket events. + servesWebsocket bool } // NameOf returns the package name + the struct type's name, @@ -145,6 +148,11 @@ func whatReservedMethods(typ reflect.Type) map[string]*router.Route { return routes } +func (c *ControllerActivator) markAsWebsocket() { + c.servesWebsocket = true + c.attachInjector() +} + // Dependencies returns the write and read access of the dependencies that are // came from the parent MVC Application, with this you can customize // the dependencies per controller, used at the `BeforeActivation`. @@ -325,6 +333,21 @@ func (c *ControllerActivator) Handle(method, path, funcName string, middleware . var emptyIn = []reflect.Value{} +func (c *ControllerActivator) attachInjector() { + if c.injector == nil { + c.injector = di.Struct(c.Value, c.dependencies...) + if !c.servesWebsocket { + golog.Debugf("MVC Controller [%s] [Scope=%s]", c.fullName, c.injector.Scope) + } else { + golog.Debugf("MVC Websocket Controller [%s]", c.fullName) + } + + if c.injector.Has { + golog.Debugf("Dependencies:\n%s", c.injector.String()) + } + } +} + func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []reflect.Value) context.Handler { // Remember: // The `Handle->handlerOf` can be called from `BeforeActivation` event @@ -333,12 +356,7 @@ func (c *ControllerActivator) handlerOf(m reflect.Method, funcDependencies []ref // To solve this we're doing a check on the FIRST `Handle`, // if c.injector is nil, then set it with the current bindings, // these bindings can change after, so first add dependencies and after register routes. - if c.injector == nil { - c.injector = di.Struct(c.Value, c.dependencies...) - if c.injector.Has { - golog.Debugf("MVC dependencies of '%s':\n%s", c.fullName, c.injector.String()) - } - } + c.attachInjector() // fmt.Printf("for %s | values: %s\n", funcName, funcDependencies) diff --git a/mvc/mvc.go b/mvc/mvc.go index da438c63..4b18e303 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -1,12 +1,14 @@ package mvc import ( + "reflect" "strings" "github.com/kataras/iris/context" "github.com/kataras/iris/core/router" "github.com/kataras/iris/hero" "github.com/kataras/iris/hero/di" + "github.com/kataras/iris/websocket" "github.com/kataras/golog" ) @@ -172,6 +174,52 @@ Set the Logger's Level to "debug" to view the active dependencies per controller // // Examples at: https://github.com/kataras/iris/tree/master/_examples/mvc func (app *Application) Handle(controller interface{}) *Application { + app.handle(controller) + return app +} + +// HandleWebsocket handles a websocket specific controller. +// Its exported methods are the events. +// If a "Namespace" field or method exists then namespace is set, otherwise empty namespace. +// Note that a websocket controller is registered and ran under a specific connection connected to a namespace +// and it cannot send HTTP responses on that state. +// However all static and dynamic dependency injection features are working, as expected, like any regular MVC Controller. +func (app *Application) HandleWebsocket(controller interface{}) { + c := app.handle(controller) + c.markAsWebsocket() +} + +var _ websocket.ConnHandler = (*Application)(nil) + +// GetNamespaces completes the websocket ConnHandler interface. +// It returns a collection of namespace and events that +// were registered through `HandleWebsocket` controllers. +func (app *Application) GetNamespaces() websocket.Namespaces { + makeInjector := func(injector *di.StructInjector) websocket.StructInjector { + return func(_ reflect.Type, nsConn *websocket.NSConn) reflect.Value { + v := injector.Acquire() + if injector.CanInject { + injector.InjectElem(v.Elem(), reflect.ValueOf(websocket.GetContext(nsConn.Conn))) + } + return v + } + } + + var websocketControllers []websocket.ConnHandler + + for _, c := range app.Controllers { + if c.servesWebsocket { + wsInjector := makeInjector(c.injector) + s := websocket.NewStruct(c.Value).SetInjector(wsInjector) + websocketControllers = append(websocketControllers, s) + + } + } + + return websocket.JoinConnHandlers(websocketControllers...).GetNamespaces() +} + +func (app *Application) handle(controller interface{}) *ControllerActivator { // initialize the controller's activator, nothing too magical so far. c := newControllerActivator(app.Router, controller, app.Dependencies, app.ErrorHandler) @@ -193,7 +241,7 @@ func (app *Application) Handle(controller interface{}) *Application { } app.Controllers = append(app.Controllers, c) - return app + return c } // HandleError registers a `hero.ErrorHandlerFunc` which will be fired when diff --git a/mvc/session_controller.go b/mvc/session_controller.go deleted file mode 100644 index a4885fa5..00000000 --- a/mvc/session_controller.go +++ /dev/null @@ -1,48 +0,0 @@ -package mvc - -import ( - "github.com/kataras/iris/context" - "github.com/kataras/iris/sessions" -) - -var defaultSessionManager = sessions.New(sessions.Config{}) - -// SessionController is a simple `Controller` implementation -// which requires a binded session manager in order to give -// direct access to the current client's session via its `Session` field. -// -// SessionController is deprecated please use the new dependency injection's methods instead, -// i.e `mvcApp.Register(sessions.New(sessions.Config{}).Start)`. -// It's more controlled by you, -// also *sessions.Session type can now `Destroy` itself without the need of the manager, embrace it. -type SessionController struct { - Manager *sessions.Sessions - Session *sessions.Session -} - -// BeforeActivation called, once per application lifecycle NOT request, -// every single time the dev registers a specific SessionController-based controller. -// It makes sure that its "Manager" field is filled -// even if the caller didn't provide any sessions manager via the MVC's Application's `Handle` function. -func (s *SessionController) BeforeActivation(b BeforeActivation) { - if didntBindManually := b.Dependencies().AddOnce(defaultSessionManager); didntBindManually { - b.Router().GetReporter().Add( - `MVC SessionController: couldn't find any "*sessions.Sessions" bindable value to fill the "Manager" field, - therefore this controller is using the default sessions manager instead. - Please refer to the documentation to learn how you can provide the session manager`) - } -} - -// BeginRequest initializes the current user's Session. -func (s *SessionController) BeginRequest(ctx context.Context) { - if s.Manager == nil { - ctx.Application().Logger().Errorf(`MVC SessionController: sessions manager is nil, report this as a bug -because the SessionController should predict this on its activation state and use a default one automatically`) - return - } - - s.Session = s.Manager.Start(ctx) -} - -// EndRequest is here to complete the `BaseController`. -func (s *SessionController) EndRequest(ctx context.Context) {} diff --git a/websocket/websocket.go b/websocket/websocket.go index 893c8e2e..c771af7d 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -53,13 +53,15 @@ var ( // // See examples for more. Dial = neffos.Dial - // IsTryingToReconnect reports whether the returning "err" from the `Server#Upgrade` // is from a client that was trying to reconnect to the websocket server. // // Look the `Conn#WasReconnected` and `Conn#ReconnectTries` too. IsTryingToReconnect = neffos.IsTryingToReconnect - + // NewStruct returns the `Struct` Conn Handler based on ptr value. + NewStruct = neffos.NewStruct + // JoinConnHandlers combines two or more ConnHandlers as one. + JoinConnHandlers = neffos.JoinConnHandlers // OnNamespaceConnect is the event name which its callback is fired right before namespace connect, // if non-nil error then the remote connection's `Conn.Connect` will fail and send that error text. // Connection is not ready to emit data to the namespace. diff --git a/websocket/websocket_go19.go b/websocket/websocket_go19.go index af0f3c57..b787fbb0 100644 --- a/websocket/websocket_go19.go +++ b/websocket/websocket_go19.go @@ -39,7 +39,8 @@ type ( // i.e on `OnNamespaceConnect` it will abort a remote namespace connection. // See examples for more. MessageHandlerFunc = neffos.MessageHandlerFunc - + // ConnHandler is the interface which namespaces and events can be retrieved through. + ConnHandler = neffos.ConnHandler // Events completes the `ConnHandler` interface. // It is a map which its key is the event name // and its value the event's callback. @@ -63,6 +64,11 @@ type ( // // See `New` and `Dial`. WithTimeout = neffos.WithTimeout + // Struct completes the `ConnHandler` interface. + // It uses a structure to register a specific namespace and its events. + Struct = neffos.Struct + // StructInjector can be used to customize the value creation that can is used on serving events. + StructInjector = neffos.StructInjector // The Message is the structure which describes the incoming and outcoming data. // Emitter's "body" argument is the `Message.Body` field. // Emitter's return non-nil error is the `Message.Err` field.