// NewServer creates a new server. func NewServer(addr string) (*Server, error) { if (Conf == Config{}) { InitConf("") } s := Server{ server: neptulon.NewServer(addr), db: NewInMemDB(), queue: NewQueue(), } s.server.MiddlewareFunc(middleware.Logger) s.pubRoutes = middleware.NewRouter() s.server.Middleware(s.pubRoutes) initPubRoutes(s.pubRoutes, s.db, Conf.App.JWTPass()) //all communication below this point is authenticated s.server.MiddlewareFunc(jwt.HMAC(Conf.App.JWTPass())) s.server.Middleware(&s.queue) s.privRoutes = middleware.NewRouter() s.server.Middleware(s.privRoutes) initPrivRoutes(s.privRoutes, &s.queue) // r.Middleware(NotFoundHandler()) - 404-like handler // todo: research a better way to handle inner-circular dependencies so remove these lines back into Server contructor // (maybe via dereferencing: http://openmymind.net/Things-I-Wish-Someone-Had-Told-Me-About-Go/, but then initializers // actually using the pointer values would have to be lazy!) s.queue.SetServer(s.server) s.server.DisconnHandler(func(c *neptulon.Conn) { // only handle this event for previously authenticated if id, ok := c.Session.GetOk("userid"); ok { s.queue.RemoveConn(id.(string)) } }) return &s, nil }
// NewClient creates a new Client object. func NewClient() (*Client, error) { c, err := neptulon.NewConn() if err != nil { return nil, err } r := middleware.NewRouter() c.Middleware(r) return &Client{ ID: c.ID, Session: c.Session, conn: c, router: r, }, nil }
// InMsgHandler registers a handler to accept incoming messages from the server. func (c *Client) InMsgHandler(handler func(m []Message) error) { r := middleware.NewRouter() c.conn.Middleware(r) r.Request("msg.recv", func(ctx *neptulon.ReqCtx) error { var msg []Message if err := ctx.Params(&msg); err != nil { return fmt.Errorf("client: msg.recv: error reading request params: %v", err) } if err := handler(msg); err != nil { return err } ctx.Res = ACK return ctx.Next() }) }
func TestEcho(t *testing.T) { sh := NewServerHelper(t) route := middleware.NewRouter() sh.Server.MiddlewareFunc(middleware.Logger) sh.Server.Middleware(route) route.Request("echo", middleware.Echo) defer sh.ListenAndServe().CloseWait() ch := sh.GetConnHelper().Connect() defer ch.CloseWait() m := "Hello!" ch.SendRequestSync("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error { var msg echoMsg if err := ctx.Result(&msg); err != nil { t.Fatal(err) } if msg.Message != m { t.Fatalf("expected: %v got: %v", m, msg.Message) } return nil }) }
// Helper method for testing client implementations in other languages. // Flow of events for this function is: // * Send a {"method":"echo", "params":{"message": "..."}} request to client upon first 'echo' request from client, // and verify that message body is echoed properly in the response body. // * Echo any incoming request message body as is within a response message. // * Repeat ad infinitum, until {"method":"close", "params":"{"message": "..."}"} is received. Close message body is logged. func TestExternalClient(t *testing.T) { sh := NewServerHelper(t) sh.Server.MiddlewareFunc(middleware.Logger) var wg sync.WaitGroup m := "Hello from Neptulon server!" // handle 'echo' requests via the 'echo middleware' srout := middleware.NewRouter() sh.Server.Middleware(srout) srout.Request("echo", func(ctx *neptulon.ReqCtx) error { // send 'echo' request to client upon connection (blocks test if no response is received) wg.Add(1) ctx.Conn.SendRequest("echo", echoMsg{Message: m}, func(ctx *neptulon.ResCtx) error { defer wg.Done() var msg echoMsg if err := ctx.Result(&msg); err != nil { t.Fatal(err) } if msg.Message != m { t.Fatalf("server: expected: %v got: %v", m, msg.Message) } t.Logf("server: client sent response to our 'echo' request: %v", msg.Message) return nil }) // unmarshall incoming message into response directly if err := ctx.Params(&ctx.Res); err != nil { return err } return ctx.Next() }) // handle 'close' request (blocks test if no response is received) wg.Add(1) srout.Request("close", func(ctx *neptulon.ReqCtx) error { defer wg.Done() if err := ctx.Params(&ctx.Res); err != nil { return err } err := ctx.Next() // ctx.Conn.Close() // todo: investigate the error message!!! t.Logf("server: closed connection with message from client: %v\n", ctx.Res) return err }) defer sh.ListenAndServe().CloseWait() if *extFlog { t.Log("Started server and waiting for external client integration test since -ext flag is provided.") wg.Wait() return } // use internal conn implementation instead to test the test case itself t.Log("Skipping external client integration test since -ext flag is not provided.") ch := sh.GetConnHelper() ch.Conn.MiddlewareFunc(middleware.Logger) // handle 'echo' requests via the 'echo middleware' crout := middleware.NewRouter() ch.Conn.Middleware(crout) crout.Request("echo", middleware.Echo) defer ch.Connect().CloseWait() // handle 'echo' request and send 'close' request upon echo response mc := "Hello from Neptulon Go client!" ch.SendRequestSync("echo", echoMsg{Message: mc}, func(ctx *neptulon.ResCtx) error { var msg echoMsg if err := ctx.Result(&msg); err != nil { t.Fatal(err) } if msg.Message != mc { t.Fatalf("client: expected: %v got: %v", mc, msg.Message) } t.Log("client: server accepted and echoed 'echo' request message body") // send close request after getting our echo message back mb := "Thanks for echoing! Over and out." ch.SendRequestSync("close", echoMsg{Message: mb}, func(ctx *neptulon.ResCtx) error { var msg echoMsg if err := ctx.Result(&msg); err != nil { t.Fatal(err) } if msg.Message != mb { t.Fatalf("client: expected: %v got: %v", mb, msg.Message) } t.Log("client: server accepted and echoed 'close' request message body. bye!") return nil }) return nil }) }