Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
bb212e1
Add CI with lint, test and (commented out) coverage reporting
mostafa Oct 31, 2022
969b3f3
Add golangci-lint config
mostafa Oct 31, 2022
49a1d6f
Fix linter errors (errcheck)
mostafa Oct 31, 2022
3bf2803
Ignore funlen on main
mostafa Oct 31, 2022
7bc84d4
Fix linter errors (errcheck)
mostafa Oct 31, 2022
096db76
Fix capitalized param
mostafa Oct 31, 2022
e1f5843
Fix if/else block (golint)
mostafa Oct 31, 2022
5492b80
Use a stronger hashing algorithm
mostafa Oct 31, 2022
1274e77
Fix test
mostafa Oct 31, 2022
d7d66c7
Check type assertions (forcetypeassert)
mostafa Oct 31, 2022
3005b68
Fix wrapcheck errors
mostafa Nov 1, 2022
0f2b139
Fix variable names
mostafa Nov 1, 2022
51608f1
Remove named variables for return
mostafa Nov 1, 2022
5ddff32
Use short if-statement syntax with error
mostafa Nov 1, 2022
35d5859
Pass sync.Map as pointer
mostafa Nov 1, 2022
0da0630
Make tick interval customizable
mostafa Nov 1, 2022
5297f4a
Use constants and configurable parameters instead of magic numbers
mostafa Nov 1, 2022
f177778
Use non-dynamic errors
mostafa Nov 1, 2022
7f5fb78
Fix type assertions
mostafa Nov 1, 2022
4f19e68
Wrap errors with %w
mostafa Nov 1, 2022
2dcd43a
Fix complexity
mostafa Nov 1, 2022
66998a7
Fix error wrap issue
mostafa Nov 1, 2022
81eafb3
Fix error capitalization issue
mostafa Nov 1, 2022
7e74ae3
Fix complexity
mostafa Nov 1, 2022
b7662da
Initialize slice with 0 length
mostafa Nov 1, 2022
df3feed
Fix string length
mostafa Nov 1, 2022
7f5863e
Fix error variable name
mostafa Nov 1, 2022
6ad6866
Use switch/case and errors.Is to handle error cases
mostafa Nov 1, 2022
9e8a52e
Ignore funlen error for now
mostafa Nov 1, 2022
d7c089f
Ignore snake case for syscall.RLIMIT_NOFILE
mostafa Nov 1, 2022
b5de0a9
Run ALL tests
mostafa Nov 1, 2022
f025dc6
Fix append to slice with non-zero initial value
mostafa Nov 1, 2022
99577fc
Revert "Fix append to slice with non-zero initial value"
mostafa Nov 1, 2022
2d1d053
Fix makezero issue for the last time
mostafa Nov 1, 2022
e29aee0
Add startup packet creation for testing
mostafa Nov 3, 2022
6449681
Fix linter issues with switch/case and error.Is
mostafa Nov 3, 2022
6092c21
Add gconn and client to the hook functions
mostafa Nov 3, 2022
4282b3c
Add NewServer function to create a new Server instance
mostafa Nov 3, 2022
d6d5366
Use the NewServer function to create a new Server instance
mostafa Nov 3, 2022
a83280c
Refactor client tests with a function to creata a proper startup packet
mostafa Nov 3, 2022
a981bc9
Rewrite server tests to make it work in parallel
mostafa Nov 3, 2022
4269f49
Error check Server.Run function
mostafa Nov 3, 2022
7922e2b
Ignore function length errors
mostafa Nov 3, 2022
e0f3ce8
Run gofumpt to fix issues
mostafa Nov 3, 2022
a0a04e6
Remove unused variable
mostafa Nov 3, 2022
1858df8
Use short if error check syntax
mostafa Nov 3, 2022
4ed0f3e
Use a longer variable name
mostafa Nov 3, 2022
37cd8ec
Wrap error
mostafa Nov 3, 2022
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
36 changes: 36 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Test GatewayD

on:
push:
branches:
- main
tags:
- v*
pull_request:

jobs:
test:
name: Test GatewayD
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Install Go 🧑‍💻
uses: actions/setup-go@v3
with:
go-version: '1.18'

- name: Lint code issues 🚨
uses: golangci/golangci-lint-action@v3

- name: Run Go tests 🔬
run: go test -cover -covermode atomic -coverprofile=profile.cov -v ./...

# Enable coverage reporting
# - name: Report coverage to coveralls 📈
# uses: shogo82148/actions-goveralls@v1
# with:
# path-to-profile: profile.cov
17 changes: 17 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
linters:
enable-all: true
disable:
- cyclop
- wsl
- godox
- gochecknoglobals
- ireturn
- nlreturn
- testpackage
- paralleltest
- exhaustivestruct
- exhaustruct
- gocognit
- gochecknoinits
- gocyclo

40 changes: 28 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,27 @@ import (

"github.com/gatewayd-io/gatewayd/network"
"github.com/panjf2000/gnet/v2"
"github.com/sirupsen/logrus"
)

const (
DefaultTCPKeepAlive = 3 * time.Second
)

//nolint:funlen
func main() {
// Create a server
server := &network.Server{
Network: "tcp",
Address: "0.0.0.0:15432",
Status: network.Stopped,
Options: []gnet.Option{
server := network.NewServer(
"tcp",
"0.0.0.0:15432",
0,
0,
network.DefaultTickInterval,
network.DefaultPoolSize,
network.DefaultBufferSize,
false,
false,
[]gnet.Option{
// Scheduling options
gnet.WithMulticore(true),
gnet.WithLockOSThread(false),
Expand All @@ -37,18 +49,20 @@ func main() {

// Buffer options
// TODO: This should be configurable and optimized.
gnet.WithReadBufferCap(4096),
gnet.WithWriteBufferCap(4096),
gnet.WithSocketRecvBuffer(4096),
gnet.WithSocketSendBuffer(4096),
gnet.WithReadBufferCap(network.DefaultBufferSize),
gnet.WithWriteBufferCap(network.DefaultBufferSize),
gnet.WithSocketRecvBuffer(network.DefaultBufferSize),
gnet.WithSocketSendBuffer(network.DefaultBufferSize),

// TCP options
gnet.WithReuseAddr(true),
gnet.WithReusePort(true),
gnet.WithTCPKeepAlive(time.Second * 3),
gnet.WithTCPKeepAlive(DefaultTCPKeepAlive),
gnet.WithTCPNoDelay(gnet.TCPNoDelay),
},
}
nil,
nil,
)

// Shutdown the server gracefully
var signals []os.Signal
Expand All @@ -75,5 +89,7 @@ func main() {
}()

// Run the server
server.Run()
if err := server.Run(); err != nil {
logrus.Error(err)
}
}
17 changes: 10 additions & 7 deletions network/client.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package network

import (
"fmt"
"net"

"github.com/sirupsen/logrus"
)

const (
DefaultSeed = 1000
)

type Client struct {
net.Conn

ID string
ReceiveBufferSize int
Network string // tcp/udp/unix
Address string

// TODO: add read/write deadline and deal with timeouts
}

Expand Down Expand Up @@ -51,19 +55,18 @@ func NewClient(network, address string, receiveBufferSize int) *Client {

client.Conn = conn
if client.ReceiveBufferSize == 0 {
client.ReceiveBufferSize = 4096
client.ReceiveBufferSize = DefaultBufferSize
}
logrus.Debugf("New client created: %s", client.Address)
client.ID = GetID(conn.LocalAddr().Network(), conn.LocalAddr().String(), 1000)
client.ID = GetID(conn.LocalAddr().Network(), conn.LocalAddr().String(), DefaultSeed)

return &client
}

func (c *Client) Send(data []byte) error {
_, err := c.Write(data)
if err != nil {
if _, err := c.Write(data); err != nil {
logrus.Errorf("Couldn't send data to the server: %s", err)
return err
return fmt.Errorf("couldn't send data to the server: %w", err)
}
logrus.Debugf("Sent %d bytes to %s", len(data), c.Address)
// logrus.Infof("Sent data: %s", data)
Expand All @@ -75,7 +78,7 @@ func (c *Client) Receive() (int, []byte, error) {
read, err := c.Read(buf)
if err != nil {
logrus.Errorf("Couldn't receive data from the server: %s", err)
return 0, nil, err
return 0, nil, fmt.Errorf("couldn't receive data from the server: %w", err)
}
logrus.Debugf("Received %d bytes from %s", read, c.Address)
// logrus.Infof("Received data: %s", buf[:read])
Expand Down
58 changes: 29 additions & 29 deletions network/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ func TestNewClient(t *testing.T) {
}
}()

c := NewClient("tcp", "localhost:5432", 4096)
defer c.Close()

assert.Equal(t, "tcp", c.Network)
assert.Equal(t, "127.0.0.1:5432", c.Address)
assert.Equal(t, 4096, c.ReceiveBufferSize)
assert.NotEmpty(t, c.ID)
assert.NotNil(t, c.Conn)
client := NewClient("tcp", "localhost:5432", DefaultBufferSize)
defer client.Close()

assert.Equal(t, "tcp", client.Network)
assert.Equal(t, "127.0.0.1:5432", client.Address)
assert.Equal(t, DefaultBufferSize, client.ReceiveBufferSize)
assert.NotEmpty(t, client.ID)
assert.NotNil(t, client.Conn)
}

func TestSend(t *testing.T) {
Expand All @@ -41,11 +41,11 @@ func TestSend(t *testing.T) {
}
}()

c := NewClient("tcp", "localhost:5432", 4096)
defer c.Close()
client := NewClient("tcp", "localhost:5432", DefaultBufferSize)
defer client.Close()

assert.NotNil(t, c)
err := c.Send(CreatePostgreSQLPacket('Q', []byte("select 1;")))
assert.NotNil(t, client)
err := client.Send(CreatePostgreSQLPacket('Q', []byte("select 1;")))
assert.Nil(t, err)
}

Expand All @@ -61,22 +61,22 @@ func TestReceive(t *testing.T) {
}
}()

c := NewClient("tcp", "localhost:5432", 4096)
defer c.Close()
client := NewClient("tcp", "localhost:5432", DefaultBufferSize)
defer client.Close()

assert.NotNil(t, c)
err := c.Send(CreatePostgreSQLPacket('Q', []byte("select 1;")))
assert.NotNil(t, client)
err := client.Send(CreatePgStartupPacket())
assert.Nil(t, err)

size, data, err := c.Receive()
msg := "SFATAL\x00VFATAL\x00C0A000\x00Munsupported frontend protocol 0.0: server supports 3.0 to 3.0\x00Fpostmaster.c\x00L2138\x00RProcessStartupPacket\x00\x00"
assert.Equal(t, 132, size)
size, data, err := client.Receive()
msg := "\x00\x00\x00\x03"
assert.Equal(t, 9, size)
assert.Equal(t, len(data[:size]), size)
assert.Nil(t, err)
assert.NotEmpty(t, data[:size])
assert.Equal(t, msg, string(data[5:size]))
assert.Equal(t, "E", string(data[0]))
assert.Equal(t, 83, int(data[5]))
// AuthenticationOk
assert.Equal(t, uint8(0x52), data[0])
}

func TestClose(t *testing.T) {
Expand All @@ -91,12 +91,12 @@ func TestClose(t *testing.T) {
}
}()

c := NewClient("tcp", "localhost:5432", 4096)
assert.NotNil(t, c)
c.Close()
assert.Equal(t, "", c.ID)
assert.Equal(t, "", c.Network)
assert.Equal(t, "", c.Address)
assert.Nil(t, c.Conn)
assert.Equal(t, 0, c.ReceiveBufferSize)
client := NewClient("tcp", "localhost:5432", DefaultBufferSize)
assert.NotNil(t, client)
client.Close()
assert.Equal(t, "", client.ID)
assert.Equal(t, "", client.Network)
assert.Equal(t, "", client.Address)
assert.Nil(t, client.Conn)
assert.Equal(t, 0, client.ReceiveBufferSize)
}
10 changes: 10 additions & 0 deletions network/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package network

import "errors"

var (
ErrClientNotFound = errors.New("client not found")
ErrNetworkNotSupported = errors.New("network is not supported")
ErrClientNotConnected = errors.New("client is not connected")
ErrPoolExhausted = errors.New("pool is exhausted")
)
51 changes: 47 additions & 4 deletions network/network_helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,56 @@
package network

import "encoding/binary"
import (
"encoding/binary"
)

type WriteBuffer struct {
Bytes []byte

msgStart int
}

func writeStartupMsg(buf *WriteBuffer, user, database, appName string) {
// Write startup message header
buf.msgStart = len(buf.Bytes)
buf.Bytes = append(buf.Bytes, 0, 0, 0, 0)

// Write protocol version
buf.Bytes = append(buf.Bytes, 0, 0, 0, 0)
binary.BigEndian.PutUint32(buf.Bytes[len(buf.Bytes)-4:], uint32(196608))

buf.WriteString("user")
buf.WriteString(user)
buf.WriteString("database")
buf.WriteString(database)
buf.WriteString("application_name")
buf.WriteString(appName)
buf.WriteString("")

// Write message length
binary.BigEndian.PutUint32(
buf.Bytes[buf.msgStart:], uint32(len(buf.Bytes)-buf.msgStart))
}

func (buf *WriteBuffer) WriteString(s string) {
buf.Bytes = append(buf.Bytes, s...)
buf.Bytes = append(buf.Bytes, 0)
}

func CreatePostgreSQLPacket(typ byte, msg []byte) []byte {
packet := make([]byte, 1+4+len(msg))

packet = append(packet, typ)
binary.BigEndian.PutUint32(packet, uint32(len(msg)+4))
packet = append(packet, msg...)
packet[0] = typ
binary.BigEndian.PutUint32(packet[1:], uint32(len(msg)+4))
for i, b := range msg {
packet[i+5] = b
}

return packet
}

func CreatePgStartupPacket() []byte {
buf := &WriteBuffer{}
writeStartupMsg(buf, "postgres", "postgres", "gatewayd")
return buf.Bytes
}
Loading