From dcfdb3411a6feb90d8294db216eac269795ab075 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Fri, 27 Apr 2018 12:16:34 +0200 Subject: [PATCH] websocket frame parsing works + controller dispatch but have to be improved --- cmd/iface/main.go | 10 ++- internal/ws/reader/reader.go | 57 +++++-------- ws/frame/opcode/types.go | 2 +- ws/frame/types.go | 16 ++-- ws/private.go | 152 ++++++++++++++++++++++++++++++++++- ws/public.go | 2 +- ws/types.go | 8 +- 7 files changed, 196 insertions(+), 51 deletions(-) diff --git a/cmd/iface/main.go b/cmd/iface/main.go index 1795f53..c4cf82e 100644 --- a/cmd/iface/main.go +++ b/cmd/iface/main.go @@ -16,13 +16,19 @@ func main(){ /* (2) Bind default controller */ err := serv.BindDefault(func(c *ws.Client, f *ws.Frame){ - fmt.Printf("Default controller") + fmt.Printf("Default controller\n") + if f != nil { + fmt.Printf("Received: '%s'\n", f.Buf) + } }) if err != nil { panic(err) } /* (3) Bind to URI */ err = serv.Bind("/channel/./room/./", func(c *ws.Client, f *ws.Frame){ - fmt.Printf("URI controller") + fmt.Printf("URI controller\n") + if f != nil { + fmt.Printf("Received: '%s'\n", f.Buf) + } }) if err != nil { panic(err) } diff --git a/internal/ws/reader/reader.go b/internal/ws/reader/reader.go index 9e374f8..62472df 100644 --- a/internal/ws/reader/reader.go +++ b/internal/ws/reader/reader.go @@ -1,52 +1,37 @@ package reader import ( + "fmt" "io" - "bufio" ) -// Chunk reader -type chunkReader struct { - reader *bufio.Reader // the reader -} - - -// New creates a new reader -func NewReader(r io.Reader) (reader *chunkReader) { - - br, ok := r.(*bufio.Reader) - if !ok { - br = bufio.NewReader(r) - } - - return &chunkReader{reader: br} - -} - +// Maximum chunk size +const MaxChunkSize = 4096 // Read reads a chunk of n bytes // err is io.EOF when done -func (r *chunkReader) Read(n uint) ([]byte, error){ +func ReadBytes(r io.Reader, n uint) ([]byte, error){ - /* (1) Read line */ - chunk := make([]byte, n) - read, err := r.reader.Read(chunk) + res := make([]byte, 0, MaxChunkSize) + + totalRead := uint(0) + + // socket -> tmp( buffer) + for totalRead < n { + + tmp := make([]byte, 1) + + read, err := r.Read(tmp) + if err != nil { return nil, err } + if read == 0 { return nil, fmt.Errorf("Cannot read") } + + totalRead += uint(1) + + res = append(res, tmp[0]) - /* (2) manage errors */ - if err == io.EOF { - err = io.EOF } - if err != nil { - return chunk, err - } - - /* (1) Manage ending chunk */ - if uint(read) == 0 || len(chunk) == 0 { - return chunk, io.EOF - } - - return chunk, nil + return res, nil } \ No newline at end of file diff --git a/ws/frame/opcode/types.go b/ws/frame/opcode/types.go index 87b968f..e39199f 100644 --- a/ws/frame/opcode/types.go +++ b/ws/frame/opcode/types.go @@ -1,7 +1,7 @@ package opcode import ( - "git.xdrm.io/gws/internal/ws/frame" + "git.xdrm.io/gws/ws/frame" ) // Represents OpCode Values diff --git a/ws/frame/types.go b/ws/frame/types.go index fbeddf9..fad9e45 100644 --- a/ws/frame/types.go +++ b/ws/frame/types.go @@ -1,13 +1,19 @@ package frame +import ( + "fmt" +) + +// Represenst Frame errors +var ErrMalFormed = fmt.Errorf("Malformed Frame") + + // Represents an OpCode type OpCode byte - - // Represents a frame metadata type Header struct { - fin bool - opc OpCode - msk []byte // len: 4 if set, else empty + Fin bool + Opc OpCode + Msk []byte // len: 4 if set, else empty } \ No newline at end of file diff --git a/ws/private.go b/ws/private.go index 2ff3a9f..df67372 100644 --- a/ws/private.go +++ b/ws/private.go @@ -1,11 +1,16 @@ package ws import ( + "bytes" + "time" + "encoding/binary" + "git.xdrm.io/gws/ws/frame" + "fmt" + "git.xdrm.io/gws/internal/ws/reader" "git.xdrm.io/gws/upgrader" "net" ) - // dispatch finds a controller for a client func (s *Server) dispatch(sock net.Conn, u *upgrader.T){ @@ -70,9 +75,150 @@ func (s *Server) dispatch(sock net.Conn, u *upgrader.T){ /* (2) Add controller to client */ client.ctl = controller - /* (3) Launch controller routine */ + /* (3) Launch controller routine (for new connection) */ go controller.fun(client, nil) + /* (4) Launch continuous frame reader */ + go client.awaitFrame() + return -} \ No newline at end of file +} + + +// awaitFrame reads a websocket frame chunk by chunk +// for a given client +func (c *Client) awaitFrame() { + + // Get buffer + buf := new(bytes.Buffer) + + // Get buffer frame + frame := new(Frame) + + + for { + + var startTime int64 = time.Now().UnixNano() + + // Try to read frame header + err := frame.ReadHeader(buf, c.soc) + + if err != nil { + fmt.Printf(" - Header reading error: %s\n", err) + break + } + + + // Try to read frame payload + err = frame.ReadPayload(buf, c.soc) + if err != nil { + fmt.Printf(" - Payload reading error: %s\n", err) + break + } + + fmt.Printf("+ elapsed: %.3f us\n", float32(time.Now().UnixNano()-startTime)/1e3) + + // Call controller + c.ctl.fun(c, frame) + + } + + fmt.Printf(" - error\n") + +} + + +// ReadHeader reads the frame header +func (f *Frame) ReadHeader(buf *bytes.Buffer, s net.Conn) error{ + + /* (2) Byte 1: FIN and OpCode */ + b, err := reader.ReadBytes(s, 1) + if err != nil { return fmt.Errorf("Cannot read byte Fin nor OpCode (%s)", err) } + + f.H.Fin = b[0] & 0x80 == 0x80 + f.H.Opc = frame.OpCode( b[0] & 0x0f ) + + /* (3) Byte 2: Mask and Length[0] */ + b, err = reader.ReadBytes(s, 1) + if err != nil { return fmt.Errorf("Cannot read byte if has Mask nor Length (%s)", err) } + + // if mask, byte array not nil + if b[0] & 0x80 == 0x80 { + f.H.Msk = []byte{} + } + + // payload length + f.Len = uint64( b[0] & 0x7f ) + + /* (4) Extended payload */ + if f.Len == 127 { + + bx, err := reader.ReadBytes(s, 8) + if err != nil { return fmt.Errorf("Cannot read payload extended length of 64 bytes (%s)", err) } + + f.Len = binary.BigEndian.Uint64(bx) + + } else if f.Len == 126 { + + bx, err := reader.ReadBytes(s, 2) + if err != nil { return fmt.Errorf("Cannot read payload extended length of 16 bytes (%s)", err) } + + f.Len = uint64( binary.BigEndian.Uint16(bx) ) + + } + + /* (5) Masking key */ + if f.H.Msk != nil { + + bx, err := reader.ReadBytes(s, 4) + if err != nil { return fmt.Errorf("Cannot read mask or 32 bytes (%s)", err) } + + f.H.Msk = make([]byte, 4) + copy(f.H.Msk, bx) + + } + + // fmt.Printf(" + Header\n") + // fmt.Printf(" + FIN: %t\n", f.H.Fin) + // fmt.Printf(" + MASK?: %t\n", f.H.Msk != nil) + // if f.H.Msk != nil { + // fmt.Printf(" + MASK: %x\n", f.H.Msk) + // } + // fmt.Printf(" + LEN: %d\n", f.Len) + + return nil + +} + + +// ReadPayload reads the frame payload +func (f *Frame) ReadPayload(b *bytes.Buffer, s net.Conn) error{ + + /* (1) Read payload */ + buf, err := reader.ReadBytes(s, uint(f.Len) ) + if err != nil { return fmt.Errorf("Cannot read payload (%s)", err) } + + f.Buf = make([]byte, 0, f.Len) + + /* (2) Unmask payload */ + if len(f.H.Msk) == 4 { + + + for i, b := range buf{ + + mi := i % 4 // mask index + + f.Buf = append(f.Buf, b ^ f.H.Msk[mi]) + + } + + } + + // fmt.Printf(" + Payload\n") + // fmt.Printf(" + Read: %d\n", len(f.Buf)) + + return nil + +} + diff --git a/ws/public.go b/ws/public.go index ec897fa..4abab79 100644 --- a/ws/public.go +++ b/ws/public.go @@ -22,4 +22,4 @@ func CreateServer(host string, port uint16) *Server{ return inst -} \ No newline at end of file +} diff --git a/ws/types.go b/ws/types.go index 1a467e8..5c0240f 100644 --- a/ws/types.go +++ b/ws/types.go @@ -6,6 +6,8 @@ import ( "git.xdrm.io/gws/ws/frame" ) +const maxBufferLength = 4096 + // Represents a websocket controller callback function type ControllerFunc func(*Client, *Frame) @@ -41,7 +43,7 @@ type Server struct { // Represents a websocket frame type Frame struct { - met frame.Header - buf []byte - len uint64 + H frame.Header + Buf []byte + Len uint64 } \ No newline at end of file