2018-05-03 17:31:09 +00:00
|
|
|
package ws
|
|
|
|
|
|
|
|
import (
|
2018-05-03 19:10:02 +00:00
|
|
|
"time"
|
2018-05-03 17:31:09 +00:00
|
|
|
"bufio"
|
|
|
|
"encoding/binary"
|
2018-05-03 17:50:30 +00:00
|
|
|
"git.xdrm.io/gws/internal/http/upgrade/request"
|
2018-05-03 17:31:09 +00:00
|
|
|
"net"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Represents a client socket utility (reader, writer, ..)
|
|
|
|
type clientIO struct {
|
|
|
|
sock net.Conn
|
|
|
|
reader *bufio.Reader
|
2018-05-03 19:10:02 +00:00
|
|
|
kill chan<- *client // unregisters client
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Represents all channels that need a client
|
|
|
|
type clientChannelSet struct{
|
|
|
|
receive chan Message
|
|
|
|
send chan *Message
|
|
|
|
}
|
|
|
|
|
|
|
|
// Represents a websocket client
|
|
|
|
type client struct {
|
|
|
|
io clientIO
|
|
|
|
iface *Client
|
|
|
|
ch clientChannelSet
|
2018-05-03 19:10:02 +00:00
|
|
|
status MessageError // close status ; 0 = nothing ; else -> must close
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create creates a new client
|
|
|
|
func buildClient(s net.Conn, ctl ControllerSet, serverCh serverChannelSet) (*client, error){
|
|
|
|
|
|
|
|
/* (1) Manage UPGRADE request
|
|
|
|
---------------------------------------------------------*/
|
2018-05-03 17:50:30 +00:00
|
|
|
/* (1) Parse request */
|
|
|
|
req, _ := request.Parse(s)
|
|
|
|
|
|
|
|
/* (3) Build response */
|
|
|
|
res := req.BuildResponse()
|
|
|
|
|
|
|
|
/* (4) Write into socket */
|
|
|
|
_, err := res.Send(s)
|
2018-05-03 17:31:09 +00:00
|
|
|
if err != nil {
|
2018-05-03 17:50:30 +00:00
|
|
|
return nil, fmt.Errorf("Upgrade write error: %s", err)
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
2018-05-03 17:50:30 +00:00
|
|
|
if res.GetStatusCode() != 101 {
|
2018-05-03 17:31:09 +00:00
|
|
|
s.Close()
|
2018-05-03 17:50:30 +00:00
|
|
|
return nil, fmt.Errorf("Upgrade error (HTTP %d)\n", res.GetStatusCode())
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* (2) Initialise client
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Get upgrade data */
|
2018-05-03 17:50:30 +00:00
|
|
|
clientURI := req.GetURI()
|
|
|
|
clientProtocol := res.GetProtocol()
|
2018-05-03 17:31:09 +00:00
|
|
|
|
|
|
|
/* (2) Initialise client */
|
|
|
|
instance := &client{
|
|
|
|
io: clientIO{
|
|
|
|
sock: s,
|
|
|
|
reader: bufio.NewReader(s),
|
|
|
|
kill: make(chan<- *client, 1),
|
|
|
|
},
|
|
|
|
|
|
|
|
iface: &Client{
|
|
|
|
Protocol: string(clientProtocol),
|
|
|
|
Arguments: [][]string{ []string{ clientURI } },
|
|
|
|
},
|
|
|
|
|
|
|
|
ch: clientChannelSet{
|
|
|
|
receive: make(chan Message, 1),
|
|
|
|
send: make(chan *Message, 1),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* (3) Find controller by URI
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Try to find one */
|
|
|
|
controller, arguments := ctl.Match(clientURI);
|
|
|
|
|
|
|
|
/* (2) If nothing found -> error */
|
|
|
|
if controller == nil {
|
|
|
|
return nil, fmt.Errorf("No controller found, no default controller set\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (3) Copy arguments */
|
|
|
|
instance.iface.Arguments = arguments
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* (4) Launch client routines
|
|
|
|
---------------------------------------------------------*/
|
|
|
|
/* (1) Launch client controller */
|
2018-05-03 19:10:02 +00:00
|
|
|
go controller.Fun(
|
2018-05-03 17:31:09 +00:00
|
|
|
instance.iface, // pass the client
|
|
|
|
instance.ch.receive, // the receiver
|
|
|
|
instance.ch.send, // the sender
|
|
|
|
serverCh.broadcast, // broadcast sender
|
|
|
|
)
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (2) Launch message reader */
|
2018-05-03 17:31:09 +00:00
|
|
|
go instance.reader()
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (3) Launc writer */
|
2018-05-03 17:31:09 +00:00
|
|
|
go instance.writer()
|
|
|
|
|
|
|
|
return instance, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reader reads and parses messages from the buffer
|
|
|
|
func (c *client) reader(){
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (1) If error code -> close */
|
|
|
|
if c.status != NONE {
|
|
|
|
c.close(c.status)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Wait for available data */
|
2018-05-04 05:34:58 +00:00
|
|
|
// c.io.sock.SetReadDeadline(time.Now().Add(10*time.Microsecond))
|
|
|
|
// _, err := c.io.reader.Peek(1)
|
2018-05-03 19:10:02 +00:00
|
|
|
|
2018-05-04 05:34:58 +00:00
|
|
|
// // timeout -> continune checking
|
|
|
|
// if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
|
|
|
|
// time.Sleep(10*time.Microsecond)
|
|
|
|
// continue
|
|
|
|
// }
|
2018-05-03 19:10:02 +00:00
|
|
|
|
2018-05-04 05:34:58 +00:00
|
|
|
// // another error -> stop reading
|
|
|
|
// if err != nil {
|
|
|
|
// break
|
|
|
|
// }
|
2018-05-03 19:10:02 +00:00
|
|
|
|
2018-05-04 05:34:58 +00:00
|
|
|
// c.io.sock.SetReadDeadline(time.Time{}) // remove timeout
|
2018-05-03 19:10:02 +00:00
|
|
|
|
|
|
|
/* (3) Parse message */
|
2018-05-03 17:31:09 +00:00
|
|
|
msg, err := readMessage(c.io.reader)
|
|
|
|
if err != nil {
|
|
|
|
// fmt.Printf(" [reader] %s\n", err)
|
|
|
|
c.close(NORMAL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (4) CLOSE */
|
2018-05-03 17:31:09 +00:00
|
|
|
if msg.Type == CLOSE {
|
|
|
|
c.close(NORMAL)
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (5) PING size error */
|
2018-05-03 17:31:09 +00:00
|
|
|
if msg.Type == PING && msg.Size > 125 {
|
|
|
|
c.close(PROTOCOL_ERR)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (6) Send PONG */
|
2018-05-03 17:31:09 +00:00
|
|
|
if msg.Type == PING {
|
|
|
|
msg.Final = true
|
|
|
|
msg.Type = PONG
|
|
|
|
c.ch.send <- msg
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (7) Unknown opcode */
|
2018-05-03 17:31:09 +00:00
|
|
|
if msg.Type != TEXT && msg.Type != BINARY {
|
|
|
|
c.close(PROTOCOL_ERR)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (7) Dispatch to receiver */
|
2018-05-03 17:31:09 +00:00
|
|
|
c.ch.receive <- *msg
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (8) close channel */
|
2018-05-03 17:31:09 +00:00
|
|
|
c.close(NORMAL)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// writer writes into websocket
|
|
|
|
// and is triggered by client.ch.send channel
|
|
|
|
func (c *client) writer(){
|
|
|
|
|
|
|
|
for msg := range c.ch.send {
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (1) If empty message -> close properly */
|
|
|
|
if msg == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Send message */
|
2018-05-03 17:31:09 +00:00
|
|
|
err := msg.Send(c.io.sock)
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (3) Fail on error */
|
2018-05-03 17:31:09 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf(" [writer] %s\n", err)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-03 19:10:02 +00:00
|
|
|
/* (4) proper close */
|
2018-05-03 17:31:09 +00:00
|
|
|
c.close(NORMAL)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// close writes the error message (if needed)
|
|
|
|
// and it closes the socket
|
|
|
|
func (c *client) close(status MessageError){
|
|
|
|
|
|
|
|
/* (1) If error status -> send close frame */
|
|
|
|
if status != NONE {
|
|
|
|
|
|
|
|
/* Create message */
|
|
|
|
msg := &Message{
|
|
|
|
Final: true,
|
|
|
|
Type: CLOSE,
|
|
|
|
Data: make([]byte, 8),
|
|
|
|
}
|
|
|
|
binary.BigEndian.PutUint16(msg.Data, uint16(status))
|
2018-05-04 05:34:58 +00:00
|
|
|
msg.Data = append(msg.Data, []byte(" close")...)
|
2018-05-03 17:31:09 +00:00
|
|
|
msg.Size = uint( len(msg.Data) )
|
|
|
|
|
|
|
|
/* Send message */
|
|
|
|
msg.Send(c.io.sock)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Close socket */
|
2018-05-04 05:34:58 +00:00
|
|
|
c.io.sock.SetReadDeadline(time.Now())
|
2018-05-03 19:10:02 +00:00
|
|
|
time.Sleep(time.Second * 3)
|
2018-05-03 17:31:09 +00:00
|
|
|
c.io.sock.Close()
|
|
|
|
|
|
|
|
|
|
|
|
/* (4) Unregister */
|
|
|
|
c.io.kill <- c
|
|
|
|
|
|
|
|
}
|