@@ -89,8 +89,19 @@ type Options struct {
89
89
// A custom handler for 404 errors
90
90
NotFoundHandler HandlerFunc
91
91
92
+ // A custom handler for 405 errors
93
+ MethodNotAllowedHandler HandlerFunc
94
+
92
95
// TLSConfig optionally provides a TLS configuration for use.
93
96
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
94
105
}
95
106
96
107
// ServerFilesOptions sets the parameters to use in a ServeFiles call
@@ -127,12 +138,6 @@ const (
127
138
defaultServerName = "go-webserver"
128
139
129
140
serveFilesSuffix = "{filepath:*}"
130
-
131
- stateNotStarted = 1
132
- stateStarting = 2
133
- stateRunning = 3
134
- stateStopping = 4
135
- stateStopped = 3
136
141
)
137
142
138
143
// -----------------------------------------------------------------------------
@@ -188,6 +193,10 @@ func Create(options Options) (*Server, error) {
188
193
parsedBindAddress = p4
189
194
}
190
195
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
+
191
200
// Create a new server container
192
201
srv := & Server {
193
202
router : router .New (),
@@ -207,7 +216,13 @@ func Create(options Options) (*Server, error) {
207
216
srv .requestErrorHandler = srv .defaultRequestErrorHandler
208
217
}
209
218
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
211
226
if options .NotFoundHandler != nil {
212
227
srv .router .NotFound = srv .createEndpointHandler (options .NotFoundHandler )
213
228
} else {
@@ -216,11 +231,14 @@ func Create(options Options) (*Server, error) {
216
231
}
217
232
}
218
233
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
+ }
224
242
225
243
// Check server name
226
244
serverName := options .Name
@@ -258,7 +276,7 @@ func Create(options Options) (*Server, error) {
258
276
// Start initiates listening
259
277
func (srv * Server ) Start () error {
260
278
if ! atomic .CompareAndSwapInt32 (& srv .state , stateNotStarted , stateStarting ) {
261
- return errors .New ("invalid state " )
279
+ return errors .New ("server is not stopped " )
262
280
}
263
281
264
282
// Create the listener
@@ -276,7 +294,7 @@ func (srv *Server) Start() error {
276
294
// Create the graceful shutdown listener
277
295
ln , err := createListener (network , address + ":" + strconv .Itoa (int (srv .bindPort )))
278
296
if err != nil {
279
- atomic . StoreInt32 ( & srv .state , stateNotStarted )
297
+ srv .setState ( stateNotStarted )
280
298
return err
281
299
}
282
300
@@ -288,37 +306,8 @@ func (srv *Server) Start() error {
288
306
// Wrap listener inside a graceful shutdown listener
289
307
ln = listener .NewGracefulListener (ln , 5 * time .Second )
290
308
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 )
322
311
323
312
// Done
324
313
return nil
@@ -378,7 +367,6 @@ func (srv *Server) DELETE(path string, handler HandlerFunc, middlewares ...Middl
378
367
379
368
// ServeFiles adds custom filesystem handler for the specified route
380
369
func (srv * Server ) ServeFiles (path string , options ServerFilesOptions , middlewares ... MiddlewareFunc ) error {
381
-
382
370
// Check some options
383
371
if ! filepath .IsAbs (options .RootDirectory ) {
384
372
return errors .New ("absolute path must be provided" )
0 commit comments