Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/timeout #6

Merged
merged 20 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fabric.properties
# .idea/misc.xml
# *.ipr

.idea/
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
Expand Down
8 changes: 0 additions & 8 deletions .idea/.gitignore

This file was deleted.

8 changes: 0 additions & 8 deletions .idea/modules.xml

This file was deleted.

9 changes: 0 additions & 9 deletions .idea/network.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
41 changes: 41 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
101 changes: 0 additions & 101 deletions network/frame.go

This file was deleted.

85 changes: 85 additions & 0 deletions network/framer.go
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 36 additions & 0 deletions network/framer_test.go
Original file line number Diff line number Diff line change
@@ -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") }
}
Loading