pnutmux

package module
v1.15.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2024 License: MIT Imports: 31 Imported by: 0

README ¶

Features

Pnutmux 🥜 is a powerful and flexible router for Go based on regular expressions.

Pnutmux 🥜 is a powerful and flexible router for Go based on regular expressions. People are free to use it and experiment with it. It is production ready and able to handle a large number of concurrent requests.

  • Handles CORS fully with a simple and effective middleware.
  • Supports HTTP2 in a simple and intuitive manner for enhanced performance.
  • Automatically checks the ALLOWED_ORIGINS environment variable for CORS.
  • Allows all origins if ALLOWED_ORIGINS is empty or does not exist.
  • Utilizes Go's standard library context for idiomatic purposes instead of a custom context.
  • Uses the slog package as the default logger to log messages in a structured manner.
  • Permits users to override the default structured logger by providing a simple implementation of the contract.Logger interface.
  • Efficiently decodes percent-encoded URLs.
  • Facilitates complex routing patterns and extraction of URL path & query parameters.
  • Provides a middleware for easy GZIP compression of HTTP responses.
  • Automatically marshals structured data to JSON when writing to http.ResponseWriter.
  • Enables scalable request handling for high-performance web applications by utilizing a semaphore at the entry point of ServeHTTP to limit concurrency ✋.
  • Thoroughly tested for all aspects: Pnutmux 🥜 has undergone comprehensive testing to ensure its reliability and safety, including addressing data race issues. By utilizing mutexes, it enables concurrent reads and safeguards concurrent writes to variables, ensuring secure and dependable performance.
  • IP-Based or Header-Based rate limiting: Pnutmux supports both IP-Based and Header-Based rate limiting, allowing you to control the rate of incoming requests based on either the client's IP or a custom header.
  • Flexible Redis Integration: Effortlessly implement rate limiting with Redis, whether locally or by creating a distributed rate limiting system using a Redis Cloud client.

Pnutmux 🥜

Pnutmux Logo

Mentioned in Awesome Go Go Reference License Badge

Go Report Card Tag Badge GitLab last commit Pipeline Badge Maintenance coverage

GitLab stars

Table of Contents

Description

Pnutmux 🥜 is a regex based, high-performance 🚀 and lightweight Go router that leverages regular expressions to match and handle HTTP requests. It offers seamless URL path parameters and query parameters extraction, enabling the development of robust web applications in a straightforward and efficient manner. Additionally, it incorporates built-in concurrency limiting capabilities to ensure optimal handling of requests with the help of a simple semaphore.

The concurrency limiting ✋ functionality is integrated within the PnutRouter.ServeHTTP() method. When PnutRouter.Run() invokes srv.ListenAndServe(), it spawns multiple goroutines to handle incoming requests, with each goroutine calling PnutRouter.ServeHTTP(). However, the execution of PnutRouter.ServeHTTP() is controlled at its entry point through a semaphore, whose size is determined by the parameter semCount defined in pnutmux.NewRouter(semCount uint, logger contract.Logger, http2Config *HTTP2Config). This feature empowers you to manage and queue the execution of your handlers effortlessly, eliminating the need for manual implementation elsewhere in your application code.

Moreover, Pnutmux 🥜 introduces a powerful Header-Based and IP-Based rate limiting feature using Redis 📌. This feature allows you to control the rate of incoming requests on a per-route basis, ensuring fair usage and preventing abuse. The rate limiting is implemented using a sliding window algorithm, with the request weights being fully customizable. This is achieved by passing the maxDailyScore parameter to the NewRouterWithRedis() function and the request weight to the Register() method. This rate limiting feature, backed by Redis 📌, is designed to be highly efficient and scalable, making Pnutmux 🥜 an excellent choice for building high-traffic web applications on distributed systems like Google Coud Run or Kubernetes.

Websites and Companies Using Pnutmux

Here is a non-exhaustive list of websites and companies that have chosen to power their backend infrastructure using Pnutmux:

Please note that this list is not comprehensive and is meant to showcase a few examples of entities utilizing Pnutmux for their backend needs. If you'd like to see your website or company added to this list, feel free to reach out to us.

Installation

To use Pnutmux in your Go project, simply run:

$ go get gitlab.com/fruitygo/pnutmux@latest

Usage

To use Pnutmux 🥜 in your Go code, you first need to import the package:

import "gitlab.com/fruitygo/pnutmux"

Create a new router, pass it an unsigned integer for setting concurrency limiting, and pass it your own implementation of contract.Logger interface, or nil, to use the default Pnutmux structured logger:

// In the given code snippet, a new router is created using pnutmux.NewRouter(40, yourOwnLoggerAdapter, nil).
// The value 40 represents the maximum number of concurrent requests that can be handled.

r := pnutmux.NewRouter(40, nil, nil)

Define handlers with path parameters & use pnutmux.Subpath() to define your paths:

home, _ := pnutmux.CompileRegex("/")
product, _ := pnutmux.CompileRegex("/product", "product_id", "[0-9]+", pnutmux.Subpath(), "category", "category_id", "[0-9]+")

Define handlers using pre-defined regex:

products, _ := pnutmux.CompileRegex("/product", "product_id", pnutmux.IdPattern)

Register routes by passing your handlers and http methods:

r.Register(home, homeHandler, pnutmux.Unlimited, "GET")
r.Register(product, productsHandler, pnutmux.Unlimited, "GET")

You could use the constants provided by the net/http package:

const (
	MethodGet     = "GET"
	MethodHead    = "HEAD"
	MethodPost    = "POST"
	MethodPut     = "PUT"
	MethodPatch   = "PATCH" // RFC 5789
	MethodDelete  = "DELETE"
	MethodConnect = "CONNECT"
	MethodOptions = "OPTIONS"
	MethodTrace   = "TRACE"
)

Start the server:

if err := r.Run("localhost:8000", 120*time.Second, 5*time.Second, 5*time.Second); err != nil {
    panic(err)
}

Example

A simple example showing how to use Pnutmux 🥜 efficiently:

package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"gitlab.com/fruitygo/pnutmux"
)

func main() {
	// Define the semaphore size.
	var c int = 100

	// Start the router and use the default structured logger which has a log level set at "slog.LevelDebug".
	r := pnutmux.NewRouter(c, nil, nil)

	// Define endpoints.
	// Note that we DO NOT include the regex anchors in our regex parameters (^ and $).
	product, _ := pnutmux.CompileRegex("/product", "product_id", "[0-9]+")
	invoices, _ := pnutmux.CompileRegex("/invoices")

	// Register routes by passing your handlers and http methods.
	// Use pnutmux.Unlimited to set the weight of the request on a router without Redis.
	r.Register(product, productsHandler, pnutmux.Unlimited, http.MethodGet)
	r.Register(invoices, invoicesHandler, pnutmux.Unlimited, http.MethodGet)

	// Create a custom not found handler.
	customNotFoundHandler := func(w http.ResponseWriter, r *http.Request) {
		pnutmux.RespondErr(w, http.StatusNotFound)
	}

	// Override not found handler.
	r.SetNotFoundHandler(customNotFoundHandler)

	// Start the server.
	if err := r.Run("localhost:8000", 120*time.Second, 5*time.Second, 5*time.Second); err != nil {
		panic(err)
	}
}

// Products handler.
func productsHandler(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	// Extract URL path parameters.
	id := pnutmux.Var(ctx, "product_id")

	// You could retrieve the complete map of path parameters instead.
	// params := pnutmux.Vars(ctx)

	// Use the "endpoint" key to retrieve the endpoint value.
	endpoint := pnutmux.Var(ctx, "endpoint")

	// Build response.
	// Note: The response must be a data type that can be converted to a JSON string.
	// If it cannot be converted, the response writer will respond with an error status
	// and a message "Error Writing Response", along with a status of Internal Server Error.
	res := fmt.Sprintf("The endpoint is %s and the product ID is %s", endpoint, id)
	response := struct {
		Msg string `json:"msg"`
	}{
		Msg: res,
	}

	// Respond.
	pnutmux.Respond(w, response, http.StatusOK)
}

// Invoices handler.
// This endpoint is call to retrieve a single, or multiple values.
// It depends of the query parameters received, for example ?invoice_number=777.
func invoicesHandler(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	// Extract query parameters slice.
	number := pnutmux.QueryVar(ctx, "invoice_number")

	// You could retrieve the complete map of query parameters instead.
	queryParams := pnutmux.QueryVars(ctx)
	numberFromMap := queryParams["invoice_number"]

	// Build response.
	res := fmt.Sprintf(w, "The invoice number extracted from the query parameters is %s", number[0])
	response := struct {
		Msg string `json:"msg"`
	}{
		Msg: res,
	}

	// Respond.
	pnutmux.Respond(w, response, http.StatusOK)
}

In the example above, we create a new instance of pnutmux.PnutRouter and register handler functions for the desired URL paths. The handler functions are executed when a matching request is made to the corresponding URL path. Finally, we start the HTTP server by calling r.Run() with the proper arguments. This will make the server listen on port 8080 for incoming requests.

Middleware

A simple example showing how to use pnutmux.Chain() function to wrap handlers with middlewares:

package main

import (
	"fmt"
	"net/http"
	"time"

	"gitlab.com/fruitygo/pnutmux"
)

func main() {
	var c int = 20

	// Start the router and use the default structured logger which has a log level set at "slog.LevelDebug".
	r := pnutmux.NewRouter(c, nil, nil)

	// Define endpoints.
	homeRoute, _ := pnutmux.CompileRegex("/")

	// Wrap homeHandler with middleware chain and enable Gzip.
	wrappedHandler := pnutmux.Chain(pnutmux.Gzip, middleware1, middleware2, middleware3)(homeHandler)

	// Register routes by passing your handlers and http methods.
	r.Register(homeRoute, wrappedHandler, pnutmux.Unlimited, http.MethodGet)

	// Start the server.
	if err := r.Run("localhost:8000", 120*time.Second, 5*time.Second, 5*time.Second); err != nil {
		panic(err)
	}
}

// Home handler.
func homeHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("Hello World!")
}

// Define middleware 1 function.
var middleware1 = func(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Executing middleware1 before the handler")
		next.ServeHTTP(w, r)
		fmt.Println("Executing middleware1 after the handler")
	})
}

// Define middleware 2 function.
var middleware2 = func(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Executing middleware2 before the handler")
		next.ServeHTTP(w, r)
		fmt.Println("Executing middleware2 after the handler")
	})
}

// Define middleware 3 function.
var middleware3 = func(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Executing middleware3 before the handler")
		next.ServeHTTP(w, r)
		fmt.Println("Executing middleware3 after the handler")
	})
}

In the example above, we wrap the homeHandler with 3 MiddlewareFunc. When a request is received at the "/" endpoint, the output is the following:

Executing middleware1 before the handler
Executing middleware2 before the handler
Executing middleware3 before the handler
Hello World!
Executing middleware3 after the handler
Executing middleware2 after the handler
Executing middleware1 after the handler

Built-in Rate-Limiting with Redis

Pnutmux now introduces a powerful rate-limiting mechanism with Redis 📌. This addition allows for efficient Header-Based rate limiting with auto fallback to IP-Based rate limiting, to control the flow of incoming requests and enhance the performance and reliability of your web applications. Use it locally with Redis, or use a Redis cloud client in order to easily implement distributed rate-limiting.

The NewRouterWithRedis() function is a key component of Pnutmux. It creates and returns a new instance of a router that implements the contract.Router interface.

  • semCount: An uint specifying the semaphore value to block ServeHTTP() at its entry point.
  • logger: An optional contract.Logger to customize the logging behavior. If logger is nil, a default logger implementation is used with a log level set at slog.LevelDebug.
  • rp: A pointer to your rate limiting configuration struct of type RateLimitingParams.
  • http2Config: An optional *HTTP2Config to enable HTTP/2 support. If http2Config is not nil, the router will use HTTP/2 with the provided TLS configuration. The HTTP2Config struct should include an embedded file system (FS) containing your TLS certificate and key, and the paths to these files (CertificateFile and KeyFile) relative to the embedded file system. If http2Config is nil, the router will use HTTP/1.1. If HeaderKey pointer is nil, IP-Based rate limiting is enabled. Otherwise, rate limiting will be Header-Based. In the case Header-Based is engaged and no header matching the HeaderKey is found, rate limiting automatically falls back to IP-Based rate limiting for the specific request.

A constant was defined to represent the weight of a request that is not rate limited. This constant can be utilized when calling Register() to set the weight of the request to zero.

  • Unlimited: This represents a weight of 0, meaning the request does not count towards the rate limit.
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"gitlab.com/fruitygo/pnutmux"
)

func main() {
	var semCount uint = 100
	var maxDailyScore uint = 300
	var headerKey string = "X-Your-Custom-Header-Key"

	// GroupID is a unique string used to identify a microservice or a group of microservices. It is utilized to group counts and is appended to the Redis key.
	// This way, you can use the same Redis client with many microservices.
	// You can apply rate limiting on a single microservice (by using a unique GroupID for each), on a group of microservices (by using the same GroupID for all in the group), or on all microservices (by using the same GroupID for all).
	// HeaderKey is a pointer to the string which is going to be utilized to perform Header-Based rate limiting. Use nil in order to trigger IP-Based rate limiting.
	rp := &RateLimitingParams{
		RedisClient:   yourOwnRedisClient,
		RedisTimeout:  5 * time.Millisecond,
		MaxDailyScore: maxDailyScore,
		HeaderKey:     &headerKey,
		GroupID:       "microserviceName",
	}

	r := pnutmux.NewRouterWithRedis(semCount, yourOwnLoggerAdapter, rp, nil)

	product, _ := pnutmux.CompileRegex("/product", "product_id", "[0-9]+")
	invoices, _ := pnutmux.CompileRegex("/invoices")

	r.Register(product, productsHandler, 1, http.MethodGet)                  // Using a weight of 1, allowing 300 daily calls to this route before receiving status 429.
	r.Register(invoices, invoicesHandler, pnutmux.Unlimited, http.MethodGet) // Using Unlimited to avoid rate-limiting this specific route.

	customNotFoundHandler := func(w http.ResponseWriter, r *http.Request) {
		pnutmux.RespondErr(w, http.StatusNotFound)
	}

	r.SetNotFoundHandler(customNotFoundHandler)

	if err := r.Run("localhost:8000", 120*time.Second, 5*time.Second, 5*time.Second); err != nil {
		panic(err)
	}
}

func productsHandler(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	id := pnutmux.Var(ctx, "product_id")

	endpoint := pnutmux.Var(ctx, "endpoint")

	res := fmt.Sprintf(w, "The endpoint is %s and the product ID is %s", endpoint, id)
	response := struct {
		Msg string `json:"msg"`
	}{
		Msg: res,
	}
	pnutmux.Respond(w, response, http.StatusOK)
}

func invoicesHandler(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithCancel(r.Context())
	defer cancel()

	number := pnutmux.QueryVar(ctx, "invoice_number")

	queryParams := pnutmux.QueryVars(ctx)
	numberFromMap := queryParams["invoice_number"]

	res := fmt.Sprintf(w, "The invoice number extracted from the query parameters is %s", number[0])
	response := struct {
		Msg string `json:"msg"`
	}{
		Msg: res,
	}
	pnutmux.Respond(w, response, http.StatusOK)
}

In this example, we instantiate a pnutmux.PnutRouter with Redis-Based rate limiting. We register handlers for specific URL paths, each with a defined request weight impacting the rate limit. Handlers are triggered upon receiving a matching request. The server is started with r.Run() with the proper arguments. This showcases the package's ability to efficiently manage request traffic using Redis, ensuring optimal resource utilization and preventing abuse.

Why Semaphore?

Pnutmux 🥜 incorporates a concurrency-limiting mechanism using a semaphore to efficiently manage the execution of handlers in the ServeHTTP method. When the PnutRouter.Run() method is invoked, multiple goroutines are spawned to handle incoming requests, each calling the ServeHTTP method. The semaphore acts as a gatekeeper at the entry point of ServeHTTP, controlling the number of concurrent executions.

Role of the Semaphore 🚥

  1. Concurrency Threshold: By utilizing the semaphore, you can set a maximum limit on the number of simultaneous requests that can enter the ServeHTTP method. This helps prevent resource exhaustion and ensures that your application operates within defined concurrency thresholds.

  2. Resource Protection: When your ServeHTTP implementation involves potentially resource-intensive operations, such as accessing a database (e.g., MongoDB), the semaphore prevents an excessive number of concurrent requests from overwhelming the external resource. This is crucial for maintaining optimal performance and preventing service degradation.

  3. Avoiding Outbound Overload: In scenarios where your backend interacts with external services or databases, limiting the concurrency at the entry point helps prevent outbound overload. This is particularly beneficial for avoiding contention and delays in outbound operations, such as database queries, by controlling the rate at which requests are processed.

Customization and Fine-Tuning

You have the flexibility to customize the size of the semaphore by providing the semCount parameter when creating a new router instance with pnutmux.NewRouter(semCount uint, logger contract.Logger, http2Config *HTTP2Config). Adjusting this parameter allows you to fine-tune the concurrency levels based on your application's requirements and the capacity of external resources.

By incorporating this semaphore concurrency limiting mechanism, Pnutmux 🥜 provides a robust solution for handling incoming requests, ensuring efficient resource utilization, and protecting against potential performance bottlenecks.

Feel free to experiment with different semCount values to find the optimal balance between concurrency and resource efficiency in your specific application scenario.

HTTP/2

Our router supports HTTP/2 and uses a minimum version of TLS 1.2. It dynamically adapts to any newer versions of TLS based on the version used by the client.

To enable HTTP/2, simply provide a non-null instance of *HTTP2Config when creating the router. If http2Config is nil, HTTP/2 will not be used.

The http2Config is an optional parameter that enables HTTP/2 support. It also includes an UseH2C field. If UseH2C is set to true, the router will use HTTP/2 with h2c (HTTP/2 Cleartext), an unencrypted version of HTTP/2. In this case, FS, CertificateFile, and KeyFile will not be utilized, and TLS will not be used.

If http2Config is not nil and UseH2C is false, the router will use HTTP/2 with the provided TLS configuration. The *HTTP2Config struct should include an embedded file system (FS) containing your TLS certificate and key, and the paths to these files (CertificateFile and KeyFile) relative to the embedded file system. If http2Config is nil, the router will use HTTP/1.1.

Here's an example of how you can initialize http2Config:

package main

import "embed"

//go:embed certificates
var FS embed.FS

http2Config := &pnutmux.HTTP2Config{
	UseH2C:          false,
	FS:              FS,
	CertificateFile: "certificates/mycertificate.crt",
	KeyFile:         "certificates/mykey.key",
}

router := NewRouterWithRedis(semCount, nil, rateLimitParams, http2Config)

Contributing

If you encounter any issues, have suggestions, or would like to contribute to the development of Pnutmux 🥜, please feel free to submit a pull request or open an issue. We appreciate your feedback and contributions in making Pnutmux 🥜 even better!

Reference

Refer to our Go Reference for detailed usage instructions and examples.

License

Pnutmux is licensed under the MIT License.

End

In conclusion, Pnutmux 🥜 is a lightweight and powerful 🚀 router for Go that uses regular expressions to match and handle HTTP requests. It offers easy URL parameter extraction and a clean API for handling requests and responses. With its easiness of use, you can easily create complex routing patterns and build efficient and scalable web applications. By using Pnutmux 🥜, you can unlock the power of flexible routing in your Go applications while ensuring data race-free operations, handling CORS effectively, and enjoying structured logging by default. Try it out today and experience enhanced routing capabilities in your projects! Start using Pnutmux 🥜 today by installing the package and importing it into your Go code.

Fabriktor Logo Fabriktor Logo Fabriktor Logo Fabriktor Logo

Documentation ¶

Overview ¶

Note: This package requires Go version 1.20 or later.

For more information and usage examples, refer to the README and documentation at https://gitlab.com/fruitygo/pnutmux.

Index ¶

Constants ¶

View Source
const (
	IdPattern                  = `[0-9]+`                                         // Matches any sequence of digits, used for IDs.
	NamePattern                = `[a-zA-Z]+`                                      // Matches any sequence of letters, used for names.
	WordPattern                = `\w+`                                            // Matches any sequence of word characters (letters, digits, underscores).
	WordCaseInsensitivePattern = `(?i)\w+`                                        // Matches any sequence of word characters, case-insensitive.
	EmailPattern               = `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}` // Matches any valid email address.
	UrlPattern                 = `https?://\S+`                                   // Matches any valid http or https URL.
	HexObjectIdPattern         = `[a-fA-F0-9]+`                                   // Matches any sequence of hexadecimal digits, used for object IDs.
	FirebaseUIDPattern         = `[a-zA-Z0-9+/=]+`                                // Matches any valid Firebase user ID.
	MongoDBObjectIdPattern     = `[0-9a-fA-F]{24}`                                // Matches any valid MongoDB ObjectID.
	FilePatternWithDot         = `.*\.[^/]*$`                                     // Matches any file path with a file extension.
)
View Source
const (
	// Unlimited represents an unlimited request weight.
	Unlimited pnutconfig.RequestWeight = 0
)

Variables ¶

This section is empty.

Functions ¶

func CompileRegex ¶ added in v1.1.7

func CompileRegex(endpoint string, pairs ...string) (*regexp.Regexp, error)

CompileRegex compiles a regular expression pattern from a provided endpoint and key-value parameter pairs, returning the compiled pattern and any compilation errors.

To set a parameter as a subpath, use the key pnutmux.Subpath.

In order to create a endpoint at the root, use an empty string as the endpoint or use forward slash "/".

To define your pairs of key -> value parameters, provide them as a list of strings, where the first string is the key and the second string is the value. A check is performed to ensure that the number of parameters is even, since they are pairs of key -> values.

Do not include any regex anchors for the values, as they are added automatically. Adding them will lead to unexpected results.

Here is an example of how to use this function to create a regex pattern for a URL endpoint:

/jobs/{job_id}/applications/{application_id}

CompileRegex("/jobs", "job_id", pnutmux.IdPattern, "applications", pnutmux.Subpath, "application_id", pnutmux.IdPattern)

func Gzip ¶ added in v1.13.0

func Gzip(next http.HandlerFunc) http.HandlerFunc

Gzip returns an http.HandlerFunc that compresses the HTTP response using gzip compression.

It checks the "Accept-Encoding" header of the HTTP request to determine if the client supports gzip compression.

func NewHTTP2ServerWrapper ¶ added in v1.13.0

func NewHTTP2ServerWrapper(addr string, handler http.Handler, idleTimeout, readTimeout, writeTimeout time.Duration, http2Config *HTTP2Config) (*serverwrapper, error)

NewHTTP2ServerWrapper creates a new serverwrapper instance with http/2 enabled.

func NewRouter ¶

func NewRouter(semCount uint, logger contract.Logger, http2Config *HTTP2Config) contract.Router

NewRouter creates and returns a new PnutRouter, implementing the contract.Router interface, with a specified semaphore count.

An optional contract.Logger can be provided to customize logging behavior, otherwise a default logger implementation is used.

A valid TLS configuration will enable HTTP2 if provided. If the HTTP2Config pointer is nil or struct contains zero values, HTTP2 will not be enabled.

func NewRouterWithRedis ¶ added in v1.13.0

func NewRouterWithRedis(semCount uint, logger contract.Logger, rp *RateLimitingParams, http2Config *HTTP2Config) contract.Router

NewRouterWithRedis creates and returns a new PnutRouter, implementing the contract.Router interface, with a specified semaphore count and optional Redis-Based rate limiting.

An optional contract.Logger can be provided to customize logging behavior, and a Redis client, timeout, and maximum daily calls can be provided for rate limiting.

If one of the parameters for rate limiting is not provided, the router will be created without rate limiting.

A valid TLS configuration will enable HTTP2 if provided. If the HTTP2Config pointer is nil or struct contains zero values, HTTP2 will not be enabled.

func NewServerWrapper ¶ added in v1.13.0

func NewServerWrapper(addr string, handler http.Handler, idleTimeout, readTimeout, writeTimeout time.Duration) *serverwrapper

NewServerWrapper creates a new serverwrapper instance.

func NewWrappedResponseWriter ¶ added in v1.13.0

func NewWrappedResponseWriter(l contract.Logger, rw http.ResponseWriter) *wrappedResponseWriter

NewWrappedResponseWriter creates a new instance of the wrappedResponseWriter struct.

func QueryVar ¶ added in v1.1.7

func QueryVar(ctx context.Context, key string) []string

QueryVar retrieves the value of a specified query variable from a given context, returning an empty string if the context lacks the necessary information or the key is not found.

func QueryVars ¶ added in v1.1.7

func QueryVars(ctx context.Context) map[string][]string

QueryVars retrieves query variables from a given context, returning them as a map[string][]string or nil if the context lacks the necessary information.

func Respond ¶ added in v1.13.0

func Respond(w http.ResponseWriter, data interface{}, code int, escapeHTML bool)

Respond writes a JSON response to the provided http.ResponseWriter with the given data and HTTP status code.

It utilizes the apijuice.WriteJSONResponse function for writing the response.

The response is not compressed, use pnutmux.Gzip() middleware to enable compression.

Parameters:

  • w: http.ResponseWriter to write the response to.
  • data: Data to be serialized and sent as the response payload.
  • code: HTTP status code to be set in the response.

func RespondErr ¶ added in v1.13.0

func RespondErr(w http.ResponseWriter, code int)

RespondError writes an error response to the provided http.ResponseWriter with the given error message and HTTP status code.

It utilizes the apijuice.WriteErrorResponse function for writing the error response.

The response message is extracted from the error code with http.StatusText.

Parameters:

  • w: http.ResponseWriter to write the error response to.
  • code: HTTP status code to be set in the response.

func ServeHTML ¶ added in v1.14.3

func ServeHTML(w http.ResponseWriter, htmlContent io.Reader, code int)

ServeHTML writes an HTML response to the provided http.ResponseWriter with the given HTML content and HTTP status code.

Parameters:

  • w: http.ResponseWriter to write the response to.
  • htmlContent: HTML content to be sent as the response payload.
  • code: HTTP status code to be set in the response.

func SetAllowedOrigin ¶ added in v1.1.7

func SetAllowedOrigin(origins string) error

SetAllowedOrigins sets the ALLOWED_ORIGINS environment variable to a provided, comma-separated list of allowed origins for CORS, logging and returning any errors encountered.

func Subpath ¶ added in v1.1.7

func Subpath() string

Subpath returns a string representing a subpath. Currently, it returns the static string "subpath".

func Var ¶

func Var(ctx context.Context, key string) string

Var retrieves a specific URL path parameter from the context, excluding subpaths.

func Vars ¶

func Vars(ctx context.Context) map[string]string

Vars returns a copy of the URL path parameters map from the context, excluding subpaths.

Types ¶

type HTTP2Config ¶ added in v1.13.0

type HTTP2Config struct {
	UseH2C          bool     // Use H2C if true, otherwise use HTTPS.
	FS              embed.FS // File system for embedded files. Optional if UseH2C is true.
	CertificateFile string   // Path to the certificate file. Optional if UseH2C is true.
	KeyFile         string   // Path to the key file. Optional if UseH2C is true.
}

HTTP2Config contains TLS configuration information.

func (*HTTP2Config) IsValid ¶ added in v1.13.0

func (h *HTTP2Config) IsValid() bool

IsValid checks if the HTTP2Config struct is valid.

type MiddlewareFunc ¶ added in v1.1.7

type MiddlewareFunc func(http.HandlerFunc) http.HandlerFunc

MiddlewareFunc represents a middleware function. It takes an http.HandlerFunc as input and returns an http.HandlerFunc.

This type can be used to define middleware functions that can be chained together using the Chain function.

func Chain ¶ added in v1.1.7

func Chain(handlers ...MiddlewareFunc) MiddlewareFunc

Chain combines multiple MiddlewareFunc functions into a single MiddlewareFunc, creating a middleware chain. The returned MiddlewareFunc applies each input middleware function to the next one in reverse order.

The function takes a variadic number of MiddlewareFunc functions as input. The order of the input functions represents the order in which they will be applied in the middleware chain, with the first function being applied last.

The returned MiddlewareFunc can be used as a middleware in an HTTP server because it follows the http.HandlerFunc interface.

type PnutRouter ¶ added in v1.1.7

type PnutRouter struct {
	// contains filtered or unexported fields
}

PnutRouter represents a router for handling HTTP requests. It uses a sync.RWMutex for synchronization, stores the registered routes in a map[*regexp.Regexp]route, and has an http.HandlerFunc for handling not found routes.

It holds the server instance in a contract.Server, uses a contract.Logger for logging, and a contract.Responder for responding.

func (*PnutRouter) Register ¶ added in v1.1.7

func (r *PnutRouter) Register(reg *regexp.Regexp, handler http.HandlerFunc, weight pnutconfig.RequestWeight, methods ...string) error

Register registers a handler function for the specified regular expression and HTTP methods on the PnutRouter.

If the router is not using Redis for rate limiting, the weight argument should be Unlimited, or 0.

func (*PnutRouter) Run ¶ added in v1.1.7

func (r *PnutRouter) Run(addr string, idleTimeout, readTimeout, writeTimeout time.Duration) error

Run starts the HTTP server on the PnutRouter, listening for incoming requests on the provided address.

Returns an error if the server is already running or encounters an error during execution.

func (*PnutRouter) ServeHTTP ¶ added in v1.1.7

func (r *PnutRouter) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP handles incoming HTTP requests, allowing PnutRouter to implement the http.Handler interface.

func (*PnutRouter) SetNotFoundHandler ¶ added in v1.1.7

func (r *PnutRouter) SetNotFoundHandler(handler http.HandlerFunc)

SetNotFoundHandler sets the custom not found handler for the PnutRouter.

func (*PnutRouter) Stop ¶ added in v1.1.7

func (r *PnutRouter) Stop() error

Stop gracefully stops the HTTP server on the PnutRouter, returning an error if there is an issue shutting down or if the server is not running.

type RateLimitingParams ¶ added in v1.13.0

type RateLimitingParams struct {
	RedisClient   *redis.Client
	RedisTimeout  time.Duration
	MaxDailyScore uint
	HeaderKey     *string
	GroupID       string
}

RateLimitingParams represents the parameters for configuring rate limiting.

RedisClient is the Redis client to use for rate limiting.

RedisTimeout is the timeout to use for Redis operations.

MaxDailyScore is the maximum daily score to use for rate limiting.

HeaderKey is the header key to use for rate limiting based on a custom header value, using this value as the identifier of the request.

GroupID is the group ID to use to allow rate limiting of a user across multiple microservices.

All parameters are required for rate limiting to be enabled, except for HeaderKey, which can be nil to use the IP-Based rate limiting.

If any of the parameters are zero values, rate limiting will not be enabled.

Directories ¶

Path Synopsis
internal
adapter/util/logger
Package logger is a package that provides a logger interface and an adapter for the slog package.
Package logger is a package that provides a logger interface and an adapter for the slog package.
adapter/util/responder
Package responder provides an adapter for writing responses to the response writer.
Package responder provides an adapter for writing responses to the response writer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL