diff --git a/.gitignore b/.gitignore index 50a823f..88dbbc7 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ fabric.properties # .idea/misc.xml # *.ipr +.idea/ # Sonarlint plugin # https://plugins.jetbrains.com/plugin/7973-sonarlint .idea/**/sonarlint/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 73f69e0..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index a82482f..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/network.iml b/.idea/network.iml deleted file mode 100644 index 5e764c4..0000000 --- a/.idea/network.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 8e8bbac..0179882 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Fusion -`Fusion` is a library which implements the `NWC-Protocol`. The `NWC-Protocol` is proprietary networking protocol which uses a small and lightweight header with a performance as fast as raw tcp performance. +`Fusion` is a library which implements the `Fusion Framing Protocol (FFP)`. +The `Fusion Framing Protocol (FFP)` is proprietary networking protocol which uses a small and lightweight header with a performance as fast as raw tcp performance. Built directly on top of Go's `net.Listener` with support for plain tcp and tls encrypted connections. -The implementation for the Client is [NetworKit](https://github.com/Vinz1911/networkit) written in swift on top of `Network.framework` to ensure maximum performance. +The implementation for the Client is [FusionKit](https://github.com/Vinz1911/fusionkit) written in swift on top of `Network.framework` to ensure maximum performance. ## License: [![License](https://img.shields.io/badge/license-GPLv3-blue.svg?longCache=true&style=flat)](https://github.com/Vinz1911/network-go/blob/main/LICENSE) @@ -34,11 +35,11 @@ listener := network.Listener{} listener.Ready = func(socket net.Conn) { } // listener received message from a connection -listener.Message = func(conn net.Conn, text *string, binary []byte) { +listener.Message = func(conn net.Conn, binary []byte, opcode uint8) { // message is text based - if text != nil { } + if opcode == network.TextMessage { println(string(binary)) } // message is binary based - if binary != nil { } + if binary == network.BinaryMessage { println(len(binary)) } } // listener connection failed @@ -48,7 +49,7 @@ listener.Failed = func(conn net.Conn, err error) { } listener.Cancelled = func(conn net.Conn) { } // start listener -err := listener.Start(network.TCPConnection, uint16(8080)) +err := listener.Start(network.TCPConnection, uint16(7878)) ``` ## Author: diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4ab156a --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/network/frame.go b/network/frame.go deleted file mode 100644 index db4ba2f..0000000 --- a/network/frame.go +++ /dev/null @@ -1,101 +0,0 @@ -// Package network - -// Copyright 2021 Vinzenz Weist. All rights reserved. -// Use of this source code is risked by yourself. -// license that can be found in the LICENSE file. - -package main - -import ( - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "errors" -) - -// frame is message creator & parser -// fast and reliable -type frame struct { - buffer []byte -} - -const ( - opcodeByteCount uint32 = 0x1 - controlByteCount uint32 = 0x5 - overheadByteCount uint32 = 0x25 - frameByteCount uint32 = 0xFFFFFFFF -) - -const ( - hashMismatch string = "message hash does not match" - parsingFailed string = "message parsing failed" - readBufferOverflow string = "read buffer overflow" - writeBufferOverflow string = "write buffer overflow" -) - -// create is for creating compliant message frames -// returns message as data frame -func (*frame) create(data []byte, opcode uint8) (message []byte, err error) { - if uint32(len(data)) > frameByteCount - overheadByteCount { return nil, errors.New(writeBufferOverflow) } - var frame []byte - var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + overheadByteCount) - frame = append(frame, opcode) - frame = append(frame, length...); var hash = sha256.Sum256(frame[:controlByteCount]) - frame = append(frame, hash[:]...) - frame = append(frame, data...) - return frame, nil -} - -// parse is for parsing data back into compliant messages -// returns parsed data as 'Data' or 'String' -func (frame *frame) parse(data []byte, completion func(text *string, data []byte, ping []byte)) error { - frame.buffer = append(frame.buffer, data...) - var length = frame.extractSize() - if length == nil { return nil } - if uint32(len(frame.buffer)) > frameByteCount { return errors.New(readBufferOverflow) } - if uint32(len(frame.buffer)) < overheadByteCount { return nil } - if len(frame.buffer) < *length { return nil } - for len(frame.buffer) >= *length && *length != 0 { - var digest = sha256.Sum256(frame.buffer[:controlByteCount]) - if hex.EncodeToString(digest[:]) != *frame.extractHash() { return errors.New(hashMismatch) } - var bytes, err = frame.extractMessage(frame.buffer) - if err != nil { return err } - switch frame.buffer[0] { - case TextMessage: var result = string(bytes); completion(&result, nil, nil) - case BinaryMessage: completion(nil, bytes, nil) - case PingMessage: completion(nil, nil, bytes) - default: return errors.New(parsingFailed) } - if len(frame.buffer) <= *length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[*length:] } - } - return nil -} - -// MARK: - Private API - - -// extract the message hash from the data -// if not possible it returns nil -func (frame *frame) extractHash() *string { - if uint32(len(frame.buffer)) < overheadByteCount { return nil } - var hash = frame.buffer[controlByteCount:overheadByteCount] - var digest = hex.EncodeToString(hash[:]) - return &digest -} - -// extract the message frame size from the data -// if not possible it returns zero -func (frame *frame) extractSize() *int { - if uint32(len(frame.buffer)) < overheadByteCount { return nil } - var size = frame.buffer[opcodeByteCount:controlByteCount] - var length = int(binary.BigEndian.Uint32(size)) - return &length -} - -// extract the message and remove the overhead -// if not possible it returns nil -func (frame *frame) extractMessage(data []byte) (message []byte, err error) { - if uint32(len(data)) < overheadByteCount { return nil, errors.New(parsingFailed) } - var length = frame.extractSize() - if length == nil { return nil, errors.New(parsingFailed) } - if uint32(*length) < overheadByteCount { return nil, errors.New(parsingFailed) } - return data[overheadByteCount:*length], nil -} diff --git a/network/framer.go b/network/framer.go new file mode 100644 index 0000000..5e12efa --- /dev/null +++ b/network/framer.go @@ -0,0 +1,85 @@ +// Fusion +// +// Created by Vinzenz Weist on 17.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// + +// Package network encapsulates functionalities related to network communication. +// It provides a structure and associated methods to create and parse data frames +// for messaging across the fusion network protocol ensuring reliability and structure. +package network + +import ( + "encoding/binary" + "errors" +) + +// frame is a type that encapsulates operations related to the creation and parsing +// of message frames over the network. It aims to ensure fast and reliable communication +// by adhering to a specified frame structure. +type framer struct { + buffer []byte +} + +// Predefined constants to maintain consistency in frame size and errors. +const ( + opcodeConstant uint32 = 0x1 + controlConstant uint32 = 0x5 + frameConstant uint32 = 0xFFFFFFFF +) + +// Predefined error messages for common frame parsing issues. +const ( + parsingFailed string = "message parsing failed" + readBufferOverflow string = "read buffer overflow" + writeBufferOverflow string = "write buffer overflow" + sizeExtractionFailed string = "size extraction failed" +) + +// create is a method to construct a compliant frame for sending a message. +// It takes in the message data and an opcode indicating the type of message. +// It returns a slice of bytes representing the framed message or an error if the frame could not be created. +func (*framer) create(data []byte, opcode uint8) (message []byte, err error) { + if uint32(len(data)) > frameConstant - controlConstant { return nil, errors.New(writeBufferOverflow) } + var frame []byte + var length = make([]byte, 0x4); binary.BigEndian.PutUint32(length, uint32(len(data)) + controlConstant) + frame = append(frame, opcode) + frame = append(frame, length...) + frame = append(frame, data...) + return frame, nil +} + +// parse is a method for converting a sequence of bytes received over the network back into a structured message. +// It appends the incoming data to the frame's buffer and attempts to extract complete messages based on the frame structure. +// The completion callback is called with the parsed data and opcode when a message is successfully parsed. +func (frame *framer) parse(data []byte, completion func(data []byte, opcode uint8)) error { + frame.buffer = append(frame.buffer, data...) + var length, err = frame.extractSize(); if err != nil { return nil } + if len(frame.buffer) > int(frameConstant) { return errors.New(readBufferOverflow) } + if len(frame.buffer) < int(controlConstant) { return nil }; if len(frame.buffer) < length { return nil } + for len(frame.buffer) >= length && length != 0 { + var bytes, err = frame.extractMessage(length); if err != nil { return err } + switch frame.buffer[0] { + case TextMessage: completion(bytes, TextMessage) + case BinaryMessage: completion(bytes, BinaryMessage) + case pingMessage: completion(bytes, pingMessage) + default: return errors.New(parsingFailed) } + if len(frame.buffer) <= length { frame.buffer = []byte{} } else { frame.buffer = frame.buffer[length:] } + }; return nil +} + +// extractSize attempts to determine the size of the next message in the buffer. +// It returns the size as an integer and an error if the size cannot be determined. +func (frame *framer) extractSize() (length int, err error) { + if len(frame.buffer) < int(controlConstant) { return 0x0, errors.New(sizeExtractionFailed) } + var size = frame.buffer[opcodeConstant:controlConstant] + return int(binary.BigEndian.Uint32(size)), nil +} + +// extractMessage attempts to extract the message from the frame buffer based on the input length. +// It returns the extracted message as byte array and an error if the message cannot be extracted. +func (frame *framer) extractMessage(length int) (message []byte, err error) { + if len(frame.buffer) < int(controlConstant) { return nil, errors.New(parsingFailed) } + if length < int(controlConstant) { return nil, errors.New(parsingFailed) } + return frame.buffer[controlConstant:length], nil +} \ No newline at end of file diff --git a/network/framer_test.go b/network/framer_test.go new file mode 100644 index 0000000..062d50a --- /dev/null +++ b/network/framer_test.go @@ -0,0 +1,36 @@ +package network + +import ( + "bytes" + "testing" +) + +// TestFramerText is for testing the frame creator and parser. +// Includes testing for multiple message extractions from single byte buffer. +func TestFramerText(test *testing.T) { + framer := framer{} + message := "Hello World!" + var bytesBlock []byte + data, err := framer.create([]byte(message), TextMessage) + if err != nil { test.Errorf("parsing failed") } + for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } + err = framer.parse(bytesBlock, func(data []byte, opcode uint8) { + if opcode == TextMessage { if string(data) != message { test.Errorf("parsing failed") } } + }) + if err != nil { test.Errorf("parsing failed") } +} + +// TestFramerBinary is for testing the frame creator and parser. +// Includes testing for multiple message extractions from single byte buffer. +func TestFramerBinary(test *testing.T) { + framer := framer{} + message := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + var bytesBlock []byte + data, err := framer.create(message, BinaryMessage) + if err != nil { test.Errorf("parsing failed") } + for i := 0; i < 10000; i++ { bytesBlock = append(bytesBlock, data...) } + err = framer.parse(bytesBlock, func(data []byte, opcode uint8) { + if opcode != BinaryMessage { if !bytes.Equal(data, message) { test.Errorf("parsing failed") } } + }) + if err != nil { test.Errorf("parsing failed") } +} \ No newline at end of file diff --git a/network/listener.go b/network/listener.go index 33d3379..51677c4 100644 --- a/network/listener.go +++ b/network/listener.go @@ -1,10 +1,14 @@ -// Package main - -// Copyright 2021 Vinzenz Weist. All rights reserved. -// Use of this source code is risked by yourself. -// license that can be found in the LICENSE file. - -package main +// Fusion +// +// Created by Vinzenz Weist on 17.06.21. +// Copyright © 2021 Vinzenz Weist. All rights reserved. +// + +// Package network encapsulates the logic required to set up network connections +// and communication, including TCP and TLS encrypted connections, along with handling +// different types of messages such as text and binary. +// It uses the fusion network protocol to ensuring reliability and structure. +package network import ( "crypto/tls" @@ -12,117 +16,104 @@ import ( "io" "net" "strconv" + "time" ) +// Predefined constants for identifying connection and message types. const ( TCPConnection uint8 = 0x0 TLSConnection uint8 = 0x1 TextMessage uint8 = 0x1 BinaryMessage uint8 = 0x2 - PingMessage uint8 = 0x3 ) -// Listener is a tcp based connection listener -// this is for handling incoming pure tcp connections -type Listener struct { - frame frame - listener net.Listener +// Private predefined constant for the maximum buffer size and pingMessage. +const ( + maximum uint32 = 0x8000 + pingMessage uint8 = 0x3 +) - Cert string - Key string +// Listener struct represents a TCP based connection listener that handles incoming +// pure TCP connections or TLS encrypted connections. +type Listener struct { + framer framer + listener net.Listener + TLSConfig *tls.Config - Ready func(conn net.Conn) - Message func(conn net.Conn, text *string, binary []byte) - Failed func(conn net.Conn, err error) - Cancelled func(conn net.Conn) + Ready func(conn net.Conn) + Message func(conn net.Conn, data []byte, opcode uint8) + Failed func(err error) } -// Start the NetworkGO connection listener -// waits for incoming connections -func (listener *Listener) Start(parameter uint8, port uint16) error { +// Start initiates the listener to start accepting incoming connections on the specified port. +// The parameter decides whether it's a TCP or TLS connection based on predefined constants. +// The interrupt defines the duration after a connection gets kicked, zero means infinity. +func (listener *Listener) Start(parameter uint8, port uint16, interrupt time.Duration) (err error) { switch parameter { - case TCPConnection: - var err error - listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) - if err != nil { return err } + case TCPConnection: listener.listener, err = net.Listen("tcp", ":" + strconv.Itoa(int(port))) case TLSConnection: - var cer, err = tls.LoadX509KeyPair(listener.Cert, listener.Key) - if err != nil { return err } - var config = &tls.Config{Certificates: []tls.Certificate{cer}} - listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), config) - if err != nil { return err } + if listener.TLSConfig == nil { return errors.New("empty tls config") } + listener.listener, err = tls.Listen("tcp", ":" + strconv.Itoa(int(port)), listener.TLSConfig) } + if err != nil { return err } defer listener.listener.Close() for { - var conn, err = listener.listener.Accept() + conn, err := listener.listener.Accept() if err != nil { return err } - go listener.receiveMessage(conn) + go listener.receiveMessage(conn, interrupt) } } -// Cancel closes all connections and stops -// the listener from accepting new connections +// Cancel stops the listener from accepting new connections and closes any existing ones. func (listener *Listener) Cancel() { - if listener.listener == nil { return } - var err = listener.listener.Close() - if err != nil { listener.Failed(nil, err) } - listener.listener = nil -} - -// SendTextMessage is for sending a text based message -func (listener *Listener) SendTextMessage(conn net.Conn, str string) { - listener.processingSend(conn, []byte(str), TextMessage) + if listener.listener != nil { + err := listener.listener.Close() + if err != nil && listener.Failed != nil { listener.Failed(err) } + }; listener.listener = nil } -// SendBinaryMessage is for sending a text based message -func (listener *Listener) SendBinaryMessage(conn net.Conn, data []byte) { - listener.processingSend(conn, data, BinaryMessage) +// SendMessage sends a message through the specified connection. +func (listener *Listener) SendMessage(conn net.Conn, messageType uint8, data []byte) { + listener.processingSend(conn, data, messageType) } -/// MARK: - Private API - -// create and send message frame +// processingSend is a helper function to create and send a message frame over a connection. func (listener *Listener) processingSend(conn net.Conn, data []byte, opcode uint8) { if listener.listener == nil { return } - var message, err = listener.frame.create(data, opcode) - if err != nil { listener.Failed(conn, err); listener.remove(conn) } + message, err := listener.framer.create(data, opcode) + if err != nil { + if listener.Failed != nil { listener.Failed(err) } + if conn != nil { err = conn.Close() }; return + } _, err = conn.Write(message) - if err != nil { listener.Failed(conn, err) } + if err != nil && listener.Failed != nil { listener.Failed(err) } } -// parse a message frame -func (listener *Listener) processingParse(conn net.Conn, frame *frame, data []byte) error { - if listener.listener == nil { return errors.New(parsingFailed) } - var err = frame.parse(data, func(text *string, data []byte, ping []byte) { - listener.Message(conn, text, data) - if ping != nil { listener.sendPong(conn, ping) } - }) - return err +// processingParse is a helper function to parse a message frame from the connection data. +func (listener *Listener) processingParse(conn net.Conn, framer *framer, data []byte) error { + if listener.listener == nil { return errors.New("parsing failed") } + err := framer.parse(data, func(data []byte, opcode uint8) { + if listener.Message != nil { listener.Message(conn, data, opcode) } + if opcode == pingMessage { listener.processingSend(conn, data, pingMessage) } + }); return err } -// remove is for terminating a specific connection -func (listener *Listener) remove(conn net.Conn) { - var err = conn.Close() - if err != nil { listener.Failed(conn, err) } - listener.Cancelled(conn) -} +// receiveMessage handles all incoming data for a connection and tracks broken connections. +func (listener *Listener) receiveMessage(conn net.Conn, interrupt time.Duration) { + defer func() { if conn != nil { conn.Close() } }() + if listener.Ready != nil && conn != nil { listener.Ready(conn) } -// sendPong is for sending a pong based message -func (listener *Listener) sendPong(conn net.Conn, data []byte) { - listener.processingSend(conn, data, PingMessage) -} + if interrupt > 0 { + deadline := time.Now().Add(interrupt * time.Second); err := conn.SetDeadline(deadline) + if err != nil { if listener.Failed != nil { listener.Failed(err) }; return } + } -// receiveMessage is handling all incoming input -// keeps track broken connections -func (listener *Listener) receiveMessage(conn net.Conn) { - var frame = frame{} - listener.Ready(conn) - var buffer = make([]byte, 0x2000) + var framer framer; buffer := make([]byte, maximum) for { - var size, err = conn.Read(buffer) - if err != nil { if err == io.EOF { listener.Cancelled(conn) } else { listener.Failed(conn, err) }; break } - err = listener.processingParse(conn, &frame, buffer[:size]) - if err != nil { listener.Failed(conn, err); listener.remove(conn); break } + size, err := conn.Read(buffer) + if err != nil { if err != io.EOF && listener.Failed != nil { listener.Failed(err) }; break } + err = listener.processingParse(conn, &framer, buffer[:size]) + if err != nil { if listener.Failed != nil { listener.Failed(err) }; break } } -} \ No newline at end of file +}