Skip to content
This repository was archived by the owner on Mar 29, 2025. It is now read-only.

Commit c718d28

Browse files
authored
Merge pull request #11 from randlabs/new_features
Updated dependencies, go version and added features
2 parents 05443b0 + d3e2b9a commit c718d28

File tree

6 files changed

+132
-62
lines changed

6 files changed

+132
-62
lines changed

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
module github.com/randlabs/go-webserver
22

3-
go 1.17
3+
go 1.19
44

55
require (
6-
github.com/fasthttp/router v1.4.12
7-
github.com/valyala/fasthttp v1.40.0
6+
github.com/fasthttp/router v1.4.13
7+
github.com/valyala/fasthttp v1.41.0
88
)
99

1010
require (
1111
github.com/andybalholm/brotli v1.0.4 // indirect
12-
github.com/klauspost/compress v1.15.11 // indirect
12+
github.com/klauspost/compress v1.15.12 // indirect
1313
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect
1414
github.com/valyala/bytebufferpool v1.0.0 // indirect
1515
github.com/valyala/tcplisten v1.0.0 // indirect
16-
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
16+
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
1717
)

go.sum

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
22
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
3-
github.com/fasthttp/router v1.4.12 h1:QEgK+UKARaC1bAzJgnIhdUMay6nwp+YFq6VGPlyKN1o=
4-
github.com/fasthttp/router v1.4.12/go.mod h1:41Qdc4Z4T2pWVVtATHCnoUnOtxdBoeKEYJTXhHwbxCQ=
3+
github.com/fasthttp/router v1.4.13 h1:42M7+7tNO6clb5seb4HhXlBIX1lnNv8DLhiT6jUv75A=
4+
github.com/fasthttp/router v1.4.13/go.mod h1:mVhHMaSQA2Hi1HeuL/ZMuZpsZWk5bya75EpaDr3fO7E=
55
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
6-
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
7-
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
8-
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
6+
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
7+
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
8+
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
99
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo=
1010
github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
1111
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
1212
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
13-
github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
14-
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
13+
github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY=
14+
github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
1515
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
1616
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
1717
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
1818
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
19-
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
19+
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
2020
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2121
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2222
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23-
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24-
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
25-
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23+
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
24+
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
2625
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
2726
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
2827
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

util_non_unix.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !unix
2+
3+
package go_webserver
4+
5+
// -----------------------------------------------------------------------------
6+
7+
func checkMaxFileDescriptors(_ uint64) bool {
8+
return true
9+
}

util_unix.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build unix
2+
3+
package go_webserver
4+
5+
import (
6+
"syscall"
7+
)
8+
9+
// -----------------------------------------------------------------------------
10+
11+
func checkMaxFileDescriptors(value uint64) bool {
12+
var rLimit syscall.Rlimit
13+
14+
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
15+
return err == nil && rLimit.Cur >= value
16+
}

webserver.go

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,19 @@ type Options struct {
8989
// A custom handler for 404 errors
9090
NotFoundHandler HandlerFunc
9191

92+
// A custom handler for 405 errors
93+
MethodNotAllowedHandler HandlerFunc
94+
9295
// TLSConfig optionally provides a TLS configuration for use.
9396
TLSConfig *tls.Config
97+
98+
// If MinReqFileDescs is greater than zero, specifies the minimum number of required file descriptors
99+
// to be available.
100+
//
101+
// NOTES:
102+
// 1. Only valid on *nix operating systems.
103+
// 2. Starting from Go v1.19, the soft limit is automatically raised to the maximum allowed on process startup.
104+
MinReqFileDescs uint64
94105
}
95106

96107
// ServerFilesOptions sets the parameters to use in a ServeFiles call
@@ -127,12 +138,6 @@ const (
127138
defaultServerName = "go-webserver"
128139

129140
serveFilesSuffix = "{filepath:*}"
130-
131-
stateNotStarted = 1
132-
stateStarting = 2
133-
stateRunning = 3
134-
stateStopping = 4
135-
stateStopped = 3
136141
)
137142

138143
// -----------------------------------------------------------------------------
@@ -188,6 +193,10 @@ func Create(options Options) (*Server, error) {
188193
parsedBindAddress = p4
189194
}
190195

196+
if options.MinReqFileDescs > 0 && checkMaxFileDescriptors(options.MinReqFileDescs) == false {
197+
return nil, errors.New("the number of process' file descriptors doesn't fulfill the minimum requirements")
198+
}
199+
191200
// Create a new server container
192201
srv := &Server{
193202
router: router.New(),
@@ -207,7 +216,13 @@ func Create(options Options) (*Server, error) {
207216
srv.requestErrorHandler = srv.defaultRequestErrorHandler
208217
}
209218

210-
// Set the NotFound handler.
219+
// Override some router settings
220+
srv.router.RedirectTrailingSlash = true
221+
srv.router.RedirectFixedPath = true
222+
srv.router.HandleMethodNotAllowed = true
223+
srv.router.HandleOPTIONS = false
224+
225+
// Set the endpoint not found handler
211226
if options.NotFoundHandler != nil {
212227
srv.router.NotFound = srv.createEndpointHandler(options.NotFoundHandler)
213228
} else {
@@ -216,11 +231,14 @@ func Create(options Options) (*Server, error) {
216231
}
217232
}
218233

219-
// Override some router settings
220-
srv.router.RedirectTrailingSlash = true
221-
srv.router.RedirectFixedPath = true
222-
srv.router.HandleMethodNotAllowed = true
223-
srv.router.HandleOPTIONS = false
234+
// Set the method not allowed handler
235+
if options.MethodNotAllowedHandler != nil {
236+
srv.router.MethodNotAllowed = srv.createEndpointHandler(options.MethodNotAllowedHandler)
237+
} else {
238+
srv.router.MethodNotAllowed = func(ctx *fasthttp.RequestCtx) {
239+
ctx.Error(fasthttp.StatusMessage(fasthttp.StatusMethodNotAllowed), fasthttp.StatusMethodNotAllowed)
240+
}
241+
}
224242

225243
// Check server name
226244
serverName := options.Name
@@ -258,7 +276,7 @@ func Create(options Options) (*Server, error) {
258276
// Start initiates listening
259277
func (srv *Server) Start() error {
260278
if !atomic.CompareAndSwapInt32(&srv.state, stateNotStarted, stateStarting) {
261-
return errors.New("invalid state")
279+
return errors.New("server is not stopped")
262280
}
263281

264282
// Create the listener
@@ -276,7 +294,7 @@ func (srv *Server) Start() error {
276294
// Create the graceful shutdown listener
277295
ln, err := createListener(network, address+":"+strconv.Itoa(int(srv.bindPort)))
278296
if err != nil {
279-
atomic.StoreInt32(&srv.state, stateNotStarted)
297+
srv.setState(stateNotStarted)
280298
return err
281299
}
282300

@@ -288,37 +306,8 @@ func (srv *Server) Start() error {
288306
// Wrap listener inside a graceful shutdown listener
289307
ln = listener.NewGracefulListener(ln, 5*time.Second)
290308

291-
// Start accepting connections
292-
errorChannel := make(chan error, 1)
293-
go func() {
294-
errorChannel <- srv.fastserver.Serve(ln)
295-
}()
296-
297-
// Set new state
298-
atomic.StoreInt32(&srv.state, stateRunning)
299-
300-
// Run in background until shutdown or error
301-
go func() {
302-
select {
303-
case errCh := <-errorChannel:
304-
atomic.StoreInt32(&srv.state, stateStopping)
305-
306-
// Web server is no longer able to accept more connections
307-
if srv.listenErrorHandler != nil {
308-
srv.listenErrorHandler(srv, errCh)
309-
}
310-
311-
// handle termination signal
312-
case <-srv.startShutdownSignal:
313-
atomic.StoreInt32(&srv.state, stateStopping)
314-
315-
// Attempt the graceful shutdown by closing the listener
316-
// and completing all inflight requests.
317-
_ = srv.fastserver.Shutdown()
318-
}
319-
320-
atomic.StoreInt32(&srv.state, stateStopped)
321-
}()
309+
// Start accepting connections and run in background until shutdown or error
310+
srv.serve(ln)
322311

323312
// Done
324313
return nil
@@ -378,7 +367,6 @@ func (srv *Server) DELETE(path string, handler HandlerFunc, middlewares ...Middl
378367

379368
// ServeFiles adds custom filesystem handler for the specified route
380369
func (srv *Server) ServeFiles(path string, options ServerFilesOptions, middlewares ...MiddlewareFunc) error {
381-
382370
// Check some options
383371
if !filepath.IsAbs(options.RootDirectory) {
384372
return errors.New("absolute path must be provided")

webserver_serve.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package go_webserver
2+
3+
import (
4+
"net"
5+
"sync/atomic"
6+
)
7+
8+
// -----------------------------------------------------------------------------
9+
10+
const (
11+
stateNotStarted = 1
12+
stateStarting = 2
13+
stateRunning = 3
14+
stateStopping = 4
15+
stateStopped = 5
16+
)
17+
18+
// -----------------------------------------------------------------------------
19+
20+
func (srv *Server) serve(ln net.Listener) {
21+
ch := make(chan error, 1)
22+
23+
go func(ln net.Listener) {
24+
ch <- srv.fastserver.Serve(ln)
25+
}(ln)
26+
27+
// Set new state
28+
srv.setState(stateRunning)
29+
30+
// Run in background until shutdown or error
31+
go srv.serveLoop(ch)
32+
}
33+
34+
func (srv *Server) serveLoop(ch chan error) {
35+
select {
36+
case err := <-ch:
37+
srv.setState(stateStopping)
38+
39+
// Web server is no longer able to accept more connections
40+
if srv.listenErrorHandler != nil && err != nil {
41+
srv.listenErrorHandler(srv, err)
42+
}
43+
44+
// handle termination signal
45+
case <-srv.startShutdownSignal:
46+
srv.setState(stateStopping)
47+
48+
// Attempt the graceful shutdown by closing the listener
49+
// and completing all inflight requests.
50+
_ = srv.fastserver.Shutdown()
51+
}
52+
53+
srv.setState(stateStopped)
54+
}
55+
56+
func (srv *Server) setState(newState int32) {
57+
atomic.StoreInt32(&srv.state, newState)
58+
}

0 commit comments

Comments
 (0)