The middleware
directory contains useful middleware implementations for the Quick Framework, making it easy to integrate common features such as authentication, compression, logging, request size limits, and UUID tracking.
Middlewares are functions that intercept HTTP requests before they reach the final handler. They allow:
- Validation (e.g., authentication and security policies)
- Request/response modification (e.g., GZIP compression)
- Logging and monitoring (e.g., request logging and UUID tracking)
Each part of the request contains headers and a body:
POST /# HTTP/1.1
Host: example.com
Authorization: Basic YWRtaW46MTIzNA==
Content-Type: application/json
Content-Length: 50
{
"username": "admin",
"password": "1234"
}
Header | Description |
---|---|
Authorization |
ends the BasicAuth credentials (Base64(username:password)). |
Content-Type |
Defines the format of the request body (e.g., application/json). |
Content-Length |
Specifies the size of the request body (optional but recommended). |
🔐 BasicAuth Provides HTTP Basic Authentication, requiring a username and password to access protected routes.
- Can be applied globally or to specific routes.
- Supports authentication via environment variables.
- Allows custom implementation if needed.
This example sets up Basic Authentication using environment variables to store the credentials securely. the routes below are affected, to isolate the route use group to apply only to routes in the group.
package main
import (
"log"
"os"
"github.com/jeffotoni/quick"
middleware "github.com/jeffotoni/quick/middleware/basicauth"
)
// Environment variables for authentication
// export USER=admin
// export PASSWORD=1234
var (
// Retrieve the username and password from environment variables
User = os.Getenv("USER")
Password = os.Getenv("PASSORD")
)
func main() {
// Initialize a new Quick instance
q := quick.New()
// Apply Basic Authentication middleware
q.Use(middleware.BasicAuth(User, Password))
// Define a protected route
q.Get("/protected", func(c *quick.Ctx) error {
// Set the response content type to JSON
c.Set("Content-Type", "application/json")
// Return a success message
return c.SendString("You have accessed a protected route!")
})
// Start the server on port 8080
q.Listen("0.0.0.0:8080")
}
$ curl -i -X GET http://localhost:8080/api/v1/users/123
You have accessed a protected route!
This example uses the built-in BasicAuth middleware provided by Quick, offering a simple authentication setup.
package main
import (
"log"
"github.com/jeffotoni/quick"
middleware "github.com/jeffotoni/quick/middleware/basicauth"
)
func main() {
//starting Quick
q := quick.New()
// calling middleware
q.Use(middleware.BasicAuth("admin", "1234"))
// everything below Use will apply the middleware
q.Get("/protected", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.SendString("You have accessed a protected route!")
})
// Start the server on port 8080
q.Listen("0.0.0.0:8080")
}
$ curl -i -X GET 'http://localhost:8080/protected' \
--header 'Authorization: Basic YWRtaW46MTIzNA=='
You have accessed a protected route!
This example shows how to apply Basic Authentication to a specific group of routes using Quick's Group functionality. When we use group we can isolate the middleware, this works for any middleware in quick.
package main
import (
"log"
"github.com/jeffotoni/quick"
middleware "github.com/jeffotoni/quick/middleware/basicauth"
)
func main() {
//starting Quick
q := quick.New()
// using group to isolate routes and middlewares
gr := q.Group("/")
// middleware BasicAuth
gr.Use(middleware.BasicAuth("admin", "1234"))
// route public
q.Get("/v1/user", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.SendString("Public quick route")
})
// protected route
gr.Get("/protected", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.SendString("You have accessed a protected route!")
})
// Start the server on port 8080
q.Listen("0.0.0.0:8080")
}
$ curl -i -X GET http://localhost:8080/v1/user
Public quick route
This example shows a custom implementation of Basic Authentication without using any middleware. It manually verifies user credentials and applies authentication to protected routes.
In quick you are allowed to make your own custom implementation directly in q.Use(..), that is, you will be able to implement it directly if you wish.
package main
import (
"encoding/base64"
"log"
"net/http"
"strings"
"github.com/jeffotoni/quick"
)
func main() {
//starting Quick
q := quick.New()
// implementing middleware directly in Use
q.Use(func(next http.Handler) http.Handler {
// credentials
username := "admin"
password := "1234"
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Check if it starts with "Basic"
if !strings.HasPrefix(authHeader, "Basic ") {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Decode credentials
payload, err := base64.StdEncoding.DecodeString(authHeader[len("Basic "):])
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
creds := strings.SplitN(string(payload), ":", 2)
if len(creds) != 2 || creds[0] != username || creds[1] != password {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
})
q.Get("/protected", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.SendString("You have accessed a protected route!")
})
// Start the server on port 8080
q.Listen("0.0.0.0:8080")
}
$ curl -i -u admin:1234 -X GET http://localhost:8080/protected
You have accessed a protected route!
The Compress Middleware in Quick provides automatic GZIP compression for HTTP responses, reducing response sizes and improving performance.
- ✅ Automatic compression detection (Accept-Encoding: gzip)
- ✅ Transparent response compression without modifying business logic
- ✅ Bandwidth efficiency improvement
The Quick framework supports multiple styles of middleware implementation for GZIP compression:
This is the standard and recommended approach in Quick. It follows the framework’s native middleware pattern using quick.Handler.
func Gzip() func(next quick.Handler) quick.Handler {
return func(next quick.Handler) quick.Handler {
return quick.HandlerFunc(func(c *quick.Ctx) error {
if !clientSupportsGzip(c) {
// If the client does not support gzip, proceed without compression
return next.ServeQuick(c)
}
// Remove Content-Length to avoid conflicts with compression
c.Del("Content-Length")
c.Set("Content-Encoding", "gzip")
c.Add("Vary", "Accept-Encoding")
// Retrieve a gzip writer from the pool
gz := gzipWriterPool.Get().(*gzip.Writer)
defer gzipWriterPool.Put(gz)
gz.Reset(c.Response)
defer gz.Close()
// Wrap the response writer with gzipResponseWriter
gzr := gzipResponseWriter{ResponseWriter: c.Response, writer: gz}
c.Response = gzr
//return next(c)
return next.ServeQuick(c)
})
}
}
Quick also supports quick.HandlerFunc, which allows a function-based syntax instead of using the Handler interface.
func Gzip() func(next quick.HandlerFunc) quick.HandlerFunc {
return func(next quick.HandlerFunc) quick.HandlerFunc {
return func(c *quick.Ctx) error {
if !clientSupportsGzip(c) {
// If the client does not support gzip, proceed without compression
return next(c)
}
// Remove Content-Length to avoid conflicts with compression
c.Del("Content-Length")
c.Set("Content-Encoding", "gzip")
c.Add("Vary", "Accept-Encoding")
// Retrieve a gzip writer from the pool
gz := gzipWriterPool.Get().(*gzip.Writer)
defer gzipWriterPool.Put(gz)
gz.Reset(c.Response)
defer gz.Close()
// Wrap the response writer with gzipResponseWriter
gzr := gzipResponseWriter{ResponseWriter: c.Response, writer: gz}
c.Response = gzr
return next(c)
}
}
}
Quick also supports native net/http
middleware, making it compatible with standard Go HTTP handlers.
func clientSupportsGzipHttp(r *http.Request) bool {
return strings.Contains(strings.ToLower(r.Header.Get("Accept-Encoding")), "gzip")
}
// Gzip creates a middleware to compress the response using gzip.
func Gzip() func(h http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Checks if the client supports gzip
if !clientSupportsGzipHttp(r) {
next.ServeHTTP(w, r)
return
}
// Remove Content-Length to avoid conflict with compressed response
w.Header().Del("Content-Length")
// Adjust headers to indicate that the response will be gzipped
w.Header().Set("Content-Encoding", "gzip")
w.Header().Add("Vary", "Accept-Encoding")
gz := gzip.NewWriter(w)
defer func() {
if err := gz.Close(); err != nil {
// If an error occurs when closing, we send 500
http.Error(w, fmt.Sprintf("error closing gzip: %v", err), http.StatusInternalServerError)
}
}()
gzr := gzipResponseWriter{writer: gz, ResponseWriter: w}
next.ServeHTTP(gzr, r)
})
}
}
package main
import (
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/compress"
)
func main() {
q := quick.New()
// Enable GZIP compression
q.Use(compress.Gzip())
// Define a compressed response route
q.Get("/v1/compress", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
c.Set("Accept-Encoding", "gzip")
type response struct {
Msg string `json:"msg"`
Headers map[string][]string `json:"headers"`
}
return c.Status(200).JSON(&response{
Msg: "Quick ❤️",
Headers: c.Headers,
})
})
log.Fatal(q.Listen("0.0.0.0:8080"))
}
package main
import (
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/compress"
)
func main() {
q := quick.New()
// Enable GZIP middleware using HandlerFunc version
q.Use(compress.Gzip())
// Define a compressed response route
q.Get("/v1/compress", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
type response struct {
Msg string `json:"msg"`
Headers map[string][]string `json:"headers"`
}
return c.Status(200).JSON(&response{
Msg: "Quick ❤️",
Headers: c.Headers,
})
})
log.Fatal(q.Listen(":8080"))
}
package main
import (
"log"
"net/http"
"github.com/jeffotoni/quick/middleware/compress"
)
func main() {
mux := http.NewServeMux()
// Route with compression enabled using the middleware
mux.Handle("/v1/compress", compress.Gzip()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"message": "Hello, net/http with Gzip!"}`))
})))
log.Println("Server running at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
Controls how your API can be accessed from different domains.
- Restricts which domains, methods, and headers are allowed.
- Helps prevent CORS errors in browsers.
- Configurable via allowed origins, headers, and credentials.
The example below configures CORS to allow requests from any origin, method, and header.
package main
import (
"fmt"
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/cors"
)
func main() {
// Create a new Quick instance
app := quick.New()
// Apply CORS middleware to allow all origins, methods, and headers
app.Use(cors.New(cors.Config{
AllowedOrigins: []string{"*"}, // Allows requests from any origin
AllowedMethods: []string{"*"}, // Allows all HTTP methods (GET, POST, PUT, DELETE, etc.)
AllowedHeaders: []string{"*"}, // Allows all headers
}))
// Define a POST route for creating a user
app.Post("/v1/user", func(c *quick.Ctx) error {
// Set response content type as JSON
c.Set("Content-Type", "application/json")
// Define a struct to hold incoming JSON data
type My struct {
Name string `json:"name"`
Year int `json:"year"`
}
var my My
// Parse the request body into the struct
err := c.BodyParser(&my)
fmt.Println("byte:", c.Body()) // Print raw request body
if err != nil {
// Return a 400 Bad Request if parsing fails
return c.Status(400).SendString(err.Error())
}
// Print the request body as a string
fmt.Println("String:", c.BodyString())
// Return the parsed JSON data with a 200 OK status
return c.Status(200).JSON(&my)
})
// Start the server on port 8080
log.Fatal(app.Listen("0.0.0.0:8080"))
}
$ curl --location --request POST 'http://localhost:8080/v1/user' \
--header 'Content-Type: application/json' \
--data '{"name": "John Doe", "year": 2024}'
Logs incoming HTTP requests, helping in monitoring and debugging.
- Logs request method, path, response time, and status code.
- Can be integrated with structured logging tools.
- Helps with API usage tracking and debugging.
This example applies simple logging.
package main
import (
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/logger"
)
func main() {
q := quick.New()
q.Use(logger.New())
q.Use(logger.New(logger.Config{
Level: "DEGUB",
}))
q.Use(logger.New(logger.Config{
Level: "WARN",
}))
q.Get("/v1/logger", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.Status(200).JSON(quick.M{
"msg": "Quick ❤️",
})
})
q.Listen("0.0.0.0:8080")
}
Text Logging
$ curl -i -XGET http://localhost:8080/v1/logger
This example applies logging in text format with custom log fields.
package main
import (
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/logger"
)
func main() {
q := quick.New()
q.Use(logger.New(logger.Config{
Format: "text",
Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
Level: "DEBUG",
CustomFields: map[string]string{
"user_id": "usr-001",
"trace": "trace-debug",
},
}))
q.Use(logger.New(logger.Config{
Format: "text",
Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
Level: "INFO",
CustomFields: map[string]string{
"user_id": "usr-002",
"trace": "trace-info",
},
}))
q.Use(logger.New(logger.Config{
Format: "text",
Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
Level: "WARN",
CustomFields: map[string]string{
"user_id": "usr-003",
"trace": "trace-warn",
},
}))
// Definir rota GET para gerar logs
q.Get("/v1/logger", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
// Retornar resposta JSON
return c.Status(200).JSON(quick.M{
"msg": "Quick ❤️",
})
})
// Iniciar o servidor na porta 8080
q.Listen("0.0.0.0:8080")
}
Text Logging
$ curl -i -XGET http://localhost:8080/v1/logger
This example uses structured logging (slog) for better log parsing.
package main
import (
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/logger"
)
func main() {
q := quick.New()
// Apply the logger middleware with structured logging (slog)
q.Use(logger.New(logger.Config{
Format: "slog",
Level: "DEBUG",
Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
"user=${user_id} trace=${trace}\n",
CustomFields: map[string]string{
"user_id": "99999",
"trace": "trace-debug",
},
}))
// Apply the logger middleware with structured logging (slog)
q.Use(logger.New(logger.Config{
Format: "slog",
Level: "INFO",
Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
"user=${user_id} trace=${trace}\n",
CustomFields: map[string]string{
"user_id": "99999",
"trace": "trace-info",
},
}))
// Apply the logger middleware with structured logging (slog)
q.Use(logger.New(logger.Config{
Format: "slog",
Level: "WARN",
Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
"user=${user_id} trace=${trace}\n",
CustomFields: map[string]string{
"user_id": "99999",
"trace": "trace-warn",
},
}))
// Define a test route
q.Get("/v1/logger/slog", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.Status(200).JSON(quick.M{
"msg": "Structured logging with slog",
})
})
// Start the server
q.Listen("0.0.0.0:8080")
}
Structured Logging (Slog)
$ curl -i -XGET http://localhost:8080/v1/logger/slog
Ideal for log aggregation systems, this example logs in JSON format.
package main
import (
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/logger"
)
func main() {
q := quick.New()
// Apply logger with JSON format
q.Use(logger.New(logger.Config{
Format: "json",
Level: "INFO",
}))
q.Use(logger.New(logger.Config{
Format: "json",
Pattern: "[${level}] ${time} ${ip} ${method} ${status} - ${latency} user_id=${user_id} trace=${trace}\n",
Level: "DEBUG",
CustomFields: map[string]string{
"user_id": "usr-001",
"trace": "trace-debug",
},
}))
// Apply the logger middleware with structured logging (slog)
q.Use(logger.New(logger.Config{
Format: "json",
Level: "WARN",
Pattern: "[${level}] ${ip} ${method} ${path} - ${latency} " +
"user=${user_id} trace=${trace}\n",
CustomFields: map[string]string{
"user_id": "usr-001",
"trace": "trace-warn",
},
}))
// Define an endpoint that triggers logging
q.Get("/v1/logger/json", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
return c.Status(200).JSON(quick.M{
"msg": "JSON logging example",
})
})
// Start the server
q.Listen("0.0.0.0:8080")
}
JSON Logging
$ curl -i -XGET http://localhost:8080/v1/logger/json
Restricts the maximum request body size to prevent clients from sending excessively large payloads.
- ✅ Avoids excessive memory usage.
- ✅ Can prevent attacks such as DoS (Denial-of-Service).
- ✅ Returns a 413 Payload Too Large error when exceeded.
This example limits the request body size using maxbody.New(), which applies the restriction globally.
package main
import (
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/maxbody"
)
func main() {
q := quick.New()
// Middleware to enforce a 50KB request body limit
q.Use(maxbody.New(50000)) // 50KB
// Define a route that accepts a request body
q.Post("/v1/user/maxbody/any", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
log.Printf("Body received: %s", c.BodyString())
return c.Status(200).Send(c.Body())
})
log.Fatal(q.Listen("0.0.0.0:8080"))
}
This example applies MaxBytesReader for additional security by enforcing the body size limit at the request handling level.
package main
import (
"io"
"log"
"net/http"
"github.com/jeffotoni/quick"
)
const maxBodySize = 1024 // 1KB
func main() {
q := quick.New()
// Define a route that applies MaxBytesReader for additional protection
q.Post("/v1/user/maxbody/max", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
// Limit request body size to 1KB
c.Request.Body = quick.MaxBytesReader(c.Response, c.Request.Body, maxBodySize)
// Read the request body safely
body, err := io.ReadAll(c.Request.Body)
if err != nil {
log.Printf("Error reading request body: %v", err)
return c.Status(http.StatusRequestEntityTooLarge).String("Request body too large")
}
return c.Status(http.StatusOK).Send(body)
})
log.Println("Server running at http://0.0.0.0:8080")
log.Fatal(q.Listen("0.0.0.0:8080"))
}
Implementation | Description |
---|---|
maxbody.New() |
Enforces a global request body size limit. |
MaxBytesReader() |
Adds an extra validation layer inside the request handler. |
Assigns a UUID (Universally Unique Identifier) to each request.
- Allows easy tracking of requests in logs.
- Useful for distributed systems where tracing requests across services is required.
- Adds a unique identifier to every request automatically.
Feature | Benefit |
---|---|
🆔 Unique Identifier | Adds a UUID to each request for tracking and correlation. |
🔄 Automatic Generation | No need for manual UUID creation, added seamlessly. |
📊 Enhanced Debugging | Makes log analysis easier by attaching request identifiers. |
🚀 Lightweight & Fast | Does not impact performance, operates efficiently. |
This example generates a unique request identifier with the MsgUUUID middleware.
package main
import (
"fmt"
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/msguuid"
)
func main() {
q := quick.New()
// Apply MsgUUID Middleware globally
q.Use(msguuid.New())
// Define an endpoint that responds with a UUID
q.Get("/v1/msguuid/default", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
// Log headers to validate UUID presence
fmt.Println("Headers:", c.Response.Header())
// Return a 200 OK status
return c.Status(200).JSON(nil)
})
log.Fatal(q.Listen("0.0.0.0:8080"))
}
$ curl -i -XGET http://localhost:8080/v1/msguuid/default
"Headers":"map"[
"Content-Type":["application/json"],
"Msguuid":[5f49cf4d-b62e-4d81-b46e-5125b52058a6]
]
The MsgID Middleware
automatically assigns a unique identifier (MsgID) to each request. This helps with tracking, debugging, and log correlation in distributed systems.
- Automatically generates a unique MsgID for every incoming request.
- Ensures traceability across microservices and distributed applications.
- Adds the MsgID to both request and response headers.
- Lightweight & fast, with minimal performance overhead.
Feature | Benefit |
---|---|
🆔 Unique Identifier | Adds a MsgID to each request for tracking and correlation. |
🔄 Automatic Generation | No need for manual MsgID creation, added seamlessly. |
📊 Enhanced Debugging | Makes log analysis easier by attaching request identifiers. |
🚀 Lightweight & Fast | Minimal performance impact, operates efficiently. |
The MsgID Middleware intercepts each incoming HTTP request. It checks if the request already has a MsgID in the headers. If not present, it generates a new MsgID and attaches it to:
- The request headers (Msgid)
- The response headers (Msgid)
The next middleware or handler processes the request with the assigned MsgID.
Here is an example of how to use the MsgID Middleware
with Quick:
package main
import (
"fmt"
"log"
"github.com/jeffotoni/quick"
"github.com/jeffotoni/quick/middleware/msguuid"
)
func main() {
q := quick.New()
// Apply MsgUUID Middleware globally
q.Use(msguuid.New())
// Define an endpoint that responds with a UUID
q.Get("/v1/msguuid/default", func(c *quick.Ctx) error {
c.Set("Content-Type", "application/json")
// Log headers to validate UUID presence
fmt.Println("Headers:", c.Response.Header())
// Return a 200 OK status
return c.Status(200).JSON(nil)
})
log.Fatal(q.Listen("0.0.0.0:8080"))
}
$ curl -i -X GET http://localhost:8080/v1/msguuid/default
{
"msgid": "974562398"
}
Helmet is a middleware this package provides sensible security defaults while allowing full customization.
- Sets common security-related HTTP headers
- Provides secure defaults
- Easily customizable via
Options
struct - Supports skipping middleware per request
Feature | Status | Notes / Observations |
---|---|---|
X-XSS-Protection header |
✅ | Legacy protection, still included |
X-Content-Type-Options: nosniff header |
✅ | Prevents MIME sniffing attacks |
X-Frame-Options header |
✅ | Helps prevent clickjacking |
Content-Security-Policy header |
✅ | Defaults to default-src 'self' |
CSPReportOnly support |
✅ | Optional report-only mode for CSP |
Referrer-Policy header |
✅ | Defaults to no-referrer |
Permissions-Policy header |
✅ | Controls browser features like camera, mic, etc. |
Strict-Transport-Security (HSTS) support |
✅ | Adds HSTS for HTTPS requests |
HSTS options: maxAge , includeSubDomains , preload |
✅ | Fully customizable |
Cache-Control header |
✅ | Defaults to no-cache, improves response integrity |
Cross-Origin-Embedder-Policy header |
✅ | Required for certain advanced browser APIs |
Cross-Origin-Opener-Policy header |
✅ | Isolates browsing contexts |
Cross-Origin-Resource-Policy header |
✅ | Restricts resource access |
Origin-Agent-Cluster header |
✅ | Enables memory isolation in browsers |
X-DNS-Prefetch-Control header |
✅ | Controls browser DNS prefetching |
X-Download-Options header |
✅ | Prevents automatic downloads (IE-specific) |
X-Permitted-Cross-Domain-Policies header |
✅ | Blocks Flash and Silverlight legacy access |
Next func(c) to skip middleware dynamically |
✅ | Allows conditional header injection per route |
Secure defaults applied when no options are provided | ✅ | Based on OWASP and best practices |
Option naming compatible with Fiber | ✅ | Enables easier migration from Fiber to Quick |
Built-in TLS simulation support in Qtest |
✅ | Enables full testing of HTTPS-only behavior |
Full HTTP method coverage in Qtest |
✅ | GET, POST, PUT, PATCH, DELETE, OPTIONS supported |
Extended Qtest assertions (headers, body, etc.) | ✅ | Includes AssertString , AssertNoHeader , and more |
package main
import (
"github.com/jeffotoni/quick"
"github.com/seuusuario/helmet"
)
func main() {
q := quick.New()
// Use Helmet middleware with default security headers
q.Use(helmet.Helmet())
// Simple route to test headers
q.Get("/v1/user", func(c *quick.Ctx) error {
// list all headers
headers := make(map[string]string)
for k, v := range c.Response.Header() {
if len(v) > 0 {
headers[k] = v[0]
}
}
return c.Status(200).JSONIN(headers)
})
q.Listen("0.0.0.0:8080")
}
$ curl -X GET 'http://localhost:8080/v1/user'
{
"Cache-Control": "no-cache, no-store, must-revalidate",
"Content-Security-Policy": "default-src 'self'",
"Cross-Origin-Embedder-Policy": "require-corp",
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Resource-Policy": "same-origin",
"Origin-Agent-Cluster": "?1",
"Referrer-Policy": "no-referrer",
"X-Content-Type-Options": "nosniff",
"X-DNS-Prefetch-Control": "off",
"X-Download-Options": "noopen",
"X-Frame-Options": "SAMEORIGIN",
"X-Permitted-Cross-Domain-Policies": "none",
"X-XSS-Protection": "0"
}
- Etag
- Pprof
- Proxy
- RequestID
- Skip
- Timeout