Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
lewisgibson committed Jul 10, 2024
0 parents commit 031087b
Show file tree
Hide file tree
Showing 109 changed files with 31,917 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
quote_type = single

[Makefile]
tab_width = 4
indent_style = tab
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
* text=auto
*.md eol=lf
*.yaml eol=lf
*.json eol=lf linguist-language=jsonc
vendor -diff
25 changes: 25 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: build

on:
push:
branches:
- main

pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
cache: true
go-version-file: 'go.mod'

- run: go mod verify

- run: make build
29 changes: 29 additions & 0 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: lint

on:
push:
branches:
- main

pull_request:
branches:
- main

permissions:
contents: read
pull-requests: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
cache: true
go-version-file: 'go.mod'

- uses: golangci/golangci-lint-action@v6
with:
version: v1.59
25 changes: 25 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: test

on:
push:
branches:
- main

pull_request:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
cache: true
go-version-file: 'go.mod'

- run: go mod verify

- run: make test
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# logs
logs
*.log

# coverage
coverage

# build output
bin
12 changes: 12 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
run:
go: '1.22'
timeout: 5m
allow-parallel-runners: true

linters:
presets:
- test
- unused

disable:
- exhaustruct
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],

"files.readonlyInclude": {
"internal/mocks/*.go": true
}
}
44 changes: 44 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
SHA ?= $(shell git rev-parse HEAD)

CPUS ?= $(shell (nproc --all || sysctl -n hw.ncpu) 2>/dev/null || echo 1)
MAKEFLAGS += --jobs=$(CPUS)

.PHONY: help
help: ## Display available commands
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: fakes
fakes: ## Generate fakes for testing
@go install go.uber.org/mock/mockgen@latest
@go generate ./...

.PHONY: build
build: ## Build the application
@go build ./...

.PHONY: test
test: ## Run tests
@go test ./...

.PHONY: lint
lint: ## Lint files
@golangci-lint run ./...

.PHONY: coverage
coverage: coverage-html coverage-xml ## Generate and report coverage

.PHONY: coverage-profile
coverage-profile:
@mkdir -p coverage
@go test -cover -covermode=count -coverprofile=coverage/profile.cov ./...

.PHONY: coverage-html
coverage-html: coverage-profile
@go tool cover -html=coverage/profile.cov -o coverage/coverage.html

.PHONY: coverage-xml
coverage-xml: coverage-profile
@go install github.com/axw/gocov/gocov@latest
@go install github.com/AlekSi/gocov-xml@latest
@gocov convert coverage/profile.cov | gocov-xml > coverage/coverage.xml

10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# go-engine.io

An implementation of the [engine.io](https://socket.io/docs/v4/engine-io-protocol/) protocol in go.

## todo

- Server implementation
- Integration testing
- End to end testing between client and server
- Support v2 and v3 parsing
65 changes: 65 additions & 0 deletions encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package engineio

import (
"encoding/base64"
"errors"
"unicode"
)

// ErrEmptyPacket is returned when the input is empty.
var ErrEmptyPacket = errors.New("empty packet")

// BinaryMarker is the marker for binary packets.
const BinaryMarker = 'b'

// EncodePacket encodes a packet into bytes.
func EncodePacket(packet Packet) ([]byte, error) {
// binary is true if the data contains non-ASCII characters.
var binary bool
for _, r := range string(packet.Data) {
if r > unicode.MaxASCII || !unicode.IsPrint(r) {
binary = true
}
}

switch {
// The packet is a binary packet.
case binary:
return append(
[]byte{BinaryMarker},
[]byte(base64.StdEncoding.EncodeToString(packet.Data))...,
), nil

// The packet is a text packet.
default:
return append(
[]byte{packet.Type.Byte()},
packet.Data...,
), nil
}
}

// DecodePacket decodes a packet from a string.
func DecodePacket(input []byte) (Packet, error) {
switch {
// The input is empty.
case len(input) == 0:
return Packet{}, ErrEmptyPacket

// The input is a binary packet. This must be a message packet.
case input[0] == BinaryMarker:
data, err := base64.StdEncoding.DecodeString(string(input[1:]))
if err != nil {
return Packet{}, err
}
return Packet{Type: PacketMessage, Data: data}, nil

// The input is a single byte packet, this indicates no data.
case len(input) == 1:
return Packet{Type: PacketTypeFromByte(input[0]), Data: []byte{}}, nil

// The input is a packet with data.
default:
return Packet{Type: PacketTypeFromByte(input[0]), Data: input[1:]}, nil
}
}
122 changes: 122 additions & 0 deletions encoding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package engineio_test

import (
"testing"

engineio "github.com/lewisgibson/go-engine.io"
"github.com/stretchr/testify/require"
)

func TestEncodePacket(t *testing.T) {
t.Parallel()

// Arrange: create a packet
packet := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!"),
}

// Act: encode the packet
encodedPacket, err := engineio.EncodePacket(packet)
require.NoError(t, err)

// Assert: the encoded packet is the expected packet
expectedPacket := []byte{0x34, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21}
require.Equal(t, expectedPacket, encodedPacket)
}

func TestEncodePacket_Binary(t *testing.T) {
t.Parallel()

// Arrange: create a packet
packet := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!\n"),
}

// Act: encode the packet
encodedPacket, err := engineio.EncodePacket(packet)
require.NoError(t, err)

// Assert: the encoded packet is the expected packet
expectedPacket := []byte{0x62, 0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x73, 0x49, 0x46, 0x64, 0x76, 0x63, 0x6d, 0x78, 0x6b, 0x49, 0x51, 0x6f, 0x3d}
require.Equal(t, expectedPacket, encodedPacket)
}

func TestDecodePacket(t *testing.T) {
t.Parallel()

// Arrange: create a packet
input := []byte{0x34, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21}

// Act: decode the packet
decodedPacket, err := engineio.DecodePacket(input)
require.NoError(t, err)

// Assert: the decoded packet is the same
expectedPacket := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!"),
}
require.Equal(t, expectedPacket, decodedPacket)
}

func TestDecodePacket_Binary(t *testing.T) {
t.Parallel()

// Arrange: create a packet
input := []byte{0x62, 0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x73, 0x49, 0x46, 0x64, 0x76, 0x63, 0x6d, 0x78, 0x6b, 0x49, 0x51, 0x6f, 0x3d}

// Act: decode the packet
decodedPacket, err := engineio.DecodePacket(input)
require.NoError(t, err)

// Assert: the decoded packet is the same
expectedPacket := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!\n"),
}
require.Equal(t, expectedPacket, decodedPacket)
}

func TestEncodeDecode(t *testing.T) {
t.Parallel()

// Arrange: create a packet
packet := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!"),
}

// Act: encode the packet
encodedPacket, err := engineio.EncodePacket(packet)
require.NoError(t, err)

// Act: decode the packet
decodedPacket, err := engineio.DecodePacket(encodedPacket)
require.NoError(t, err)

// Assert: the decoded packet is the same as the original packet
require.Equal(t, packet, decodedPacket)
}

func TestEncodeDecode_Binary(t *testing.T) {
t.Parallel()

// Arrange: create a packet
packet := engineio.Packet{
Type: engineio.PacketMessage,
Data: []byte("Hello, World!\n"),
}

// Act: encode the packet
encodedPacket, err := engineio.EncodePacket(packet)
require.NoError(t, err)

// Act: decode the packet
decodedPacket, err := engineio.DecodePacket(encodedPacket)
require.NoError(t, err)

// Assert: the decoded packet is the same as the original packet
require.Equal(t, packet, decodedPacket)
}
Loading

0 comments on commit 031087b

Please sign in to comment.