// NewConnection creates a SSMP connection out of a streaming netwrok connection. // // This method blocks until either a first message is received or a 10s timeout // elapses. // // Each accepted connection spawns a goroutine continuously reading from the // underlying network connection and triggering the Dispatcher. The caller must // keep track of the returned Connection and call the Close method to stop the // read goroutine and close the udnerlying netwrok connection. // // errInvalidLogin is returned if the first message is not a well-formed LOGIN // request. // errUnauthorized is returned if the authenticator doesn't accept the provided // credentials. func NewConnection(c net.Conn, a Authenticator, d *Dispatcher) (*Connection, error) { r := ssmp.NewDecoder(c) c.SetReadDeadline(time.Now().Add(10 * time.Second)) verb, err := r.DecodeVerb() if err != nil || !ssmp.Equal(verb, ssmp.LOGIN) { return nil, ErrInvalidLogin } user, err := r.DecodeId() if err != nil { return nil, ErrInvalidLogin } scheme, err := r.DecodeId() if err != nil { return nil, ErrInvalidLogin } var cred []byte if r.AtEnd() { cred = []byte{} } else if cred, err = r.DecodePayload(); err != nil { return nil, ErrInvalidLogin } if !a.Auth(c, user, scheme, cred) { return nil, ErrUnauthorized } r.Reset() cc := &Connection{ c: c, r: r, User: string(user), } go cc.readLoop(d) cc.Write(respOk) return cc, nil }
func (c *client) readLoop() { defer c.wg.Done() defer close(c.responses) idle := false r := ssmp.NewDecoder(c.c) for { c.c.SetReadDeadline(time.Now().Add(30 * time.Second)) code, err := r.DecodeCode() if err != nil { if nerr, ok := err.(net.Error); ok && nerr.Timeout() && !idle { idle = true c.c.Write(ping) continue } // unwrap network error if oerr, ok := err.(*net.OpError); ok { err = oerr.Err } if err != io.EOF && err.Error() != "use of closed network connection" { fmt.Printf("Client[%p] Failed to read: %v\n", c, err) } break } idle = false if code == ssmp.CodeEvent { ev, err := parseEvent(r) if err != nil { fmt.Printf("Client[%p] Invalid event: %v\n", c, err) break } r.Reset() if ssmp.Equal(ev.Name, ssmp.PING) { c.c.Write(pong) continue } if ssmp.Equal(ev.Name, ssmp.PONG) { continue } h := c.EventHandler() if h == nil { continue } h.HandleEvent(ev) continue } var payload string if !r.AtEnd() { d, err := r.DecodePayload() if err != nil { fmt.Printf("Client[%p] Invalid response: %v\n", c, err) break } payload = string(d) } r.Reset() c.responses <- Response{ Code: code, Message: payload, } } c.c.Close() }