Skip to content
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ cover-view:
go tool cover -func .testCoverage.txt
go tool cover -html .testCoverage.txt

spec: test lint
check: test lint
go tool cover -func .testCoverage.txt

bench:
Expand Down
54 changes: 54 additions & 0 deletions examples/tcp/simple_tls/client/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"crypto/tls"
"github.com/DarthPestilane/easytcp"
"github.com/DarthPestilane/easytcp/examples/fixture"
"github.com/DarthPestilane/easytcp/examples/tcp/simple_tls/common"
"github.com/DarthPestilane/easytcp/message"
"github.com/sirupsen/logrus"
"time"
)

func main() {
cert, err := tls.LoadX509KeyPair("internal/test_data/certificates/cert.pem", "internal/test_data/certificates/cert.key")
if err != nil {
panic(err)
}
conn, err := tls.Dial("tcp", fixture.ServerAddr, &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true})
if err != nil {
panic(err)
}
log := logrus.New()
packer := easytcp.NewDefaultPacker()
go func() {
// write loop
for {
time.Sleep(time.Second)
rawData := []byte("ping, ping, ping")
msg := &message.Entry{
ID: common.MsgIdPingReq,
Data: rawData,
}
packedMsg, err := packer.Pack(msg)
if err != nil {
panic(err)
}
if _, err := conn.Write(packedMsg); err != nil {
panic(err)
}
log.Infof("snd >>> | id:(%d) size:(%d) data: %s", msg.ID, len(rawData), rawData)
}
}()
go func() {
// read loop
for {
msg, err := packer.Unpack(conn)
if err != nil {
panic(err)
}
log.Infof("rec <<< | id:(%d) size:(%d) data: %s", msg.ID, len(msg.Data), msg.Data)
}
}()
select {}
}
7 changes: 7 additions & 0 deletions examples/tcp/simple_tls/common/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package common

const (
_ int = iota
MsgIdPingReq
MsgIdPingAck
)
91 changes: 91 additions & 0 deletions examples/tcp/simple_tls/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"crypto/tls"
"fmt"
"github.com/DarthPestilane/easytcp"
"github.com/DarthPestilane/easytcp/examples/fixture"
"github.com/DarthPestilane/easytcp/examples/tcp/simple_tls/common"
"github.com/DarthPestilane/easytcp/message"
"github.com/sirupsen/logrus"
"os"
"os/signal"
"runtime"
"syscall"
"time"
)

var log *logrus.Logger

func init() {
log = logrus.New()
log.SetLevel(logrus.TraceLevel)
}

func main() {
// go printGoroutineNum()

easytcp.SetLogger(log)
s := easytcp.NewServer(&easytcp.ServerOption{
ReadTimeout: time.Second * 3,
WriteTimeout: time.Second * 3,
RespQueueSize: -1,
Packer: easytcp.NewDefaultPacker(),
Codec: nil,
})
s.OnSessionCreate = func(sess easytcp.Session) {
log.Infof("session created: %v", sess.ID())
}
s.OnSessionClose = func(sess easytcp.Session) {
log.Warnf("session closed: %v", sess.ID())
}

// register global middlewares
s.Use(fixture.RecoverMiddleware(log), logMiddleware)

// register a route
s.AddRoute(common.MsgIdPingReq, func(c easytcp.Context) {
c.SetResponseMessage(&message.Entry{
ID: common.MsgIdPingAck,
Data: []byte("pong, pong, pong"),
})
})

cert, err := tls.LoadX509KeyPair("internal/test_data/certificates/cert.pem", "internal/test_data/certificates/cert.key")
if err != nil {
panic(err)
}
go func() {
if err := s.ServeTLS(fixture.ServerAddr, &tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}); err != nil {
log.Errorf("serve err: %s", err)
}
}()

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
if err := s.Stop(); err != nil {
log.Errorf("server stopped err: %s", err)
}
time.Sleep(time.Second * 3)
}

func logMiddleware(next easytcp.HandlerFunc) easytcp.HandlerFunc {
return func(c easytcp.Context) {
req := c.Request()
log.Infof("rec <<< id:(%d) size:(%d) data: %s", req.ID, len(req.Data), req.Data)
defer func() {
resp := c.Response()
log.Infof("snd >>> id:(%d) size:(%d) data: %s", resp.ID, len(resp.Data), resp.Data)
}()
next(c)
}
}

// nolint: deadcode, unused
func printGoroutineNum() {
for {
fmt.Println("goroutine num: ", runtime.NumGoroutine())
time.Sleep(time.Second)
}
}
28 changes: 28 additions & 0 deletions internal/test_data/certificates/cert.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXR3nB7F/pyCP4
uZJH9ZmLasplwfyU1jjg/OqhhyJgt4lCbsvshKMATOINVkZECA/XnjAb+naAL+4K
Kuh5CTvywPBqeaBJyZboOJYz4EP/yJCKu6HiFyABnfbtrE5UwBJWzPLOhvg9aaQ9
RBLqpH9MtWQoSFBmfpVOMRnnirt4V9VNDq2u+fUbVmWwjWypNqnNnfqp9yZfpPhz
a9CaSMt1HtKRr1N9wriS3u3/gk7bQizfzM5LWUnSsQoCXlW/uQSae+Jp4wZC62wQ
tPrc+4shU6WjkJp2hLGD96Hqg4hn/gMZey7r6x54r6qJq2eK7R0lihZqrk0u3jOA
5BM4w22/AgMBAAECggEAP/dD7WQfx6YpUhFJrsoCGpPbmcq/EzZn5iogSWiOxXAj
f7LOMvPiPweZL19QN4yxsF6XaujL5yDWuPyw4K3muyWrCegjwWj9xvhxYO6lJc5h
bGd+HWDDqNdX9Jz7FWGQ0WvKRaWDAzRtkaq1eDTygkdvgCykfx/jmz0ptkvZklLz
TWM/nr1fxswHkQVfCWxRuRf+XYPXba86H1iM7RYCIFiApO7GMR20QjiJzmavjVFJ
7akv28Ca9PIFJkmGoDB0tlBhfKkWo5hcHA0t0Uz6k5Ks+a1qKOlp/avRKFE3xBy9
SNuolhYRzeEU2N2GbBu0FfC7nG3FnI0mxnF3H0+60QKBgQDu0i9PTARuhSgHYUGg
DzwBKcpsprNOpOQ4tmA8JHghuAB79hIqqaWVpWYU9YzBNlA9/AQMYFm++pX6JYkq
rma3rkIROzs+W/9dtoGmDgDjmmWmBE3H9wgpxRjmCZ65kyVNRK0MymemVdgrN300
Jymsq6GZFD5pMTFIEFgzFzLmtwKBgQDmw8Z4I/mHUhJ3iXugBlPAAkLBERREPwYB
vTGzcBxD5upzFyUrq21TGNhAPS8ss+GnmPQOEBCi8ose5ehxJ6f6FxuziVuowyn0
Bx/jtCMHVLGmOM5SKJFodL1vsXufV57O638k1EvLsgH2vmI3oK6m/8EOZ/gnD0S6
6I6Ja5tpOQKBgGCI73yzMptmEa8h/f/wCIZD2UIgBBzHBEV0WuQUrcabdP6mkeNS
3c7mo6PXOcUj6j2T8CL8k2piKluJ7q8k/fpDYwtKEQF8+HVt/2wa/vsBfxMjbDln
PpJ7zDu4KcPDmfFo0DZ6Xnla+91EOcTqC6tzWQfiqfOlYdFKYgyM1RNzAoGAYQD5
A/WzZePlKWScmBcwy2zn3LquN0X6425BXzmIWC7QbRLUqDfGnAC8nrxZgUQYXlhY
dzTfmW+1dYaVoENYRDPEjEL4ScfIcfEwwYouk11R1Bra+ARfo3Y3T6Ve3wt5EWhD
KRsoxXaNhshfBx0/bani4Ihp8xli/eLWUAPw71kCgYBkxxW2u5hhrjEU14oeaH0a
Z0Os/gJlLZiWgNFi3Fm6iNbzwigELHX5q+HT7ybOYhCWOVYAyrZpvUogNNtqFQh5
NvlaHy0OpzdwUVXRuadgoUXMo7UE6DyYKjl8ddDSUcIAiyhOczs0qOk+d5O0cBC8
6PsLBGgTUrEFCTzCUKPz9g==
-----END PRIVATE KEY-----
19 changes: 19 additions & 0 deletions internal/test_data/certificates/cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIjCCAgoCCQCTBFILb0p2XTANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJD
TjELMAkGA1UECAwCR0QxCzAJBgNVBAcMAlNaMRMwEQYDVQQKDApBY21lLCBJbmMu
MRUwEwYDVQQDDAxBY21lIFJvb3QgQ0EwHhcNMjIwMzE4MDc0NDEzWhcNMjMwMzE4
MDc0NDEzWjBTMQswCQYDVQQGEwJDTjELMAkGA1UECAwCR0QxCzAJBgNVBAcMAlNa
MRMwEQYDVQQKDApBY21lLCBJbmMuMRUwEwYDVQQDDAxBY21lIFJvb3QgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXR3nB7F/pyCP4uZJH9ZmLaspl
wfyU1jjg/OqhhyJgt4lCbsvshKMATOINVkZECA/XnjAb+naAL+4KKuh5CTvywPBq
eaBJyZboOJYz4EP/yJCKu6HiFyABnfbtrE5UwBJWzPLOhvg9aaQ9RBLqpH9MtWQo
SFBmfpVOMRnnirt4V9VNDq2u+fUbVmWwjWypNqnNnfqp9yZfpPhza9CaSMt1HtKR
r1N9wriS3u3/gk7bQizfzM5LWUnSsQoCXlW/uQSae+Jp4wZC62wQtPrc+4shU6Wj
kJp2hLGD96Hqg4hn/gMZey7r6x54r6qJq2eK7R0lihZqrk0u3jOA5BM4w22/AgMB
AAEwDQYJKoZIhvcNAQELBQADggEBAH+qa+pxRVGcItLiVV+S6LjUPVK7syDF2DSC
rx/iyX6wui37ht5x8IHmn9y8jbVso7lGZgEssaDN4ezrGIgrbKafMyeyZYjIKkfa
xDS+r11K7JoeJ8ZwbOQrZFrtDDotXRylaOVmlhBfTBOaJFAwaceb7SWizll3m3cb
5+s6DqQWAwS2FLyBCnHgJ6H200mPedPa8jd7fzF1aVk6WxlS9q3H0hRR4G1KZbYH
vkQe6hw+OzY6jPMXTzCDlBF7t951imtoQ3wPR141XMFVaAKB/vQhUwKmtQayT0uU
vW3FSjiadLfQ6QDQuNZFd30o3SCmZ/y0+Ak0psm2RMiurmX3Sd0=
-----END CERTIFICATE-----
50 changes: 39 additions & 11 deletions server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package easytcp

import (
"crypto/tls"
"fmt"
"net"
"time"
Expand Down Expand Up @@ -97,18 +98,39 @@ func NewServer(opt *ServerOption) *Server {
}
}

func (s *Server) listen(addr string) (net.Listener, error) {
address, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
lis, err := net.ListenTCP("tcp", address)
if err != nil {
return nil, err
}
return lis, nil
}

// Serve starts to listen TCP and keeps accepting TCP connection in a loop.
// The loop breaks when error occurred, and the error will be returned.
func (s *Server) Serve(addr string) error {
address, err := net.ResolveTCPAddr("tcp", addr)
lis, err := s.listen(addr)
if err != nil {
return err
}
lis, err := net.ListenTCP("tcp", address)
s.Listener = lis
if s.printRoutes {
s.router.printHandlers(fmt.Sprintf("tcp://%s", s.Listener.Addr()))
}
return s.acceptLoop()
}

// ServeTLS starts serve TCP with TLS.
func (s *Server) ServeTLS(addr string, config *tls.Config) error {
lis, err := s.listen(addr)
if err != nil {
return err
}
s.Listener = lis
s.Listener = tls.NewListener(lis, config)
if s.printRoutes {
s.router.printHandlers(fmt.Sprintf("tcp://%s", s.Listener.Addr()))
}
Expand Down Expand Up @@ -139,27 +161,33 @@ func (s *Server) acceptLoop() error {
return fmt.Errorf("accept err: %s", err)
}
if s.socketReadBufferSize > 0 {
if err := conn.(*net.TCPConn).SetReadBuffer(s.socketReadBufferSize); err != nil {
return fmt.Errorf("conn set read buffer err: %s", err)
if c, ok := conn.(*net.TCPConn); ok {
if err := c.SetReadBuffer(s.socketReadBufferSize); err != nil {
return fmt.Errorf("conn set read buffer err: %s", err)
}
}
}
if s.socketWriteBufferSize > 0 {
if err := conn.(*net.TCPConn).SetWriteBuffer(s.socketWriteBufferSize); err != nil {
return fmt.Errorf("conn set write buffer err: %s", err)
if c, ok := conn.(*net.TCPConn); ok {
if err := c.SetWriteBuffer(s.socketWriteBufferSize); err != nil {
return fmt.Errorf("conn set write buffer err: %s", err)
}
}
}
if s.socketSendDelay {
if err := conn.(*net.TCPConn).SetNoDelay(false); err != nil {
return fmt.Errorf("conn set no delay err: %s", err)
if c, ok := conn.(*net.TCPConn); ok {
if err := c.SetNoDelay(false); err != nil {
return fmt.Errorf("conn set no delay err: %s", err)
}
}
}
go s.handleConn(conn)
}
}

// handleConn creates a new session with conn,
// handleConn creates a new session with `conn`,
// handles the message through the session in different goroutines,
// and waits until the session's closed, then close the conn.
// and waits until the session's closed, then close the `conn`.
func (s *Server) handleConn(conn net.Conn) {
defer conn.Close() // nolint

Expand Down
23 changes: 23 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package easytcp

import (
"crypto/tls"
"fmt"
"github.com/DarthPestilane/easytcp/internal/mock"
"github.com/DarthPestilane/easytcp/message"
Expand Down Expand Up @@ -40,6 +41,28 @@ func TestServer_Serve(t *testing.T) {
<-done
}

func TestServer_ServeTLS(t *testing.T) {
server := NewServer(&ServerOption{
SocketReadBufferSize: 123, // won't work
})
cert, err := tls.LoadX509KeyPair("internal/test_data/certificates/cert.pem", "internal/test_data/certificates/cert.key")
assert.NoError(t, err)
done := make(chan struct{})
go func() {
cfg := &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{cert},
}
assert.ErrorIs(t, server.ServeTLS("localhost:0", cfg), ErrServerStopped)
close(done)
}()
<-server.accepting
time.Sleep(time.Millisecond * 5)
err = server.Stop()
assert.NoError(t, err)
<-done
}

func TestServer_acceptLoop(t *testing.T) {
t.Run("when everything's fine", func(t *testing.T) {
server := NewServer(&ServerOption{
Expand Down
2 changes: 1 addition & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (s *session) attemptConnWrite(outboundMsg []byte, attemptTimes int) (err er
time.Sleep(tempErrDelay * time.Duration(i))
_, err = s.conn.Write(outboundMsg)

// breaks if err is not nil or it's the last attempt.
// breaks if err is not nil, or it's the last attempt.
if err == nil || i == attemptTimes-1 {
break
}
Expand Down