• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jeffotoni / quick / 260

19 Mar 2025 02:23PM UTC coverage: 52.974% (-0.03%) from 53.001%
260

push

circleci

jeffotoni
Commit Message

📌 Refactoring and improvements to the body limit middleware in Quick
• Implemented quick.MaxBytesReader to wrap http.MaxBytesReader.
• Fixed and improved ContentLength validation to avoid 413 errors.
• Adjustments to body reading to avoid inconsistencies in
MaxBytesReader.
• Fixed tests (q.Qtest) to ensure that Body is handled correctly.
• Improved documentation and test coverage for exact limit cases,
overflow, and empty body.

20 of 24 new or added lines in 3 files covered. (83.33%)

3 existing lines in 1 file now uncovered.

2716 of 5127 relevant lines covered (52.97%)

1949.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.74
/quick.go
1
/*
2
🚀 Quick is a flexible and extensible route manager for the Go language.
3
It aims to be fast and performant, and 100% net/http compatible.
4
Quick is a project under constant development and is open for collaboration,
5
everyone is welcome to contribute. 😍
6
*/
7
package quick
8

9
import (
10
        "bytes"
11
        "context"
12
        "crypto/tls"
13
        "embed"
14
        "encoding/json"
15
        "encoding/xml"
16
        "fmt"
17
        "io"
18
        "log"
19
        "net"
20
        "net/http"
21
        "net/http/httptest"
22
        "os/signal"
23
        "regexp"
24
        "runtime"
25
        "runtime/debug"
26
        "strings"
27
        "sync"
28
        "syscall"
29
        "time"
30

31
        "github.com/jeffotoni/quick/internal/concat"
32
)
33

34
// SO_REUSEPORT is a constant manually defined for Linux systems
35
const SO_REUSEPORT = 0x0F
36

37
// Content-Type constants used for response headers
38
const (
39
        ContentTypeAppJSON = `application/json`
40
        ContentTypeAppXML  = `application/xml`
41
        ContentTypeTextXML = `text/xml`
42
)
43

44
// contextKey is a custom type used for storing values in context
45
type contextKey int
46

47
// myContextKey is a predefined key used for context storage
48
const myContextKey contextKey = 0
49

50
// HandleFunc represents a function signature for route handlers in Quick.
51
//
52
// This function type is used for defining request handlers within Quick's
53
// routing system. It receives a pointer to `Ctx`, which encapsulates
54
// request and response data.
55
//
56
// Example Usage:
57
//
58
//        q.Get("/example", func(c *quick.Ctx) error {
59
//            return c.Status(quick.StatusOK).SendString("Hello, Quick!")
60
//        })
61
type HandleFunc func(*Ctx) error
62

63
// HandlerFunc defines the function signature for request handlers in Quick.
64
//
65
// This type provides a way to implement request handlers as standalone
66
// functions while still conforming to the `Handler` interface. It allows
67
// functions of type `HandlerFunc` to be passed as middleware or endpoint handlers.
68
//
69
// Example Usage:
70
//
71
//        func myHandler(c *quick.Ctx) error {
72
//            return c.Status(quick.StatusOK).SendString("HandlerFunc example")
73
//        }
74
//
75
//        q.Use(quick.HandlerFunc(myHandler))
76
type HandlerFunc func(c *Ctx) error
77

78
// Handler defines an interface that wraps the ServeQuick method.
79
//
80
// Any type implementing `ServeQuick(*Ctx) error` can be used as a request
81
// handler in Quick. This abstraction allows for more flexible middleware
82
// and handler implementations, including struct-based handlers.
83
//
84
// Example Usage:
85
//
86
//        type MyHandler struct{}
87
//
88
//        func (h MyHandler) ServeQuick(c *quick.Ctx) error {
89
//            return c.Status(quick.StatusOK).SendString("Struct-based handler")
90
//        }
91
//
92
//        q.Use(MyHandler{})
93
type Handler interface {
94
        // ServeQuick processes an HTTP request in the Quick framework.
95
        //
96
        // Parameters:
97
        //   - c *Ctx: The request context containing request and response details.
98
        //
99
        // Returns:
100
        //   - error: Any error encountered while processing the request.
101
        ServeQuick(*Ctx) error
102
}
103

104
// ServeQuick allows a HandlerFunc to satisfy the Handler interface.
105
//
106
// This method enables `HandlerFunc` to be used wherever a `Handler`
107
// is required by implementing the `ServeQuick` method.
108
//
109
// Example Usage:
110
//
111
//        q.Use(quick.HandlerFunc(func(c *quick.Ctx) error {
112
//            return c.Status(quick.StatusOK).SendString("Hello from HandlerFunc!")
113
//        }))
114
func (h HandlerFunc) ServeQuick(c *Ctx) error {
×
115
        return h(c)
×
116
}
×
117

118
// MaxBytesReader is a thin wrapper around http.MaxBytesReader to limit the
119
// size of the request body in Quick applications.
120
//
121
// It returns an io.ReadCloser that reads from r but stops with an error
122
// after n bytes.  The sink just sees an io.EOF.
123
//
124
// This is useful to protect against large request bodies.
125
//
126
// Example usage:
127
//
128
//        c.Request.Body = quick.MaxBytesReader(c.Response, c.Request.Body, 10_000) // 10KB
NEW
129
func MaxBytesReader(w http.ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
×
NEW
130
        // Internally, just call the standard library function.
×
NEW
131
        return http.MaxBytesReader(w, r, n)
×
NEW
132
}
×
133

134
// Route represents a registered HTTP route in the Quick framework
135
type Route struct {
136
        Group   string           // Route group for organization
137
        Pattern string           // URL pattern associated with the route
138
        Path    string           // The registered path for the route
139
        Params  string           // Parameters extracted from the URL
140
        Method  string           // HTTP method associated with the route (GET, POST, etc.)
141
        handler http.HandlerFunc // Handler function for processing the request
142
}
143

144
// ctxServeHttp represents the structure for handling HTTP requests
145
type ctxServeHttp struct {
146
        Path      string            // Requested URL path
147
        Params    string            // Query parameters from the request
148
        Method    string            // HTTP method of the request
149
        ParamsMap map[string]string // Parsed parameters mapped as key-value pairs
150
}
151

152
// Config defines various configuration options for the Quick server
153
type Config struct {
154
        BodyLimit      int64 // Deprecated: Use MaxBodySize instead
155
        MaxBodySize    int64 // Maximum request body size allowed.
156
        MaxHeaderBytes int   // Maximum number of bytes allowed in the HTTP headers.
157

158
        GOMAXPROCS      int   // defines the maximum number of CPU cores
159
        GCHeapThreshold int64 // GCHeapThreshold sets the memory threshold (in bytes)
160
        BufferPoolSize  int   // BufferPoolSize determines the size (in bytes)
161

162
        RouteCapacity     int           // Initial capacity of the route slice.
163
        MoreRequests      int           // Value to set GCPercent. influences the garbage collector performance. 0-1000
164
        ReadTimeout       time.Duration // Maximum duration for reading the entire request.
165
        WriteTimeout      time.Duration // Maximum duration before timing out writes of the response.
166
        IdleTimeout       time.Duration // Maximum amount of time to wait for the next request when keep-alives are enabled.
167
        ReadHeaderTimeout time.Duration // Amount of time allowed to read request headers.
168
        GCPercent         int           // Renamed to be more descriptive (0-1000) - influences the garbage collector performance.
169
        TLSConfig         *tls.Config   // Integrated TLS configuration
170
        CorsConfig        *CorsConfig   // Specific type for CORS
171

172
        NoBanner bool // Flag to disable the Quick startup Display.
173
}
174

175
// defaultConfig defines the default values for the Quick server configuration
176
var defaultConfig = Config{
177
        BodyLimit:      2 * 1024 * 1024, // Deprecated: Use MaxBodySize instead
178
        MaxBodySize:    2 * 1024 * 1024, // 2MB max request body size
179
        MaxHeaderBytes: 1 * 1024 * 1024, // 1MB max header size
180

181
        GOMAXPROCS:      runtime.NumCPU(), // Use all available CPU cores
182
        GCHeapThreshold: 1 << 30,          // 1GB memory threshold for GC
183
        BufferPoolSize:  32768,            // Buffer pool size
184

185
        RouteCapacity: 1000,  // Default initial route capacity
186
        MoreRequests:  290,   // Default GC value
187
        NoBanner:      false, // Show Quick banner by default
188
}
189

190
// Zeroth is a custom type for zero-value constants
191
type Zeroth int
192

193
// Zero is a predefined constant of type Zeroth
194
const (
195
        Zero Zeroth = 0
196
)
197

198
// CorsConfig defines the CORS settings for Quick
199
type CorsConfig struct {
200
        Enabled  bool              // If true, enables CORS support
201
        Options  map[string]string // Custom CORS options
202
        AllowAll bool              // If true, allows all origins
203
}
204

205
// Quick is the main structure of the framework, holding routes and configurations.
206
type Quick struct {
207
        config        Config         // Configuration settings.
208
        Cors          bool           // Indicates if CORS is enabled.
209
        groups        []Group        // List of route groups.
210
        handler       http.Handler   // The primary HTTP handler.
211
        mux           *http.ServeMux // Multiplexer for routing requests.
212
        routes        []*Route       // Registered routes.
213
        routeCapacity int            // The maximum number of routes allowed.
214
        mws2          []any          // List of registered middlewares.
215

216
        CorsSet     func(http.Handler) http.Handler // CORS middleware handler function.
217
        CorsOptions map[string]string               // CORS options map
218
        // corsConfig    *CorsConfig // Specific type for CORS // Removed unused field
219
        embedFS embed.FS     // File system for embedded static files.
220
        server  *http.Server // Http server
221

222
        bufferPool *sync.Pool
223
}
224

225
// indeed to Quick
226
type App = Quick
227

228
// HandlerFunc adapts a quick.HandlerFunc to a standard http.HandlerFunc.
229
// It creates a new Quick context (Ctx) for each HTTP request,
230
// allowing Quick handlers to access request and response objects seamlessly.
231
//
232
// Usage Example:
233
//
234
//        http.HandleFunc(\"/\", app.HandlerFunc(func(c *quick.Ctx) error {
235
//                return c.Status(200).JSON(map[string]string{\"message\": \"Hello, Quick!\"})
236
//        }))
237
func (q *Quick) HandlerFunc(h HandlerFunc) http.HandlerFunc {
×
238
        return func(w http.ResponseWriter, req *http.Request) {
×
239
                c := &Ctx{
×
240
                        Response: w,
×
241
                        Request:  req,
×
242
                        App:      q,
×
243
                }
×
244

×
245
                if err := h(c); err != nil {
×
246
                        http.Error(w, err.Error(), StatusInternalServerError)
×
247
                }
×
248
        }
249
}
250

251
// Handler returns the main HTTP handler for Quick, allowing integration with standard http.Server and testing frameworks.
252
func (q *Quick) Handler() http.Handler {
×
253
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
×
254
                q.ServeHTTP(w, r)
×
255
        })
×
256
}
257

258
// MiddlewareFunc defines the signature for middleware functions in Quick.
259
// A middleware function receives the next HandlerFunc in the chain and returns a new HandlerFunc.
260
// Middleware can perform actions before and/or after calling the next handler.
261
//
262
// Example:
263
//
264
//        func LoggingMiddleware() quick.MiddlewareFunc {
265
//                return func(next quick.HandlerFunc) quick.HandlerFunc {
266
//                        return func(c *quick.Ctx) error {
267
//                                // Before handler logic (e.g., logging request details)
268
//                                log.Printf("Request received: %s %s", c.Request.Method, c.Request.URL)
269
//
270
//                                err := next(c) // Call the next handler
271
//
272
//                                // After handler logic (e.g., logging response status)
273
//                                log.Printf("Response sent with status: %d", c.ResponseWriter.Status())
274
//
275
//                                return err
276
//                        }
277
//                }
278
//        }
279
type MiddlewareFunc func(next HandlerFunc) HandlerFunc
280

281
// GetDefaultConfig returns the default configuration pre-defined for the system.
282
//
283
// This function provides a standardized configuration setup, ensuring that
284
// new instances use a consistent and optimized set of defaults.
285
//
286
// Returns:
287
//   - Config: A struct containing the default system configuration.
288
//
289
// Example Usage:
290
//
291
//        // This function is typically used when initializing a new Quick instance
292
//        // to ensure it starts with the default settings if no custom config is provided.
293
func GetDefaultConfig() Config {
1✔
294
        return defaultConfig
1✔
295
}
1✔
296

297
// New creates a new instance of the Quick structure to manage HTTP routes and handlers.
298
//
299
// This function initializes a Quick instance with optional configurations provided
300
// through the `Config` parameter. If no configuration is provided, it uses the `defaultConfig`.
301
//
302
// Parameters:
303
//   - c ...Config: (Optional) Configuration settings for customizing the Quick instance.
304
//
305
// Returns:
306
//   - *Quick: A pointer to the initialized Quick instance.
307
//
308
// Example Usage:
309
//
310
//        // Basic usage - Create a default Quick instance
311
//        q := quick.New()
312
//
313
//        // Custom usage - Create a Quick instance with specific configurations
314
//        q := quick.New(quick.Config{
315
//                RouteCapacity: 500,
316
//        })
317
//
318
//        q.Get("/", func(c quick.Ctx) error {
319
//                return c.SendString("Hello, Quick!")
320
//        })
321
func New(c ...Config) *Quick {
88✔
322
        var config Config
88✔
323
        // Check if a custom configuration is provided
88✔
324
        if len(c) > 0 {
94✔
325
                config = c[0] // Use the provided configuration
6✔
326
        } else {
88✔
327
                config = defaultConfig // Use the default configuration
82✔
328
        }
82✔
329

330
        // Ensure a minimum route capacity if not set
331
        if config.RouteCapacity == 0 {
92✔
332
                config.RouteCapacity = 1000
4✔
333
        }
4✔
334

335
        // Initialize and return the Quick instance
336
        return &Quick{
88✔
337
                routes:        make([]*Route, 0, config.RouteCapacity),
88✔
338
                routeCapacity: config.RouteCapacity,
88✔
339
                mux:           http.NewServeMux(),
88✔
340
                handler:       http.NewServeMux(),
88✔
341
                config:        config,
88✔
342
        }
88✔
343
}
344

345
// Use function adds middleware to the Quick server, with special treatment for CORS.
346
//
347
// This method allows adding custom middleware functions to process requests before they
348
// reach the final handler. If a CORS middleware is detected, it is automatically applied.
349
//
350
// Parameters:
351
//   - mw any: Middleware function to be added. It must be of type `func(http.Handler) http.Handler`.
352
//
353
// Example Usage:
354
//
355
//        q := quick.New()
356
//
357
//        q.Use(maxbody.New(50000))
358
//
359
//        q.Post("/v1/user/maxbody/any", func(c *quick.Ctx) error {
360
//            c.Set("Content-Type", "application/json")//
361
//            return c.Status(200).Send(c.Body())
362
//        })
363
func (q *Quick) Use(mw any) {
4✔
364
        switch mwc := mw.(type) {
4✔
365
        case func(http.Handler) http.Handler:
3✔
366
                // Detect if the middleware is related to CORS and apply it separately
3✔
367
                if isCorsMiddleware(mwc) {
3✔
368
                        q.Cors = true
×
369
                        q.CorsSet = mwc
×
370
                        return
×
371
                }
×
372

373
        case func(HandleFunc) HandleFunc:
×
374
                q.mws2 = append(q.mws2, mwc)
×
375

376
        }
377

378
        // Append middleware to the list of registered middlewares
379
        q.mws2 = append(q.mws2, mw)
4✔
380

381
}
382

383
// isCorsMiddleware checks whether the provided middleware function is a CORS handler.
384
//
385
// This function detects if a middleware is handling CORS by sending an
386
// HTTP OPTIONS request and checking if it sets the `Access-Control-Allow-Origin` header.
387
//
388
// Parameters:
389
//   - mw func(http.Handler) http.Handler: The middleware function to be tested.
390
//
391
// Returns:
392
//   - bool: `true` if the middleware is identified as CORS, `false` otherwise.
393
//
394
// Example Usage:
395
// This function is automatically executed when a middleware is added to detect if it's a CORS handler.
396
func isCorsMiddleware(mw func(http.Handler) http.Handler) bool {
3✔
397
        testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
6✔
398
        testRequest := httptest.NewRequest("OPTIONS", "/", nil)
3✔
399
        testResponse := httptest.NewRecorder()
3✔
400

3✔
401
        mw(testHandler).ServeHTTP(testResponse, testRequest)
3✔
402

3✔
403
        // If the middleware sets Access-Control-Allow-Origin, it's CORS
3✔
404
        return testResponse.Header().Get("Access-Control-Allow-Origin") != ""
3✔
405
}
406

407
// clearRegex processes a route pattern, removing dynamic path parameters
408
// and replacing them with a simplified placeholder.
409
//
410
// This function is used internally to standardize dynamic routes in
411
// ServeMux, converting patterns like `/v1/user/{id:[0-9]+}` into
412
// `/v1/user/_id_`, making them easier to process.
413
//
414
// Parameters:
415
//   - route string: The route pattern containing dynamic parameters.
416
//
417
// Returns:
418
//   - string: A cleaned-up version of the route with placeholders instead of regex patterns.
419
//
420
// Example Usage:
421
// This function is automatically triggered internally to normalize route patterns.
422
func clearRegex(route string) string {
71✔
423
        // Here you transform "/v1/user/{id:[0-9]+}"
71✔
424
        // into something simple, like "/v1/user/_id_"
71✔
425
        // You can get more sophisticated if you want
71✔
426
        var re = regexp.MustCompile(`\{[^/]+\}`)
71✔
427
        return re.ReplaceAllStringFunc(route, func(s string) string {
72✔
428
                // s is "{id:[0-9]+}"
1✔
429
                // Let's just replace it with "_id_"
1✔
430
                // or any string that doesn't contain ":" or "{ }"
1✔
431
                return "_" + strings.Trim(s, "{}") + "_"
1✔
432
                //return "_" + strings.ReplaceAll(strings.ReplaceAll(strings.Trim(s, "{}"), ":", "_"), "[", "_") + "_"
1✔
433
        })
1✔
434
}
435

436
// registerRoute is a helper function that centralizes the logic for registering routes.
437
//
438
// This function processes and registers an HTTP route, ensuring no duplicate routes
439
// are added. It extracts route parameters, formats the route, and associates the
440
// appropriate handler function.
441
//
442
// Parameters:
443
//   - method string: The HTTP method (e.g., "GET", "POST").
444
//   - pattern string: The route pattern, which may include dynamic parameters.
445
//   - handlerFunc HandleFunc: The function that will handle the route.
446
//
447
// Example Usage:
448
// This function is automatically triggered internally when a new route is added.
449
func (q *Quick) registerRoute(method, pattern string, handlerFunc HandleFunc) {
71✔
450
        path, params, patternExist := extractParamsPattern(pattern)
71✔
451
        formattedPath := concat.String(strings.ToLower(method), "#", clearRegex(pattern))
71✔
452

71✔
453
        for _, route := range q.routes {
118✔
454
                if route.Method == method && route.Path == path {
47✔
455
                        fmt.Printf("Warning: Route '%s %s' is already registered, ignoring duplicate registration.\n", method, path)
×
456
                        return // Ignore duplication instead of generating panic
×
457
                }
×
458
        }
459

460
        route := Route{
71✔
461
                Pattern: patternExist,
71✔
462
                Path:    path,
71✔
463
                Params:  params,
71✔
464
                handler: extractHandler(q, method, path, params, handlerFunc),
71✔
465
                Method:  method,
71✔
466
        }
71✔
467

71✔
468
        q.appendRoute(&route)
71✔
469
        q.mux.HandleFunc(formattedPath, route.handler)
71✔
470

471
}
472

473
// handleOptions processes HTTP OPTIONS requests for CORS preflight checks.
474
// This function is automatically called before routing when an OPTIONS request is received.
475
// It ensures that the appropriate CORS headers are included in the response.
476
//
477
// If CORS middleware is enabled, it applies the middleware before setting default headers.
478
//
479
// Headers added by this function:
480
// - Access-Control-Allow-Origin: Allows cross-origin requests (set dynamically).
481
// - Access-Control-Allow-Methods: Specifies allowed HTTP methods (GET, POST, PUT, DELETE, OPTIONS).
482
// - Access-Control-Allow-Headers: Defines which headers are allowed in the request.
483
//
484
// If no Origin header is provided in the request, a 204 No Content response is returned.
485
//
486
// Parameters:
487
// - w: http.ResponseWriter – The response writer to send headers and status.
488
// - r: *http.Request – The incoming HTTP request.
489
//
490
// Response:
491
// - 204 No Content (if the request is valid and processed successfully).
492
//
493
// Example Usage:
494
// This function is automatically triggered in `ServeHTTP()` when an OPTIONS request is received.
495
func (q *Quick) handleOptions(w http.ResponseWriter, r *http.Request) {
2✔
496
        origin := r.Header.Get("Origin")
2✔
497
        if origin == "" {
4✔
498
                w.WriteHeader(StatusNoContent)
2✔
499
                return
2✔
500
        }
2✔
501

502
        // Apply CORS middleware before setting headers
503
        if q.Cors && q.CorsSet != nil {
×
504
                q.CorsSet(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})).ServeHTTP(w, r)
×
505
        }
506

507
        // Set default CORS headers
508
        w.Header().Set("Allow", "GET, POST, PUT, DELETE, OPTIONS")
×
509
        w.Header().Set("Access-Control-Allow-Origin", "*") // Ajustável pelo middleware
×
510
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
×
511
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
×
512

×
513
        w.WriteHeader(http.StatusNoContent) // Returns 204 No Content
×
514
}
515

516
// Get registers an HTTP route with the GET method on the Quick server.
517
//
518
// This function associates a GET request with a specific route pattern and handler function.
519
// It ensures that the request is properly processed when received.
520
//
521
// Parameters:
522
//   - pattern string: The route pattern (e.g., "/users/:id").
523
//   - handlerFunc HandleFunc: The function that will handle the GET request.
524
//
525
// Example Usage:
526
//
527
//        // This function is automatically triggered when defining a GET route in Quick.
528
func (q *Quick) Get(pattern string, handlerFunc HandleFunc) {
33✔
529
        q.registerRoute(MethodGet, pattern, handlerFunc)
33✔
530
}
33✔
531

532
// Post registers an HTTP route with the POST method on the Quick server.
533
//
534
// This function associates a POST request with a specific route pattern and handler function.
535
// It is typically used for handling form submissions, JSON payloads, or data creation.
536
//
537
// Parameters:
538
//   - pattern string: The route pattern (e.g., "/users").
539
//   - handlerFunc HandleFunc: The function that will handle the POST request.
540
//
541
// Example Usage:
542
//
543
//        // This function is automatically triggered when defining a POST route in Quick.
544
func (q *Quick) Post(pattern string, handlerFunc HandleFunc) {
18✔
545
        q.registerRoute(MethodPost, pattern, handlerFunc)
18✔
546
}
18✔
547

548
// Put registers an HTTP route with the PUT method on the Quick server.
549
//
550
// This function associates a PUT request with a specific route pattern and handler function.
551
// It is typically used for updating existing resources.
552
//
553
// Parameters:
554
//   - pattern string: The route pattern (e.g., "/users/:id").
555
//   - handlerFunc HandleFunc: The function that will handle the PUT request.
556
//
557
// Example Usage:
558
//
559
//        // This function is automatically triggered when defining a PUT route in Quick.
560
func (q *Quick) Put(pattern string, handlerFunc HandleFunc) {
8✔
561
        q.registerRoute(MethodPut, pattern, handlerFunc)
8✔
562
}
8✔
563

564
// Delete registers an HTTP route with the DELETE method on the Quick server.
565
//
566
// This function associates a DELETE request with a specific route pattern and handler function.
567
// It is typically used for deleting existing resources.
568
//
569
// Parameters:
570
//   - pattern string: The route pattern (e.g., "/users/:id").
571
//   - handlerFunc HandleFunc: The function that will handle the DELETE request.
572
//
573
// Example Usage:
574
//
575
//        // This function is automatically triggered when defining a DELETE route in Quick.
576
func (q *Quick) Delete(pattern string, handlerFunc HandleFunc) {
8✔
577
        q.registerRoute(MethodDelete, pattern, handlerFunc)
8✔
578
}
8✔
579

580
// Patch registers an HTTP route with the PATCH method on the Quick server.
581
//
582
// This function associates a PATCH request with a specific route pattern and handler function.
583
// It is typically used for applying partial updates to an existing resource.
584
//
585
// Parameters:
586
//   - pattern string: The route pattern (e.g., "/users/:id").
587
//   - handlerFunc HandleFunc: The function that will handle the PATCH request.
588
//
589
// Example Usage:
590
//
591
//        // This function is automatically triggered when defining a PATCH route in Quick.
592
func (q *Quick) Patch(pattern string, handlerFunc HandleFunc) {
2✔
593
        q.registerRoute(MethodPatch, pattern, handlerFunc)
2✔
594
}
2✔
595

596
// Options registers an HTTP route with the OPTIONS method on the Quick server.
597
//
598
// This function associates an OPTIONS request with a specific route pattern and handler function.
599
// OPTIONS requests are typically used to determine the allowed HTTP methods for a resource.
600
//
601
// Parameters:
602
//   - pattern string: The route pattern (e.g., "/users").
603
//   - handlerFunc HandleFunc: The function that will handle the OPTIONS request.
604
//
605
// Example Usage:
606
//
607
//        // This function is automatically triggered when defining an OPTIONS route in Quick.
608
func (q *Quick) Options(pattern string, handlerFunc HandleFunc) {
2✔
609
        q.registerRoute(MethodOptions, pattern, handlerFunc)
2✔
610
}
2✔
611

612
// extractHandler selects the appropriate handler function for different HTTP methods.
613
//
614
// This function is responsible for determining which internal request processing function
615
// should handle a given HTTP method. It maps the method to the corresponding request parser.
616
//
617
// Parameters:
618
//   - q *Quick: The Quick instance managing the route and request context.
619
//   - method string: The HTTP method (e.g., "GET", "POST").
620
//   - path string: The route path associated with the request.
621
//   - params string: Route parameters extracted from the request URL.
622
//   - handlerFunc HandleFunc: The function that will handle the request.
623
//
624
// Returns:
625
//   - http.HandlerFunc: The appropriate handler function based on the HTTP method.
626
//
627
// Example Usage:
628
//
629
//        // This function is automatically executed internally when processing an HTTP request.
630
func extractHandler(q *Quick, method, path, params string, handlerFunc HandleFunc) http.HandlerFunc {
72✔
631
        switch method {
72✔
632
        case MethodGet:
33✔
633
                return extractParamsGet(q, path, params, handlerFunc)
33✔
634
        case MethodPost:
18✔
635
                return extractParamsPost(q, handlerFunc)
18✔
636
        case MethodPut:
8✔
637
                return extractParamsPut(q, handlerFunc)
8✔
638
        case MethodDelete:
8✔
639
                return extractParamsDelete(q, handlerFunc)
8✔
640
        case MethodPatch:
2✔
641
                return extractParamsPatch(q, handlerFunc) // same as PUT
2✔
642
        case MethodOptions:
2✔
643
                return extractParamsOptions(q, method, path, handlerFunc)
2✔
644
        }
645
        return nil
1✔
646
}
647

648
// extractParamsPatch processes an HTTP PATCH request by reusing the logic of the PUT method.
649
//
650
// The PATCH method is typically used for partial updates, while PUT replaces an entire resource.
651
// However, both methods often handle request parameters and body parsing in the same way,
652
// so this function delegates the processing to `extractParamsPut`.
653
//
654
// Parameters:
655
//   - q *Quick: The Quick instance managing the request context.
656
//   - handlerFunc HandleFunc: The function that will handle the PATCH request.
657
//
658
// Returns:
659
//   - http.HandlerFunc: A handler function that processes PATCH requests.
660
//
661
// Example Usage:
662
//
663
//        // This function is automatically executed internally when a PATCH request is received.
664
func extractParamsPatch(q *Quick, handlerFunc HandleFunc) http.HandlerFunc {
2✔
665
        return extractParamsPut(q, handlerFunc)
2✔
666
}
2✔
667

668
// extractParamsOptions processes an HTTP OPTIONS request, setting appropriate
669
// headers to handle CORS preflight requests. It reuses a pooled Ctx instance
670
// for optimized memory usage and performance.
671
//
672
// If a handlerFunc is provided, it executes that handler with the pooled context.
673
// If no handlerFunc is given, it responds with HTTP 204 No Content.
674
//
675
// Parameters:
676
//   - q: The Quick instance providing configurations and routing context.
677
//   - method: The HTTP method being handled (typically "OPTIONS").
678
//   - path: The route path being handled.
679
//   - handlerFunc: An optional handler to execute for the OPTIONS request.
680
//
681
// Returns:
682
//   - http.HandlerFunc: A handler function optimized for handling OPTIONS requests.
683
func extractParamsOptions(q *Quick, method, path string, handlerFunc HandleFunc) http.HandlerFunc {
4✔
684
        return func(w http.ResponseWriter, r *http.Request) {
6✔
685
                // Acquire a pooled context
2✔
686
                ctx := acquireCtx()
2✔
687
                defer releaseCtx(ctx) // Ensure context is returned to the pool after handling
2✔
688

2✔
689
                // Populate the pooled context
2✔
690
                ctx.Response = w
2✔
691
                ctx.Request = r
2✔
692
                ctx.MoreRequests = q.config.MoreRequests
2✔
693

2✔
694
                if q.Cors && q.CorsSet != nil {
2✔
695
                        wrappedHandler := q.CorsSet(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
×
696
                                // Middleware CORS apply success
×
697
                        }))
×
698
                        wrappedHandler.ServeHTTP(w, r)
×
699
                }
700

701
                if ctx.Response.Header().Get("Access-Control-Allow-Origin") == "" {
4✔
702
                        allowMethods := []string{MethodGet, MethodPost, MethodPut, MethodDelete, MethodPatch, MethodOptions}
2✔
703
                        ctx.Set("Allow", strings.Join(allowMethods, ", "))
2✔
704
                        ctx.Set("Access-Control-Allow-Origin", "*")
2✔
705
                        ctx.Set("Access-Control-Allow-Methods", strings.Join(allowMethods, ", "))
2✔
706
                        ctx.Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
2✔
707
                }
2✔
708

709
                // Execute handler function if provided
710
                if handlerFunc != nil {
3✔
711
                        if err := handlerFunc(ctx); err != nil {
2✔
712
                                http.Error(w, err.Error(), StatusInternalServerError)
1✔
713
                        }
1✔
714
                } else {
1✔
715
                        w.WriteHeader(StatusNoContent) // 204 No Content if no handlerFunc
1✔
716
                }
1✔
717
        }
718
}
719

720
// extractHeaders extracts all headers from an HTTP request and returns them as a map.
721
//
722
// This function iterates over all headers in the request and organizes them into a
723
// map structure, where each header key is mapped to its corresponding values.
724
//
725
// Parameters:
726
//   - req http.Request: The HTTP request from which headers will be extracted.
727
//
728
// Returns:
729
//   - map[string][]string: A map containing all request headers.
730
//
731
// Example Usage:
732
//
733
//        // This function is automatically executed internally when extracting headers from a request.
734
func extractHeaders(req http.Request) map[string][]string {
90✔
735
        headersMap := make(map[string][]string)
90✔
736
        for key, values := range req.Header {
151✔
737
                headersMap[key] = values
61✔
738
        }
61✔
739
        return headersMap
90✔
740
}
741

742
// extractParamsBind decodes request bodies for JSON/XML payloads using a pooled buffer
743
// to minimize memory allocations and garbage collection overhead.
744
//
745
// This function checks the request's `Content-Type` and processes JSON or XML payloads accordingly.
746
// It ensures efficient memory usage by leveraging buffer pools for reading request bodies.
747
//
748
// Parameters:
749
//   - c *Ctx: The Quick context containing request information.
750
//   - v interface{}: The target structure where the decoded JSON/XML data will be stored.
751
//
752
// Returns:
753
//   - error: Returns any decoding errors encountered or an error for unsupported content types.
754
//
755
// Example Usage:
756
//
757
//        // This function is automatically executed internally when binding request data to a struct.
758
func extractParamsBind(c *Ctx, v interface{}) error {
8✔
759
        contentType := strings.ToLower(c.Request.Header.Get("Content-Type"))
8✔
760

8✔
761
        // Check supported Content-Type
8✔
762
        if !strings.HasPrefix(contentType, ContentTypeAppJSON) &&
8✔
763
                !strings.HasPrefix(contentType, ContentTypeAppXML) &&
8✔
764
                !strings.HasPrefix(contentType, ContentTypeTextXML) {
9✔
765
                return fmt.Errorf("unsupported content type: %s", contentType)
1✔
766
        }
1✔
767

768
        switch {
7✔
769
        case strings.HasPrefix(contentType, ContentTypeAppJSON):
4✔
770

4✔
771
                // Acquire pooled buffer
4✔
772
                buf := acquireJSONBuffer()
4✔
773
                defer releaseJSONBuffer(buf)
4✔
774

4✔
775
                // Read body content into buffer
4✔
776
                if _, err := buf.ReadFrom(c.Request.Body); err != nil {
5✔
777
                        return err
1✔
778
                }
1✔
779

780
                // Reset the Request.Body after reading, enabling re-reads if needed
781
                c.Request.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
3✔
782

3✔
783
                return json.Unmarshal(buf.Bytes(), v)
3✔
784
        case strings.HasPrefix(contentType, ContentTypeAppXML), strings.HasPrefix(contentType, ContentTypeTextXML):
3✔
785

3✔
786
                // Acquire pooled buffer
3✔
787
                buf := acquireXMLBuffer()
3✔
788
                defer releaseXMLBuffer(buf)
3✔
789

3✔
790
                // Read body content into buffer
3✔
791
                if _, err := buf.ReadFrom(c.Request.Body); err != nil {
3✔
792
                        return err
×
793
                }
×
794

795
                // Reset the Request.Body after reading, enabling re-reads if needed
796
                c.Request.Body = io.NopCloser(bytes.NewReader(buf.Bytes()))
3✔
797

3✔
798
                return xml.Unmarshal(buf.Bytes(), v)
3✔
799
        default:
×
800
                return fmt.Errorf("unsupported content type: %s", contentType)
×
801
        }
802
}
803

804
// extractParamsPattern extracts the fixed path and dynamic parameters from a given route pattern.
805
//
806
// This function is responsible for identifying and separating static paths from dynamic parameters
807
// in a route pattern. It ensures proper extraction of URL path segments and dynamic query parameters.
808
//
809
// Parameters:
810
//   - pattern string: The route pattern that may contain dynamic parameters.
811
//
812
// Returns:
813
//   - path string: The fixed portion of the route without dynamic parameters.
814
//   - params string: The extracted dynamic parameters (if any).
815
//   - patternExist string: The original pattern before extraction.
816
//
817
// Example Usage:
818
//
819
//        // This function is automatically executed internally when registering a dynamic route.
820
//        path, params, patternExist := extractParamsPattern("/users/:id")
821
func extractParamsPattern(pattern string) (path, params, partternExist string) {
84✔
822
        path = pattern
84✔
823
        index := strings.Index(pattern, ":")
84✔
824

84✔
825
        if index > 0 {
99✔
826
                path = pattern[:index]
15✔
827
                path = strings.TrimSuffix(path, "/")
15✔
828
                if index == 1 {
16✔
829
                        path = "/"
1✔
830
                }
1✔
831
                params = strings.TrimPrefix(pattern, path)
15✔
832
                partternExist = pattern
15✔
833
        }
834

835
        return
84✔
836
}
837

838
// extractParamsGet processes an HTTP GET request for a dynamic route,
839
// extracting query parameters, headers, and handling the request using
840
// the provided handler function.
841
//
842
// This function ensures efficient processing by leveraging a pooled
843
// Ctx instance, which minimizes memory allocations and reduces garbage
844
// collection overhead.
845
//
846
// The request context (`myContextKey`) is retrieved to extract dynamic
847
// parameters mapped to the route.
848
//
849
// Parameters:
850
//   - q: The Quick instance that provides configurations and routing context.
851
//   - pathTmp: The template path used for dynamic route matching.
852
//   - paramsPath: The actual path used to extract route parameters.
853
//   - handlerFunc: The function that processes the HTTP request.
854
//
855
// Returns:
856
//   - http.HandlerFunc: A function that processes the request efficiently.
857
func extractParamsGet(q *Quick, pathTmp, paramsPath string, handlerFunc HandleFunc) http.HandlerFunc {
36✔
858
        return func(w http.ResponseWriter, req *http.Request) {
71✔
859
                // Acquire a context from the pool
35✔
860
                ctx := acquireCtx()
35✔
861
                defer releaseCtx(ctx)
35✔
862

35✔
863
                // Retrieve the custom context from the request (myContextKey)
35✔
864
                v := req.Context().Value(myContextKey)
35✔
865
                if v == nil {
36✔
866
                        http.NotFound(w, req)
1✔
867
                        return
1✔
868
                }
1✔
869

870
                cval := v.(ctxServeHttp)
34✔
871

34✔
872
                // Fill the pooled context with request-specific data
34✔
873
                ctx.Response = w
34✔
874
                ctx.Request = req
34✔
875
                ctx.Params = cval.ParamsMap
34✔
876

34✔
877
                // Initialize Query and Headers maps properly
34✔
878
                ctx.Query = make(map[string]string)
34✔
879
                for key, val := range req.URL.Query() {
37✔
880
                        ctx.Query[key] = val[0]
3✔
881
                }
3✔
882

883
                ctx.Headers = extractHeaders(*req)
34✔
884
                ctx.MoreRequests = q.config.MoreRequests
34✔
885

34✔
886
                // Execute the handler function using the pooled context
34✔
887
                execHandleFunc(ctx, handlerFunc)
34✔
888
        }
889
}
890

891
// extractParamsPost processes an HTTP POST request, extracting the request body
892
// and headers and handling the request using the provided handler function.
893
//
894
// This function ensures that the request body is within the allowed size limit,
895
// extracts headers, and reuses a pooled Ctx instance to optimize memory usage.
896
//
897
// Parameters:
898
//   - q: The Quick instance that provides configurations and routing context.
899
//   - handlerFunc: The function that processes the HTTP request.
900
//
901
// Returns:
902
//   - http.HandlerFunc: A handler function that processes the request efficiently.
903
func extractParamsPost(q *Quick, handlerFunc HandleFunc) http.HandlerFunc {
24✔
904
        return func(w http.ResponseWriter, req *http.Request) {
56✔
905
                // Validate body size before processing
32✔
906
                // req.Body = http.MaxBytesReader(w, req.Body, q.config.MaxBodySize)
32✔
907
                if req.ContentLength > q.config.MaxBodySize {
33✔
908
                        http.Error(w, "Request body too large", StatusRequestEntityTooLarge)
1✔
909
                        return
1✔
910
                }
1✔
911

912
                // Acquire a pooled context for request processing
913
                ctx := acquireCtx()
31✔
914
                defer releaseCtx(ctx) // Ensure the context is returned to the pool after execution
31✔
915

31✔
916
                // Retrieve the custom context from the request
31✔
917
                v := req.Context().Value(myContextKey)
31✔
918
                if v == nil {
32✔
919
                        http.NotFound(w, req) // Return 404 if no context value is found
1✔
920
                        return
1✔
921
                }
1✔
922

923
                // Extract headers into the pooled Ctx
924
                ctx.Headers = extractHeaders(*req)
30✔
925

30✔
926
                // Read the request body while minimizing allocations
30✔
927
                bodyBytes, bodyReader := extractBodyBytes(req.Body)
30✔
928

30✔
929
                // Populate the Ctx with relevant data
30✔
930
                ctx.Response = w
30✔
931
                ctx.Request = req
30✔
932
                ctx.bodyByte = bodyBytes
30✔
933
                ctx.MoreRequests = q.config.MoreRequests
30✔
934

30✔
935
                // Reset `Request.Body` with the new bodyReader to allow re-reading
30✔
936
                ctx.Request.Body = bodyReader
30✔
937

30✔
938
                // Execute the handler function using the pooled context
30✔
939
                execHandleFunc(ctx, handlerFunc)
30✔
940
        }
941
}
942

943
// extractParamsPut processes an HTTP PUT request, extracting the request body,
944
// headers, and route parameters while efficiently reusing a pooled Ctx instance.
945
//
946
// This function ensures that the request body does not exceed the configured
947
// size limit, extracts headers, and minimizes memory allocations by leveraging
948
// a preallocated Ctx from the sync.Pool.
949
//
950
// Parameters:
951
//   - q: The Quick instance that provides configurations and routing context.
952
//   - handlerFunc: The function that processes the HTTP request.
953
//
954
// Returns:
955
//   - http.HandlerFunc: A function that processes the request efficiently.
956
func extractParamsPut(q *Quick, handlerFunc HandleFunc) http.HandlerFunc {
15✔
957
        return func(w http.ResponseWriter, req *http.Request) {
31✔
958
                // Validate body size before processing
16✔
959
                if req.ContentLength > q.config.MaxBodySize {
17✔
960
                        http.Error(w, "Request body too large", StatusRequestEntityTooLarge)
1✔
961
                        return
1✔
962
                }
1✔
963

964
                // Acquire a pooled context for request processing
965
                ctx := acquireCtx()
15✔
966
                defer releaseCtx(ctx) // Ensure the context is returned to the pool after execution
15✔
967

15✔
968
                // Retrieve the custom context from the request
15✔
969
                v := req.Context().Value(myContextKey)
15✔
970
                if v == nil {
16✔
971
                        http.NotFound(w, req) // Return 404 if no context value is found
1✔
972
                        return
1✔
973
                }
1✔
974

975
                cval := v.(ctxServeHttp)
14✔
976

14✔
977
                // Extract headers into the pooled Ctx
14✔
978
                ctx.Headers = extractHeaders(*req)
14✔
979

14✔
980
                // Read the request body while minimizing allocations
14✔
981
                bodyBytes, bodyReader := extractBodyBytes(req.Body)
14✔
982

14✔
983
                // Populate the Ctx with relevant data
14✔
984
                ctx.Response = w
14✔
985
                ctx.Request = req
14✔
986
                ctx.bodyByte = bodyBytes
14✔
987
                ctx.Params = cval.ParamsMap
14✔
988
                ctx.MoreRequests = q.config.MoreRequests
14✔
989

14✔
990
                // Reset `Request.Body` with the new bodyReader to allow re-reading
14✔
991
                ctx.Request.Body = bodyReader
14✔
992

14✔
993
                // Execute the handler function using the pooled context
14✔
994
                execHandleFunc(ctx, handlerFunc)
14✔
995
        }
996
}
997

998
// extractParamsDelete processes an HTTP DELETE request, extracting request parameters
999
// and headers before executing the provided handler function.
1000
//
1001
// This function optimizes memory usage by reusing a pooled Ctx instance,
1002
// reducing unnecessary allocations and garbage collection overhead.
1003
//
1004
// Parameters:
1005
//   - q: The Quick instance that provides configurations and routing context.
1006
//   - handlerFunc: The function that processes the HTTP request.
1007
//
1008
// Returns:
1009
//   - http.HandlerFunc: A function that processes the request efficiently.
1010
func extractParamsDelete(q *Quick, handlerFunc HandleFunc) http.HandlerFunc {
12✔
1011
        return func(w http.ResponseWriter, req *http.Request) {
25✔
1012
                // Acquire a pooled context for request processing
13✔
1013
                ctx := acquireCtx()
13✔
1014
                defer releaseCtx(ctx) // Ensure the context is returned to the pool after execution
13✔
1015

13✔
1016
                // Retrieve the custom context from the request
13✔
1017
                v := req.Context().Value(myContextKey)
13✔
1018
                if v == nil {
14✔
1019
                        http.NotFound(w, req) // Return 404 if no context value is found
1✔
1020
                        return
1✔
1021
                }
1✔
1022

1023
                cval := v.(ctxServeHttp)
12✔
1024

12✔
1025
                // Extract headers into the pooled Ctx
12✔
1026
                ctx.Headers = extractHeaders(*req)
12✔
1027

12✔
1028
                // Populate the Ctx with relevant data
12✔
1029
                ctx.Response = w
12✔
1030
                ctx.Request = req
12✔
1031
                ctx.Params = cval.ParamsMap
12✔
1032
                ctx.MoreRequests = q.config.MoreRequests
12✔
1033

12✔
1034
                // Execute the handler function using the pooled context
12✔
1035
                execHandleFunc(ctx, handlerFunc)
12✔
1036
        }
1037
}
1038

1039
// execHandleFunc executes the provided handler function and handles errors if they occur.
1040
//
1041
// This function ensures that the HTTP response is properly handled, including setting the
1042
// appropriate content type and returning an error message if the handler function fails.
1043
//
1044
// Parameters:
1045
//   - c *Ctx: The Quick context instance containing request and response data.
1046
//   - handleFunc HandleFunc: The function that processes the HTTP request.
1047
//
1048
// Example Usage:
1049
//
1050
//        // This function is automatically executed internally after processing a request.
1051
func execHandleFunc(c *Ctx, handleFunc HandleFunc) {
92✔
1052
        err := handleFunc(c)
92✔
1053
        if err != nil {
93✔
1054
                c.Set("Content-Type", "text/plain; charset=utf-8")
1✔
1055
                // #nosec G104
1✔
1056
                c.Status(500).SendString(err.Error())
1✔
1057
        }
1✔
1058
}
1059

1060
// extractBodyBytes reads the entire request body into a pooled buffer, then
1061
// copies the data to a new byte slice before returning it. This ensures that
1062
// once the buffer is returned to the pool, the returned data remains valid.
1063
//
1064
// Additionally, this function returns a new `io.ReadCloser` wrapping the same data,
1065
// allowing it to be re-read if needed.
1066
//
1067
// Parameters:
1068
//   - r io.ReadCloser: The original request body stream.
1069
//
1070
// Returns:
1071
//   - []byte: A byte slice containing the full request body data.
1072
//   - io.ReadCloser: A new ReadCloser that allows the body to be re-read.
1073
//
1074
// Example Usage:
1075
//
1076
//        // Read the request body into a byte slice and obtain a new ReadCloser.
1077
func extractBodyBytes(r io.ReadCloser) ([]byte, io.ReadCloser) {
45✔
1078
        // Acquire a reusable buffer from the pool
45✔
1079
        buf := acquireBuffer()
45✔
1080
        defer releaseBuffer(buf)
45✔
1081

45✔
1082
        // Read all data from the request body into the buffer
45✔
1083
        _, err := buf.ReadFrom(r)
45✔
1084
        if err != nil {
46✔
1085
                // If there's an error, return an empty NopCloser
1✔
1086
                // so downstream logic can handle gracefully.
1✔
1087
                return nil, io.NopCloser(bytes.NewBuffer(nil))
1✔
1088
        }
1✔
1089

1090
        // Copy the data from the buffer into a separate byte slice.
1091
        // This step is crucial because once the buffer is released
1092
        // back to the pool, its underlying memory can be reused.
1093
        data := make([]byte, buf.Len())
44✔
1094
        copy(data, buf.Bytes())
44✔
1095

44✔
1096
        // Return both the raw byte slice and a new ReadCloser
44✔
1097
        // wrapping the same data, which allows for re-reading.
44✔
1098
        return data, io.NopCloser(bytes.NewReader(data))
44✔
1099
}
1100

1101
// mwWrapper applies all registered middlewares to an HTTP handler.
1102
//
1103
// This function iterates through the registered middleware stack in reverse order
1104
// (last added middleware is executed first) and wraps the final HTTP handler
1105
// with each middleware layer.
1106
//
1107
// Parameters:
1108
//   - handler http.Handler: The final HTTP handler to be wrapped with middlewares.
1109
//
1110
// Returns:
1111
//   - http.Handler: The HTTP handler wrapped with all registered middlewares.
1112
//
1113
// Example Usage:
1114
//
1115
//        // This function is automatically executed internally before processing requests.
1116
//
1117
// mwWrapper applies all registered middlewares to an HTTP handler.
1118
//
1119
// This function iterates through the middleware stack in reverse order
1120
// (last added middleware is executed first) and wraps the final HTTP handler
1121
// with each middleware layer.
1122
//
1123
// It supports multiple middleware function signatures:
1124
//   - `func(http.Handler) http.Handler`: Standard net/http middleware.
1125
//   - `func(http.ResponseWriter, *http.Request, http.Handler)`: Middleware that
1126
//     directly manipulates the response and request.
1127
//   - `func(HandlerFunc) HandlerFunc`: Quick-specific middleware format.
1128
//   - `func(Handler) Handler`: Another Quick middleware format.
1129
//
1130
// Parameters:
1131
//   - handler http.Handler: The final HTTP handler to be wrapped.
1132
//
1133
// Returns:
1134
//   - http.Handler: The HTTP handler wrapped with all registered middlewares.
1135
func (q *Quick) mwWrapper(handler http.Handler) http.Handler {
86✔
1136
        for i := len(q.mws2) - 1; i >= 0; i-- {
90✔
1137
                switch mw := q.mws2[i].(type) {
4✔
1138

1139
                case func(http.Handler) http.Handler:
3✔
1140
                        // Apply standard net/http middleware
3✔
1141
                        handler = mw(handler)
3✔
1142

1143
                case func(http.ResponseWriter, *http.Request, http.Handler):
1✔
1144
                        // Apply middleware that takes ResponseWriter, Request, and the next handler
1✔
1145
                        originalHandler := handler // Avoid infinite reassignment
1✔
1146
                        handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2✔
1147
                                mw(w, r, originalHandler)
1✔
1148
                        })
1✔
1149

1150
                case func(HandlerFunc) HandlerFunc:
×
1151
                        // Convert net/http.Handler to Quick.HandlerFunc
×
1152
                        quickHandler := convertToQuickHandler(handler)
×
1153
                        // Apply Quick middleware
×
1154
                        quickHandler = mw(quickHandler)
×
1155

×
1156
                        // Convert back to http.Handler
×
1157
                        handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
×
1158
                                c := &Ctx{
×
1159
                                        Response: w,
×
1160
                                        Request:  r,
×
1161
                                        App:      q,
×
1162
                                }
×
1163
                                quickHandler(c)
×
1164
                        })
×
1165

1166
                case func(Handler) Handler:
×
1167
                        // Convert net/http.Handler to Quick.Handler
×
1168
                        qh := convertHttpToQuickHandler(handler)
×
1169
                        // Apply Quick middleware
×
1170
                        qh = mw(qh)
×
1171
                        // Convert back to http.Handler
×
1172
                        handler = convertQuickToHttpHandler(qh)
×
1173
                }
1174
        }
1175
        return handler
86✔
1176
}
1177

1178
// convertHttpToQuickHandler adapts a net/http.Handler to a Quick.Handler.
1179
//
1180
// This function allows standard HTTP handlers to be wrapped within Quick's middleware
1181
// system by transforming them into the Quick.Handler interface.
1182
//
1183
// Parameters:
1184
//   - h http.Handler: The standard HTTP handler to convert.
1185
//
1186
// Returns:
1187
//   - Handler: The Quick-compatible handler.
1188
func convertHttpToQuickHandler(h http.Handler) Handler {
×
1189
        return HandlerFunc(func(c *Ctx) error {
×
1190
                h.ServeHTTP(c.Response, c.Request)
×
1191
                return nil
×
1192
        })
×
1193
}
1194

1195
// convertQuickToHttpHandler adapts a Quick.Handler to a net/http.Handler.
1196
//
1197
// This function wraps Quick handlers into a standard HTTP handler so they can
1198
// be used within net/http's ecosystem.
1199
//
1200
// Parameters:
1201
//   - h Handler: The Quick handler to convert.
1202
//
1203
// Returns:
1204
//   - http.Handler: The net/http-compatible handler.
1205
func convertQuickToHttpHandler(h Handler) http.Handler {
×
1206
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
×
1207
                c := &Ctx{Response: w, Request: r}
×
1208
                if err := h.ServeQuick(c); err != nil {
×
1209
                        http.Error(w, err.Error(), http.StatusInternalServerError)
×
1210
                }
×
1211
        })
1212
}
1213

1214
// convertToQuickHandler adapts a net/http.Handler to a Quick.HandlerFunc.
1215
//
1216
// This function allows standard HTTP handlers to be used within Quick's middleware
1217
// system by transforming them into Quick.HandlerFunc.
1218
//
1219
// Parameters:
1220
//   - h http.Handler: The standard HTTP handler to convert.
1221
//
1222
// Returns:
1223
//   - HandlerFunc: The Quick-compatible handler function.
1224
func convertToQuickHandler(h http.Handler) HandlerFunc {
×
1225
        return func(c *Ctx) error {
×
1226
                h.ServeHTTP(c.Response, c.Request)
×
1227
                return nil
×
1228
        }
×
1229
}
1230

1231
// func convertToQuickMiddleware(mw func(http.Handler) http.Handler) func(Handler) Handler {
1232
//         return func(next Handler) Handler {
1233
//                 return HandlerFunc(func(c *Ctx) error {
1234
//                         adaptedHandler := mw(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1235
//                                 c.Response = w
1236
//                                 c.Request = r
1237
//                                 next(c)
1238
//                         }))
1239

1240
//                         adaptedHandler.ServeHTTP(c.Response, c.Request)
1241
//                         return nil
1242
//                 })
1243
//         }
1244
// }
1245

1246
// appendRoute registers a new route in the Quick router and applies middlewares.
1247
//
1248
// This function ensures that the given route's handler is wrapped with all registered
1249
// middlewares before being stored in the router. It optimizes performance by applying
1250
// middleware only once during route registration instead of at runtime.
1251
//
1252
// Parameters:
1253
//   - route *Route: The route to be registered in the Quick router.
1254
//
1255
// Example Usage:
1256
//
1257
//        // This function is automatically called when registering a new route.
1258
func (q *Quick) appendRoute(route *Route) {
83✔
1259
        route.handler = q.mwWrapper(route.handler).ServeHTTP
83✔
1260
        //q.routes = append(q.routes, *route)
83✔
1261
        q.routes = append(q.routes, route)
83✔
1262
}
83✔
1263

1264
// Header retrieves the HTTP headers from the response writer.
1265
//
1266
// This method allows middleware or handlers to modify response headers
1267
// before sending them to the client.
1268
//
1269
// Returns:
1270
//   - http.Header: The set of response headers.
1271
//
1272
// Example Usage:
1273
//
1274
//        // Retrieve headers within a middleware or handler function
1275
func (rw *pooledResponseWriter) Header() http.Header {
68✔
1276
        return rw.ResponseWriter.Header()
68✔
1277
}
68✔
1278

1279
// ServeHTTP processes incoming HTTP requests and matches them to registered routes.
1280
//
1281
// This function efficiently routes HTTP requests to the appropriate handler while
1282
// leveraging a **pooled response writer** and **context pooling** to minimize memory
1283
// allocations and improve performance.
1284
//
1285
// If the request method is `OPTIONS`, it is handled separately via `handleOptions`.
1286
// If no matching route is found, the function responds with `404 Not Found`.
1287
//
1288
// Example Usage:
1289
// This function is automatically invoked by the `http.Server` when a request reaches
1290
// the Quick router.
1291
func (q *Quick) ServeHTTP(w http.ResponseWriter, req *http.Request) {
98✔
1292
        // call options
98✔
1293
        if req.Method == http.MethodOptions {
100✔
1294
                q.handleOptions(w, req)
2✔
1295
                return
2✔
1296
        }
2✔
1297

1298
        // Acquire a ResponseWriter from the pool for efficient request handling.
1299
        rw := acquireResponseWriter(w)
96✔
1300
        defer releaseResponseWriter(rw) // Ensure it returns to the pool.
96✔
1301

96✔
1302
        // Acquiring Ctx from the pool
96✔
1303
        ctx := newCtx(rw, req) // <- creates a new, clean instance of the context
96✔
1304
        defer releaseCtx(ctx)  // Returns it to the pool
96✔
1305

96✔
1306
        for i := range q.routes {
268✔
1307
                var requestURI = req.URL.Path
172✔
1308
                var patternUri = q.routes[i].Pattern
172✔
1309

172✔
1310
                if q.routes[i].Method != req.Method {
172✔
1311
                        continue
×
1312
                }
1313

1314
                if len(patternUri) == 0 {
312✔
1315
                        patternUri = q.routes[i].Path
140✔
1316
                }
140✔
1317

1318
                paramsMap, isValid := createParamsAndValid(requestURI, patternUri)
172✔
1319

172✔
1320
                if !isValid {
254✔
1321
                        continue // This route doesn't match, continue checking.
82✔
1322
                }
1323

1324
                var c = ctxServeHttp{
90✔
1325
                        Path:      requestURI,
90✔
1326
                        ParamsMap: paramsMap,
90✔
1327
                        Method:    q.routes[i].Method,
90✔
1328
                }
90✔
1329
                req = req.WithContext(context.WithValue(req.Context(), myContextKey, c))
90✔
1330

90✔
1331
                // Pass the rw (pooledResponseWriter) to the handler
90✔
1332
                q.routes[i].handler(rw, req)
90✔
1333
                return
90✔
1334
        }
1335

1336
        // If no route matches, send a 404 response.
1337
        http.NotFound(rw, req)
6✔
1338
}
1339

1340
// createParamsAndValid extracts dynamic parameters from a request URI and validates the pattern.
1341
//
1342
// This function compares the request URI with the registered pattern and extracts
1343
// route parameters such as `:id` or `{id:[0-9]+}` dynamically.
1344
//
1345
// Example Usage:
1346
// This function is internally used by `ServeHTTP()` to verify if a request matches a
1347
// registered route pattern. If it does, it extracts the dynamic parameters and returns
1348
// them as a map.
1349
func createParamsAndValid(reqURI, patternURI string) (map[string]string, bool) {
182✔
1350
        params := make(map[string]string)
182✔
1351
        var builder strings.Builder
182✔
1352

182✔
1353
        reqURI = strings.TrimPrefix(reqURI, "/")
182✔
1354
        patternURI = strings.TrimPrefix(patternURI, "/")
182✔
1355

182✔
1356
        reqSplit := strings.Split(reqURI, "/")
182✔
1357
        patternSplit := strings.Split(patternURI, "/")
182✔
1358
        if len(reqSplit) != len(patternSplit) {
236✔
1359
                return nil, false
54✔
1360
        }
54✔
1361

1362
        for i, seg := range patternSplit {
315✔
1363
                reqSeg := reqSplit[i]
187✔
1364

187✔
1365
                switch {
187✔
1366
                // Ex: :id => paramName = "id"
1367
                case strings.HasPrefix(seg, ":"):
14✔
1368
                        paramName := seg[1:]
14✔
1369
                        if paramName == "" {
15✔
1370
                                return nil, false
1✔
1371
                        }
1✔
1372
                        params[paramName] = reqSeg
13✔
1373
                        builder.WriteString("/")
13✔
1374
                        builder.WriteString(reqSeg)
13✔
1375

1376
                // Ex: {id:[0-9]+}
1377
                case strings.HasPrefix(seg, "{") && strings.HasSuffix(seg, "}"):
8✔
1378
                        content := seg[1 : len(seg)-1]
8✔
1379
                        parts := strings.SplitN(content, ":", 2)
8✔
1380
                        // Check for name and regex
8✔
1381
                        if len(parts) != 2 || parts[0] == "" {
9✔
1382
                                return nil, false
1✔
1383
                        }
1✔
1384
                        paramName, regexPattern := parts[0], parts[1]
7✔
1385

7✔
1386
                        rgx, err := regexp.Compile("^" + regexPattern + "$")
7✔
1387
                        if err != nil || !rgx.MatchString(reqSeg) {
9✔
1388
                                return nil, false
2✔
1389
                        }
2✔
1390
                        params[paramName] = reqSeg
5✔
1391
                        builder.WriteString("/")
5✔
1392
                        builder.WriteString(reqSeg)
5✔
1393

1394
                default:
165✔
1395
                        if seg != reqSeg {
196✔
1396
                                return nil, false
31✔
1397
                        }
31✔
1398
                        builder.WriteString("/")
134✔
1399
                        builder.WriteString(seg)
134✔
1400
                }
1401
        }
1402

1403
        //if "/"+reqURI != builder.String() {
1404
        //        return nil, false
1405
        //}
1406

1407
        return params, true
93✔
1408
}
1409

1410
// GetRoute retrieves all registered routes in the Quick framework.
1411
//
1412
// This function returns a slice containing all the routes that have been
1413
// registered in the Quick instance. It is useful for debugging, logging,
1414
// or dynamically inspecting available routes.
1415
//
1416
// Example Usage:
1417
//
1418
//        routes := q.GetRoute()
1419
//        for _, route := range routes {
1420
//            fmt.Println("Method:", route.Method, "Path:", route.Path)
1421
//        }
1422
//
1423
// Returns:
1424
//   - []*Route: A slice of pointers to the registered Route instances.
1425
func (q *Quick) GetRoute() []*Route {
4✔
1426
        return q.routes
4✔
1427
}
4✔
1428

1429
// Static serves static files (HTML, CSS, JS, images, etc.) from a directory or embedded filesystem.
1430
//
1431
// This function allows you to register a static file server in the Quick framework, either using
1432
// a local directory (`string`) or an embedded filesystem (`embed.FS`). By embedding files,
1433
// they become part of the binary at compile time, eliminating the need for external file access.
1434
//
1435
// Example Usage:
1436
// This function is useful for serving front-end assets or static resources directly from
1437
// the Go application. It supports both local directories and embedded files.
1438
//
1439
// Parameters:
1440
//   - route: The base path where static files will be served (e.g., "/static").
1441
//   - dirOrFS: The source of the static files. It accepts either:
1442
//   - `string`: A local directory path (e.g., `"./static"`).
1443
//   - `embed.FS`: An embedded file system for compiled-in assets.
1444
//
1445
// Returns:
1446
//   - None (void function).
1447
//
1448
// Notes:
1449
//   - The function automatically trims trailing slashes from `route`.
1450
//   - If an invalid parameter is provided, the function panics.
1451
//   - When using an embedded filesystem, files are served directly from memory.
1452
func (q *Quick) Static(route string, dirOrFS any) {
1✔
1453
        route = strings.TrimSuffix(route, "/")
1✔
1454

1✔
1455
        var fileServer http.Handler
1✔
1456

1✔
1457
        // check of dirOrFS is a embed.FS
1✔
1458
        switch v := dirOrFS.(type) {
1✔
1459
        case string:
×
1460
                fileServer = http.FileServer(http.Dir(v))
×
1461
        case embed.FS:
×
1462
                q.embedFS = v
×
1463
                fileServer = http.FileServer(http.FS(v))
×
1464
        default:
1✔
1465
                panic("Static: invalid parameter, must be string or embed.FS")
1✔
1466
        }
1467

1468
        q.mux.Handle(concat.String(route, "/"), http.StripPrefix(route, fileServer))
×
1469
}
1470

1471
// execHandler wraps an HTTP handler with additional processing.
1472
//
1473
// This function ensures that the provided `http.Handler` is executed properly,
1474
// allowing for additional middleware wrapping or request pre-processing.
1475
//
1476
// Example Usage:
1477
// This function is automatically applied within Quick's internal request processing pipeline.
1478
//
1479
// Parameters:
1480
//   - next: The next HTTP handler to be executed.
1481
//
1482
// Returns:
1483
//   - http.Handler: A wrapped HTTP handler that ensures correct execution.
1484
func (q *Quick) execHandler(next http.Handler) http.Handler {
3✔
1485
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
4✔
1486
                next.ServeHTTP(w, r)
1✔
1487
        })
1✔
1488
}
1489

1490
// corsHandler applies the configured CORS middleware to the Quick router.
1491
//
1492
// This function checks if CORS is enabled in the Quick instance (`q.Cors`).
1493
// If enabled, it wraps the request processing with the configured CORS middleware.
1494
//
1495
// Example Usage:
1496
// Automatically used when CORS support is detected within `Use()`.
1497
//
1498
// Returns:
1499
//   - http.Handler: The HTTP handler wrapped with CORS processing.
1500
func (q *Quick) corsHandler() http.Handler {
2✔
1501
        return q.CorsSet(q)
2✔
1502
}
2✔
1503

1504
// httpServerTLS initializes and returns an HTTP server instance configured for TLS/HTTPS.
1505
//
1506
// This function sets up a secure server with `TLSConfig` and allows optional
1507
// custom handlers to be provided. If no custom handler is specified, the default
1508
// Quick router is used.
1509
//
1510
// Example Usage:
1511
// Used internally by Quick when setting up an HTTPS server via `ListenTLS()`.
1512
//
1513
// Parameters:
1514
//   - addr:      The network address the server should listen on (e.g., ":443").
1515
//   - tlsConfig: A `*tls.Config` instance containing certificate and security settings.
1516
//   - handler:   (Optional) One or more custom HTTP handlers.
1517
//
1518
// Returns:
1519
//   - *http.Server: A configured HTTPS server instance.
1520
func (q *Quick) httpServerTLS(addr string, tlsConfig *tls.Config, handler ...http.Handler) *http.Server {
4✔
1521
        var h http.Handler = q
4✔
1522
        if len(handler) > 0 {
5✔
1523
                h = q.execHandler(handler[0])
1✔
1524
        } else if q.Cors {
4✔
1525
                h = q.corsHandler()
×
1526
        }
×
1527

1528
        // Return a fully configured http.Server, including TLS settings.
1529
        return &http.Server{
4✔
1530
                Addr:              addr,
4✔
1531
                Handler:           h,
4✔
1532
                TLSConfig:         tlsConfig,
4✔
1533
                ReadTimeout:       q.config.ReadTimeout,
4✔
1534
                WriteTimeout:      q.config.WriteTimeout,
4✔
1535
                IdleTimeout:       q.config.IdleTimeout,
4✔
1536
                ReadHeaderTimeout: q.config.ReadHeaderTimeout,
4✔
1537
                MaxHeaderBytes:    q.config.MaxHeaderBytes,
4✔
1538
        }
4✔
1539
}
1540

1541
// httpServer initializes and returns an HTTP server instance configured for plain HTTP.
1542
//
1543
// This function sets up a standard HTTP server, allowing optional custom handlers
1544
// to be specified. If no handler is provided, the default Quick router is used.
1545
//
1546
// Example Usage:
1547
// Used internally when starting an HTTP server via `Listen()`.
1548
//
1549
// Parameters:
1550
//   - addr:    The network address the server should listen on (e.g., ":8080").
1551
//   - handler: (Optional) One or more custom HTTP handlers.
1552
//
1553
// Returns:
1554
//   - *http.Server: A configured HTTP server instance.
1555
func (q *Quick) httpServer(addr string, handler ...http.Handler) *http.Server {
7✔
1556
        // Determine the handler to use based on optional arguments and CORS configuration.
7✔
1557
        var h http.Handler = q
7✔
1558
        if len(handler) > 0 {
8✔
1559
                h = q.execHandler(handler[0])
1✔
1560
        } else if q.Cors {
8✔
1561
                h = q.corsHandler()
1✔
1562
        }
1✔
1563

1564
        // Return a fully configured http.Server for plain HTTP usage.
1565
        return &http.Server{
7✔
1566
                Addr:              addr,
7✔
1567
                Handler:           h,
7✔
1568
                ReadTimeout:       q.config.ReadTimeout,
7✔
1569
                WriteTimeout:      q.config.WriteTimeout,
7✔
1570
                IdleTimeout:       q.config.IdleTimeout,
7✔
1571
                ReadHeaderTimeout: q.config.ReadHeaderTimeout,
7✔
1572
                MaxHeaderBytes:    q.config.MaxHeaderBytes,
7✔
1573
        }
7✔
1574
}
1575

1576
// ListenWithShutdown starts an HTTP server and returns both the server instance and a shutdown function.
1577
//
1578
// This method initializes performance tuning settings, creates a TCP listener, and starts the server in a background goroutine.
1579
// The returned shutdown function allows for a graceful termination of the server.
1580
//
1581
// Parameters:
1582
//   - addr: The address (host:port) where the server should listen.
1583
//   - handler: Optional HTTP handlers that can be provided to the server.
1584
//
1585
// Returns:
1586
//   - *http.Server: A reference to the initialized HTTP server.
1587
//   - func(): A shutdown function to gracefully stop the server.
1588
//   - error: Any error encountered during the server setup.
1589
func (q *Quick) ListenWithShutdown(addr string, handler ...http.Handler) (*http.Server, func(), error) {
7✔
1590
        q.setupPerformanceTuning()
7✔
1591

7✔
1592
        listener, err := net.Listen("tcp", addr)
7✔
1593
        if err != nil {
9✔
1594
                return nil, nil, err
2✔
1595
        }
2✔
1596

1597
        server := q.httpServer(listener.Addr().String(), handler...)
5✔
1598
        q.server = server
5✔
1599

5✔
1600
        // Shutdown function to gracefully terminate the server.
5✔
1601
        shutdownFunc := func() {
9✔
1602
                ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
4✔
1603
                defer cancel()
4✔
1604
                server.Shutdown(ctx)
4✔
1605
                listener.Close()
4✔
1606
        }
4✔
1607

1608
        // Start the server in a background goroutine.
1609
        go func() {
10✔
1610
                server.Serve(listener)
5✔
1611
        }()
5✔
1612

1613
        return server, shutdownFunc, nil
5✔
1614
}
1615

1616
// setupPerformanceTuning configures performance settings for the Quick server.
1617
//
1618
// This method optimizes performance by:
1619
//   - Dynamically tuning garbage collection behavior if `MoreRequests` is configured.
1620
//   - Adjusting the `GOMAXPROCS` value based on the configuration.
1621
//   - Initializing a buffer pool to optimize memory allocation.
1622
//
1623
// Example Usage:
1624
// Called automatically when initializing the Quick server.
1625
func (q *Quick) setupPerformanceTuning() {
7✔
1626
        if q.config.MoreRequests > 0 {
13✔
1627
                go q.adaptiveGCTuner()
6✔
1628
        }
6✔
1629

1630
        if q.config.GOMAXPROCS > 0 {
13✔
1631
                runtime.GOMAXPROCS(q.config.GOMAXPROCS)
6✔
1632
        }
6✔
1633

1634
        q.bufferPool = &sync.Pool{
7✔
1635
                New: func() interface{} {
7✔
1636
                        return bytes.NewBuffer(make([]byte, 0, q.config.BufferPoolSize))
×
1637
                },
×
1638
        }
1639
}
1640

1641
// adaptiveGCTuner periodically monitors memory usage and triggers garbage collection if necessary.
1642
//
1643
// This function runs in a background goroutine and:
1644
//   - Checks heap memory usage every 15 seconds.
1645
//   - If the heap usage exceeds a defined threshold, it triggers garbage collection and frees OS memory.
1646
//
1647
// Example Usage:
1648
// Automatically invoked by `setupPerformanceTuning()` when `MoreRequests` is enabled.
1649
func (q *Quick) adaptiveGCTuner() {
6✔
1650
        var threshold uint64 = uint64(q.config.GCHeapThreshold)
6✔
1651
        if threshold == 0 {
6✔
1652
                threshold = 1 << 30 // Default threshold: 1GB
×
1653
        }
×
1654

1655
        ticker := time.NewTicker(15 * time.Second)
6✔
1656
        defer ticker.Stop()
6✔
1657

6✔
1658
        var m runtime.MemStats
6✔
1659
        for range ticker.C {
6✔
1660
                runtime.ReadMemStats(&m)
×
1661

×
1662
                if m.HeapInuse > threshold {
×
1663
                        debug.FreeOSMemory()
×
1664
                        runtime.GC()
×
1665
                }
×
1666
        }
1667
}
1668

1669
// Listen starts the Quick server and blocks indefinitely.
1670
//
1671
// This function initializes the HTTP server and prevents the application from exiting.
1672
//
1673
// Example Usage:
1674
//
1675
//        q.Listen(":8080")
1676
//
1677
// Parameters:
1678
//   - addr: The address on which the server should listen (e.g., ":8080").
1679
//   - handler: (Optional) Custom HTTP handlers.
1680
//
1681
// Returns:
1682
//   - error: Any errors encountered while starting the server.
1683
func (q *Quick) Listen(addr string, handler ...http.Handler) error {
2✔
1684
        _, shutdown, err := q.ListenWithShutdown(addr, handler...)
2✔
1685
        if err != nil {
3✔
1686
                return err
1✔
1687
        }
1✔
1688
        defer shutdown()
1✔
1689

1✔
1690
        q.Display("http", addr)
1✔
1691
        // Locks indefinitely
1✔
1692
        <-make(chan struct{}) // Bloqueio sem consumo de CPU
1✔
1693
        return nil
1✔
1694
}
1695

1696
// Shutdown gracefully shuts down the Quick server without interrupting active connections.
1697
//
1698
// This function ensures that all ongoing requests are completed before shutting down,
1699
// preventing abrupt connection termination.
1700
//
1701
// Example Usage:
1702
//
1703
//                q := quick.New()
1704
//
1705
//            q.Get("/", func(c *quick.Ctx) error {
1706
//                return c.SendString("Server is running!")
1707
//            })
1708
//
1709
//                q.Shutdown()
1710
//
1711
// Returns:
1712
//   - error: Any errors encountered during shutdown.
1713
func (q *Quick) Shutdown() error {
3✔
1714
        // Create a context with a timeout to control the shutdown process
3✔
1715
        ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
3✔
1716
        defer cancel() // Ensure the context is cancelled to free resources
3✔
1717

3✔
1718
        // Check if the server is initialized before attempting to shut it down
3✔
1719
        if q.server != nil {
5✔
1720
                q.server.SetKeepAlivesEnabled(false)
2✔
1721
                err := q.server.Shutdown(ctx)
2✔
1722
                q.releaseResources()
2✔
1723
                return err // Attempt to shutdown the server gracefully
2✔
1724
        }
2✔
1725
        return nil // Return nil if there is no server to shutdown
1✔
1726
}
1727

1728
// releaseResources resets system-level performance settings after server shutdown.
1729
//
1730
// This function ensures that system configurations return to their default states
1731
// after the Quick server is gracefully shut down, preventing excessive resource usage.
1732
//
1733
// Example Usage:
1734
//
1735
//        q.releaseResources()
1736
//
1737
// Actions Performed:
1738
//   - Resets the garbage collection behavior to the default percentage (100).
1739
//   - Restores `GOMAXPROCS` to automatic CPU thread allocation.
1740
func (q *Quick) releaseResources() {
2✔
1741
        // System settings reset
2✔
1742
        debug.SetGCPercent(100) // Return to default GC
2✔
1743
        runtime.GOMAXPROCS(0)   // Reset to automatic thread configuration
2✔
1744
}
2✔
1745

1746
// ListenTLS starts an HTTPS server on the specified address using the provided
1747
// certificate and key files. It allows enabling or disabling HTTP/2 support.
1748
// It also configures basic modern TLS settings, sets up a listener with
1749
// SO_REUSEPORT (when possible), and applies a graceful shutdown procedure.
1750
//
1751
// Parameters:
1752
//   - addr: the TCP network address to listen on (e.g., ":443")
1753
//   - certFile: the path to the SSL certificate file
1754
//   - keyFile: the path to the SSL private key file
1755
//   - useHTTP2: whether or not to enable HTTP/2
1756
//   - handler: optional HTTP handlers. If none is provided, the default handler is used.
1757
//
1758
// Returns:
1759
//   - error: an error if something goes wrong creating the listener or starting the server.
1760
func (q *Quick) ListenTLS(addr, certFile, keyFile string, useHTTP2 bool, handler ...http.Handler) error {
3✔
1761
        // If the user has specified a custom GC percentage (> 0),
3✔
1762
        // set it here to help control garbage collection aggressiveness.
3✔
1763
        if q.config.GCPercent > 0 {
4✔
1764
                debug.SetGCPercent(q.config.GCPercent)
1✔
1765
        }
1✔
1766

1767
        // Extract or create a TLS configuration.
1768
        // If q.config.TLSConfig is nil, set up a default TLS config with modern protocols
1769
        // and ciphers. This includes TLS 1.3 and secure cipher suites.
1770
        var tlsConfig = q.config.TLSConfig
3✔
1771
        if tlsConfig == nil {
6✔
1772
                tlsConfig = &tls.Config{
3✔
1773
                        MinVersion:       tls.VersionTLS13, // Sets TLS 1.3 as the minimum version
3✔
1774
                        CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
3✔
1775
                        CipherSuites: []uint16{
3✔
1776
                                tls.TLS_AES_128_GCM_SHA256,
3✔
1777
                                tls.TLS_AES_256_GCM_SHA384,
3✔
1778
                                tls.TLS_CHACHA20_POLY1305_SHA256,
3✔
1779
                        },
3✔
1780
                        PreferServerCipherSuites: true,                              // Prioritize server ciphers
3✔
1781
                        SessionTicketsDisabled:   false,                             // Enable Session Resumption (minus TLS Handshakes)
3✔
1782
                        ClientSessionCache:       tls.NewLRUClientSessionCache(128), // Cache TLS sessions for reuse
3✔
1783
                }
3✔
1784
        }
3✔
1785

1786
        // Enable or disable HTTP/2 support based on the useHTTP2 parameter.
1787
        if useHTTP2 {
4✔
1788
                // HTTP/2 + HTTP/1.1
1✔
1789
                tlsConfig.NextProtos = []string{"h2", "http/1.1"}
1✔
1790
        } else {
3✔
1791
                // Only HTTP/1.1
2✔
1792
                tlsConfig.NextProtos = []string{"http/1.1"}
2✔
1793
        }
2✔
1794

1795
        // Create a net.ListenConfig that attempts to set SO_REUSEPORT on supported platforms.
1796
        // This feature can improve load balancing by letting multiple processes
1797
        // bind to the same address.
1798
        cfg := &net.ListenConfig{
3✔
1799
                Control: func(network, address string, c syscall.RawConn) error {
6✔
1800
                        return c.Control(func(fd uintptr) {
6✔
1801
                                // Avoid setting SO_REUSEPORT on macOS to prevent errors.
3✔
1802
                                if runtime.GOOS != "darwin" {
6✔
1803
                                        if runtime.GOOS == "linux" {
6✔
1804
                                                if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_REUSEPORT, 1); err != nil {
3✔
1805
                                                        log.Fatalf("Erro ao definir SO_REUSEPORT: %v", err)
×
1806
                                                }
×
1807
                                        }
1808
                                        // } else {
1809
                                        //         if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1); err != nil {
1810
                                        //                 log.Fatalf("Erro ao definir SO_REUSEPORT: %v", err)
1811
                                        //         }
1812
                                        // }
1813
                                }
1814
                        })
1815
                },
1816
        }
1817

1818
        // Listen on the specified TCP address using our custom ListenConfig.
1819
        listener, err := cfg.Listen(context.Background(), "tcp", addr)
3✔
1820
        if err != nil {
3✔
1821
                return fmt.Errorf("failed to create listener: %w", err)
×
1822
        }
×
1823

1824
        // Create the HTTP server configured for TLS using the provided or default tlsConfig.
1825
        // The address is taken from the listener to ensure correctness in case the actual
1826
        // bound port differs (for example, if you used ":0" for a random port).
1827
        q.server = q.httpServerTLS(listener.Addr().String(), tlsConfig, handler...)
3✔
1828

3✔
1829
        // Start the server and perform a graceful shutdown when a termination signal is received.
3✔
1830
        return q.startServerWithGracefulShutdown(listener, certFile, keyFile)
3✔
1831
}
1832

1833
// startServerWithGracefulShutdown starts the HTTPS server (using the provided TLS certificate
1834
// and private key) on the given listener and blocks until the server either encounters
1835
// an unrecoverable error or receives a termination signal.
1836
//
1837
// The server runs in a goroutine so that this function can simultaneously listen for
1838
// interrupt signals (SIGINT, SIGTERM, SIGHUP). Once such a signal is detected, the function
1839
// will gracefully shut down the server, allowing any ongoing requests to finish or timing
1840
// out after 15 seconds.
1841
//
1842
// Parameters:
1843
//   - listener: A net.Listener that the server will use to accept connections.
1844
//   - certFile: Path to the TLS certificate file.
1845
//   - keyFile:  Path to the TLS private key file.
1846
//
1847
// Returns:
1848
//   - error: An error if the server fails to start, or if a forced shutdown occurs.
1849
//     Returns nil on normal shutdown.
1850
func (q *Quick) startServerWithGracefulShutdown(listener net.Listener, certFile, keyFile string) error {
4✔
1851

4✔
1852
        serverErr := make(chan error, 1)
4✔
1853

4✔
1854
        // Run ServeTLS in a goroutine. Any unrecoverable error that isn't http.ErrServerClosed
4✔
1855
        // is sent to the channel for handling in the main select block.
4✔
1856
        go func() {
8✔
1857
                if err := q.server.ServeTLS(listener, certFile, keyFile); err != nil && err != http.ErrServerClosed {
4✔
1858
                        serverErr <- fmt.Errorf("server error: %w", err)
×
1859
                }
×
1860
                close(serverErr)
4✔
1861
        }()
1862

1863
        // Create a context that listens for SIGINT, SIGTERM, and SIGHUP signals.
1864
        // When one of these signals occurs, the context is canceled automatically.
1865
        ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
4✔
1866
        defer stop()
4✔
1867

4✔
1868
        select {
4✔
1869
        case <-ctx.Done():
2✔
1870
                // We've received a termination signal, so attempt a graceful shutdown.
2✔
1871
                log.Println("Received shutdown signal. Stopping server...")
2✔
1872
                shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
2✔
1873
                defer cancel()
2✔
1874

2✔
1875
                // If the server cannot gracefully shut down within 15 seconds,
2✔
1876
                // it will exit with an error.
2✔
1877
                if err := q.server.Shutdown(shutdownCtx); err != nil {
2✔
1878
                        return fmt.Errorf("forced shutdown: %w", err)
×
1879
                }
×
1880
                return nil
2✔
1881

1882
        case err := <-serverErr:
2✔
1883
                // If an unrecoverable error occurred in ServeTLS, return it here.
2✔
1884
                return err
2✔
1885
        }
1886
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc