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
+}