Skip to content

Commit

Permalink
feat: refactor and improve HTTP server handling (#9)
Browse files Browse the repository at this point in the history
* feat: refactor and improve HTTP server handling

- Add type definitions for `listenAndServe` and `cleanup` in `graceful.go`
- Add `appendHTTPServer` function to `Graceful` struct in `graceful.go`
- Change `appendHttpServer` function to `appendHTTPServer` in `graceful.go`
- Add `ReadHeaderTimeout` field to `http.Server` struct in `appendHTTPServer` function in `graceful.go`
- Add tests for `TestCycle`, `TestWithFd`, `TestWithContext`, and `TestRunFd` functions in `graceful_test.go`
- Change variable names and function calls in `TestCycle`, `TestWithFd`, `TestWithContext`, and `TestRunFd` functions in `graceful_test.go`
- Add code for loading PEM file and creating certificate pool in `testRequest` function in `graceful_test.go`
- Change `InsecureSkipVerify` field to `RootCAs` and add `MinVersion` field in `testRequest` function in `graceful_test.go`
- Change variable names and function calls in `testRequest` function in `graceful_test.go`
- Change variable names and function calls in `WithAddr` and `WithTLS` functions in `options.go`
- Change variable names and function calls in `listen` function in `options.go`
- Modify `cert.pem` and `key.pem` files in `testdata/certificate` directory

Signed-off-by: Bo-Yi Wu <[email protected]>

* chore: update Go version in go.mod file

- Update the Go version in the go.mod file from 1.20 to 1.18

Signed-off-by: Bo-Yi Wu <[email protected]>

* chore: update Go version in GitHub workflow and go.mod file

- Update the `go-version` in the GitHub workflow from `^1.16` to `^1.21`
- Update the `go` version in the GitHub workflow from `[1.18, 1.19, "1.20", 1.21]` to `["1.20", 1.21]`
- Update the `go` version in the `go.mod` file from `go 1.18` to `go 1.20`

Signed-off-by: Bo-Yi Wu <[email protected]>

---------

Signed-off-by: Bo-Yi Wu <[email protected]>
  • Loading branch information
appleboy authored Nov 25, 2023
1 parent 4a6271f commit 2119bdc
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 59 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Setup go
uses: actions/setup-go@v4
with:
go-version: "^1.16"
go-version: "^1.21"
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup golangci-lint
Expand All @@ -27,7 +27,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
go: [1.18, 1.19, "1.20", 1.21]
go: ["1.20", 1.21]
include:
- os: ubuntu-latest
go-build: ~/.cache/go-build
Expand Down
14 changes: 10 additions & 4 deletions graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net"
"net/http"
"sync"
"time"

"github.com/gin-gonic/gin"
)
Expand All @@ -29,8 +30,10 @@ var ErrAlreadyStarted = errors.New("already started router")
// ErrNotStarted is returned when trying to stop a router that has not been started
var ErrNotStarted = errors.New("router not started")

type listenAndServe func() error
type cleanup func()
type (
listenAndServe func() error
cleanup func()
)

// Default returns a Graceful gin instance with the Logger and Recovery middleware already attached.
func Default(opts ...Option) (*Graceful, error) {
Expand Down Expand Up @@ -243,8 +246,11 @@ func (g *Graceful) apply(o Option) error {
return nil
}

func (g *Graceful) appendHttpServer() *http.Server {
srv := &http.Server{Handler: g.Engine}
func (g *Graceful) appendHTTPServer() *http.Server {
srv := &http.Server{
Handler: g.Engine,
ReadHeaderTimeout: time.Second * 5, // Set a reasonable ReadHeaderTimeout value
}

g.lock.Lock()
defer g.lock.Unlock()
Expand Down
40 changes: 34 additions & 6 deletions graceful_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net"
Expand Down Expand Up @@ -41,7 +42,7 @@ func TestCycle(t *testing.T) {
ctxEnd, cancelEnd := context.WithCancel(context.Background())
ctxService, cancelService := context.WithCancel(context.Background())

go func(ctx context.Context, cancel context.CancelFunc) {
go func(_ context.Context, cancelEnd context.CancelFunc) {
assert.ErrorIs(t, router.RunWithContext(ctxService), context.Canceled)
cancelEnd()
}(ctxEnd, cancelEnd)
Expand Down Expand Up @@ -91,6 +92,7 @@ func TestWithFd(t *testing.T) {
return Default(WithFd(socketFile.Fd()))
}, fmt.Sprintf("http://localhost:%d/example", listener.Addr().(*net.TCPAddr).Port))
}

func TestWithListener(t *testing.T) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
assert.NoError(t, err)
Expand Down Expand Up @@ -144,8 +146,13 @@ func TestWithContext(t *testing.T) {
cancel()
<-ctx.Done()

req, err := http.NewRequestWithContext(context.Background(), "GET", "http://localhost:8080/example", nil)
assert.NoError(t, err)
client := &http.Client{Transport: &http.Transport{}}
_, err = client.Get("http://localhost:8080/example")
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
assert.Error(t, err)

err = router.Shutdown(context.Background())
Expand Down Expand Up @@ -184,6 +191,7 @@ func TestRunFd(t *testing.T) {
return g.RunFd(socketFile.Fd())
}, fmt.Sprintf("http://localhost:%d/example", listener.Addr().(*net.TCPAddr).Port))
}

func TestRunListener(t *testing.T) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
assert.NoError(t, err)
Expand Down Expand Up @@ -304,6 +312,23 @@ func testRouterRun(t *testing.T, run func(*Graceful) error, urls ...string) {
}

func testRequest(t *testing.T, urls ...string) {
// Open the PEM file
file, err := os.Open("testdata/certificate/cert.pem")
if err != nil {
t.Fatal(err)
}
defer file.Close()

// Create a new empty certificate pool
caCertPool := x509.NewCertPool()

// Load the PEM file into the certificate pool
pemData, err := io.ReadAll(file)
if err != nil {
t.Fatal(err)
}
caCertPool.AppendCertsFromPEM(pemData)

// have to wait for the goroutine to start and run the server
// otherwise the main thread will complete
time.Sleep(5 * time.Millisecond)
Expand All @@ -314,21 +339,24 @@ func testRequest(t *testing.T, urls ...string) {

tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12, // Fix for G402: TLS MinVersion too low
},
}
client := &http.Client{Transport: tr}

for _, url := range urls {
resp, err := client.Get(url)
req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
assert.NoError(t, err)
resp, err := client.Do(req)
assert.NoError(t, err)
defer resp.Body.Close()

body, ioerr := io.ReadAll(resp.Body)
assert.NoError(t, ioerr)

var responseStatus = "200 OK"
var responseBody = "it worked"
responseStatus := "200 OK"
responseBody := "it worked"

assert.Equal(t, responseStatus, resp.Status, "should get a "+responseStatus)
if responseStatus == "200 OK" {
Expand Down
10 changes: 6 additions & 4 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (o optionFunc) apply(g *Graceful) (listenAndServe, cleanup, error) {
func WithAddr(addr string) Option {
return optionFunc(func(g *Graceful) (listenAndServe, cleanup, error) {
return func() error {
srv := g.appendHttpServer()
srv := g.appendHTTPServer()
srv.Addr = addr

return srv.ListenAndServe()
Expand All @@ -35,9 +35,11 @@ func WithAddr(addr string) Option {
func WithTLS(addr string, certFile string, keyFile string) Option {
return optionFunc(func(g *Graceful) (listenAndServe, cleanup, error) {
return func() error {
srv := g.appendHttpServer()
srv := g.appendHTTPServer()
srv.Addr = addr
g.lock.Lock()
g.servers = append(g.servers, srv)
g.lock.Unlock()

return srv.ListenAndServeTLS(certFile, keyFile)
}, donothing, nil
Expand All @@ -62,7 +64,7 @@ func WithUnix(file string) Option {
// WithFd configure a http.Server to listen on the given file descriptor.
func WithFd(fd uintptr) Option {
return optionFunc(func(g *Graceful) (listenAndServe, cleanup, error) {
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd@%d", fd))
f := os.NewFile(fd, fmt.Sprintf("fd@%d", fd))
listener, err := net.FileListener(f)
if err != nil {
return nil, donothing, err
Expand All @@ -84,7 +86,7 @@ func WithListener(l net.Listener) Option {

func listen(g *Graceful, l net.Listener, close cleanup) (listenAndServe, cleanup, error) {
return func() error {
srv := g.appendHttpServer()
srv := g.appendHTTPServer()

return srv.Serve(l)
}, func() {
Expand Down
40 changes: 24 additions & 16 deletions testdata/certificate/cert.pem
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
-----BEGIN CERTIFICATE-----
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
rEcX6h/H60EN0fbQ
-----END CERTIFICATE-----
55 changes: 28 additions & 27 deletions testdata/certificate/key.pem
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAqbKP9hmkPn0GnLjDep/pXMzD25QGxan4g/iSXvPlyYYdhQef
9iilMse9HbcYAHXanoqblBbMIG4kXiPrU8lcd+Df+uNKFnvslxDeTPG7LWIoMj4M
0o3sqXOt2Mnj1APSVzNkd4G+8IvsmwkUoWMbLraudK25bwtogR22NdP4ZRlPEmHo
bvI9h8MxLUix0xAY51sbA1r6qiAy5A+HRPMfD4LvebIquNjqlESKOScwL+ucgzP1
0s+3oqXFfLhuvjjd2ljp1gYiEO4qFE5P69nTkcpqy65BQWFju/8qhSkRkwH2t9RL
ONDl9qR4NQAyeJdFx34ObC9ugbZMjqLGa48r4QIDAQABAoIBAD5mhd+GMEo2KU9J
9b/Ku8I/HapJtW/L/7Fvn0tBPncrVQGM+zpGWfDhV95sbGwG6lwwNeNvuqIWPlNL
vAY0XkdKrrIQEDdSXH50WnpKzXxzwrou7QIj5Cmvevbjzl4xBZDBOilj0XWczmV4
IljyG5XC4UXQeAaoWEZaSZ1jk8yAt2Zq1Hgg7HqhHsK/arWXBgax+4K5nV/s9gZx
yjKU9mXTIs7k/aNnZqwQKqcZF+l3mvbZttOaFwsP14H0I8OFWhnM9hie54Dejqxi
f4/llNxDqUs6lqJfP3qNxtORLcFe75M+Yl8v7g2hkjtLdZBakPzSTEx3TAK/UHgi
aM8DdxECgYEA3fmg/PI4EgUEj0C3SCmQXR/CnQLMUQgb54s0asp4akvp+M7YCcr1
pQd3HFUpBwhBcJg5LeSe87vLupY7pHCKk56cl9WY6hse0b9sP/7DWJuGiO62m0E0
vNjQ2jpG99oR2ROIHHeWsGCpGLmrRT/kY+vR3M+AOLZniXlOCw8k0aUCgYEAw7WL
XFWLxgZYQYilywqrQmfv1MBfaUCvykO6oWB+f6mmnihSFjecI+nDw/b3yXVYGEgy
0ebkuw0jP8suC8wBqX9WuXj+9nZNomJRssJyOMiEhDEqUiTztFPSp9pdruoakLTh
Wk1p9NralOqGPUmxpXlFKVmYRTUbluikVxDypI0CgYBn6sqEQH0hann0+o4TWWn9
PrYkPUAbm1k8771tVTZERR/W3Dbldr/DL5iCihe39BR2urziEEqdvkglJNntJMar
TzDuIBADYQjvltb9qq4XGFBGYMLaMg+XbUVxNKEuvUdnwa4R7aZ9EfN34MwekkfA
w5Cu9/GGG1ajVEfGA6PwBQKBgA3o71jGs8KFXOx7e90sivOTU5Z5fc6LTHNB0Rf7
NcJ5GmCPWRY/KZfb25AoE4B8GKDRMNt+X69zxZeZJ1KrU0rqxA02rlhyHB54gnoE
G/4xMkn6/JkOC0w70PMhMBtohC7YzFOQwQEoNPT0nkno3Pl33xSLS6lPlwBo1JVj
nPtZAoGACXNLXYkR5vexE+w6FGl59r4RQhu1XU8Mr5DIHeB7kXPN3RKbS201M+Tb
SB5jbu0iDV477XkzSNmhaksFf2wM9MT6CaE+8n3UU5tMa+MmBGgwYTp/i9HkqVh5
jjpJifn1VWBINd4cpNzwCg9LXoo0tbtUPWwGzqVeyo/YE5GIHGo=
-----END RSA PRIVATE KEY-----
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCscBEDCRWomHIt
2jT5Rua5/+WyOj6Jsn/PnTzOksthzjZEJMpvdzD5skn7ZCDm3NI2fPrSI7dMA53M
g88FP6OZMTbB+LruGsRGz1Z43PI2a7y+fQ3yJpNiZVOV9PhJKXIzMoYtrO/oHus7
s9QRnf65LYTO55ysxz+955GQ0WvL+lGopnYkiMRczgDwUDwL/i90T6q/+NHJjcOB
figHZONFMuYQS70rYnpJtvlIJ6yVaEia7ADTko9lbbjVNAIjeTszgNUem1uHBxo5
FtGSPG16GfqPwtb85zTXTNcHoR2Cei0P469Ps6zTHAsqK+rpwbc3GxsovrK3E+PW
tbpICWrPAgMBAAECggEBAJTMq9FrwHXY/wZUOsw1A5rTmHmgyyB1mNcmPzee8sQ2
dxt8h/eB6hA+apr1P3MYcIm2dZZqXZzJrhyyTS2hSeJC1paxtVUMYf0co+jt+FE/
zQKv1R31bc9F0sgfOTWg/zvwyFqX1fn+3EyLlF+TcCCJiAYnVUq/MWEFd9Z48MV1
lHzzPxOlFLXrAW4LUgeDWsFgUbAVUI9AcvMUZjPQy+QsA3O7BIUobjsfOkxioyQ1
X+5jUmBjPTNiBzDzLUW58Mrj06MaW8JfZs/6PuRRcLyoI4Y8o0vCoC0LAGe5OZvQ
ABv/+w1KmeHmsZrchq/ccM3RnlZ9Hqz0syx5G6curiECgYEAx/Em+E6jjtNoxVM4
+oJCDaxvvrYQUek6qRKAYd67od+f4I9Wvri9nwsF4LD22fGI+IcVgKqsz6zzz8oK
WCw3WZRHj2JXY1NhY2HYHMYRAo44h+76HzZxegjQb+j4YelDI2FPM9lJPTTTGMuP
nujoT8sE85Yly2UwUECA76xieXECgYEA3MjLvC7BGQp8Q2Y92uZXR6AbaE8iq2lp
eAoTTUkCV7/5NaRhjFnS+2VOpZq7HKhHbUj2YP/Dy7kBOs4x84xZQnC8Z59wd8eb
cTanjgz8fkzFcvq9CoD/Y0UuMnGIFaTcCGjx6L7eG09tP7J3siEJyFs7S7Df3kw6
lrNKBpciCD8CgYAYUwE/sil5xcR4RSCocVJh4XmSxQmFPY+SZBrUTUmcbt0/b5DJ
ESaYg7nKVR0i9JyKR7zY82qktVDQFa9efLqbHkv6aKdjARDXC19EWOBW5ECCYRNy
2qfBhfL9umUFXGB7nGoLT8L2PEf2O0WUupcQpg1LKRgd/4h6Ku7Eub8dMQKBgQDJ
YFxzyDFYJbgxwY+pmW8CZYaaHZQBxKPuWUJBu9XnM7KautQ+r69AnaaeZgNEoDiF
9YbD8oUjzBrTCD+HZPwFHc66tTJgDjA5kyBA8Jdcm6l8tja23fITGYaJfAQjubr3
8MC1CWKMhmWfM6j5UCkcFhp/b1TB7cWCrwVV/YONAQKBgQCJ/kt3ReuRyHf6D0GD
CBIUdE965M8hzG1V29p/nxUSuaqHyOK9gqc+DBa2C/QMLQRO0FBIrxWrcbbVpb1g
JasUH4NZrq3zXs+VCoLwAXddDUVb+yL+1SyW0GuE8RZ2qBAFbUOAV4ixOLxeX3Mm
5xLFVzHpoHjxMeK3ebBUSyr+0Q==
-----END PRIVATE KEY-----

0 comments on commit 2119bdc

Please sign in to comment.