Skip to main content

httpserver package

Package httpserver provides a modular HTTP server built on Gin and the gosoline kernel. It offers structured request binding, typed responses, middleware, SSE streaming, and dependency-injected handler registration.

import "github.com/gosoline-project/httpserver"

Application helpers

RunDefaultServer()

Usage

httpserver.RunDefaultServer(func(ctx context.Context, config cfg.Config, logger log.Logger, router *httpserver.Router) error {
router.GET("/ping", handler)
return nil
})

Description

Runs a single HTTP server named "default", reading configuration from httpserver.default. This is the simplest way to start a server.

RunServers()

Usage

httpserver.RunServers(map[string]httpserver.RouterFactory{
"public": publicDefiner,
"admin": adminDefiner,
})

Description

Runs multiple named HTTP servers. Each server reads its configuration from httpserver.<name>.

Server construction

NewServer()

Usage

application.WithModuleFactory("http", httpserver.NewServer("default", myRouterFactory))

Description

Creates a kernel.ModuleFactory for an HTTP server. The server is registered as a kernel module and runs at the Service stage. The name parameter determines both the config key (httpserver.<name>) and the module name (httpserver-<name>).

Router

RouterFactory

type RouterFactory func(ctx context.Context, config cfg.Config, logger log.Logger, router *Router) error

Implement this function to define your routes. Return an error to prevent the server from starting.

Router methods

Handle()

router.Handle(httpMethod, relativePath, handlers...)

Registers a route for the given HTTP method and path.

GET() / POST() / PUT() / DELETE() / PATCH() / OPTIONS()

router.GET("/users", handler)
router.POST("/users", handler)

Convenience methods for registering routes with specific HTTP methods.

Group()

api := router.Group("/api")
api.GET("/users", handler)

Creates a route group with a common path prefix. Groups can be nested.

Use()

router.Use(middlewareFunc)

Adds middleware to the router or group.

UseFactory()

router.UseFactory(func(ctx context.Context, config cfg.Config, logger log.Logger, settings *httpserver.Settings) (gin.HandlerFunc, error) {
// create middleware with access to config, logger, and server settings
})

Adds a middleware factory that is resolved lazily when the router is built. settings.Name contains the server name used for server-scoped configuration.

HandleWith()

router.Group("/api").HandleWith(httpserver.With(NewHandler, func(r *httpserver.Router, h *Handler) {
r.GET("/items", httpserver.Bind(h.ListItems))
}))

Registers routes using a RegisterFactoryFunc (typically created with With()).

Handler registration

With[H]()

Usage

router.HandleWith(httpserver.With(NewMyHandler, func(r *httpserver.Router, h *MyHandler) {
r.GET("/items", httpserver.Bind(h.ListItems))
r.POST("/items", httpserver.Bind(h.CreateItem))
}))

Description

Generic function that creates a handler instance via the factory function, then calls the registration function with both a sub-router and the handler. This is the primary pattern for dependency-injected route registration.

The handler factory must have this signature:

func NewMyHandler(ctx context.Context, config cfg.Config, logger log.Logger) (*MyHandler, error)

Request binding

Bind functions

FunctionHandler signatureUse case
Bind[I](ctx, *I) (Response, error)Bind request to input struct
BindR[I](ctx, *http.Request, *I) (Response, error)Bind with raw request access
BindN(ctx) (Response, error)No input binding
BindNR(ctx, *http.Request) (Response, error)No input, with raw request
BindSse[I](ctx, *I, *SseWriter) errorSSE with input binding
BindSseR[I](ctx, *http.Request, *I, *SseWriter) errorSSE with input + raw request
BindSseN(ctx, *SseWriter) errorSSE with no input
BindSseNR(ctx, *http.Request, *SseWriter) errorSSE with raw request, no input

Binding tags

Input structs use standard tags to specify the data source:

TagSourceExample
uriPath parametersuri:"id"
jsonJSON bodyjson:"name"
formQuery string or form bodyform:"limit"
xmlXML bodyxml:"item"
yamlYAML bodyyaml:"config"
headerHTTP headersheader:"X-Request-Id"
bindingValidation rulesbinding:"required,email"

Binding and validation failures return 400 Bad Request. Validation bind failures are logged at warning level, not error level, with field, tag, invalid value, and parameter details; invalid values are truncated before logging.

Responses

Response constructors

ConstructorDescription
NewJsonResponse[T](body T, opts ...ResponseOption) *jsonResponse[T]JSON response with Content-Type: application/json
NewTextResponse(text string, opts ...ResponseOption) *responsePlain text response
NewStatusResponse(statusCode int, opts ...ResponseOption) *responseStatus-only response (no body)
NewResponse(opts ...ResponseOption) *responseBase response constructor

Response options

OptionDescription
WithStatusCode(code int)Set the HTTP status code
WithHeader(key, value string)Add a single header
WithHeaders(headers http.Header)Merge multiple headers
WithBody(body []byte)Set raw response body

SSE

SseWriter

Methods

MethodDescription
Send(data string) errorSend a simple SSE data event
SendEvent(event SseEvent) errorSend a structured SSE event
Close()Close the writer and stop heartbeat

SseEvent

FieldTypeDescription
EventstringEvent type name
DatastringEvent payload
IdstringEvent ID for reconnection
RetryintReconnection hint (ms)

Error handling

WithErrorHandler()

httpserver.WithErrorHandler(func(statusCode int, err error) httpserver.Response {
return httpserver.NewJsonResponse(
map[string]any{"error": err.Error()},
httpserver.WithStatusCode(statusCode),
)
})

Sets a custom global error handler. By default, 4xx responses return {"err":"<message>"} and 5xx responses return {"err":"internal server error"}. Set httpserver.<name>.errors.privacy to public to expose 5xx error messages.

GetErrorHandler()

return httpserver.GetErrorHandler()(http.StatusBadRequest, err), nil

Returns the current error handler, useful for returning client errors with specific status codes.

Use NewErrorWithStatus(statusCode, err) when middleware attaches an error to the Gin context and the response should use a status other than 500.

Middleware

Cors()

corsMiddleware, err := httpserver.Cors(config, "default")

Creates a CORS middleware from httpserver.<name>.cors settings.

CorsFactory()

router.UseFactory(httpserver.CorsFactory)

Creates CORS middleware as a settings-aware middleware factory. The factory uses settings.Name, so reusable route setup automatically reads the CORS settings for the current named server.

CreateEmbeddedStaticServe()

router.UseFactory(httpserver.CreateEmbeddedStaticServe(fs, "public", "/api"))

Creates a middleware that serves embedded static files with SPA fallback.

Built-in middleware

The following middleware is automatically applied to every server:

MiddlewarePurpose
SamplingApply sampling decisions
MetricRecord request metrics
LoggingLog requests (fingers-crossed)
CompressionGzip compression
Max body sizeLimit incoming request bodies
ErrorCatch and format errors
RecoveryPanic recovery
Connection lifecycleManage connection age

Validation

AddCustomValidators()

httpserver.AddCustomValidators([]httpserver.CustomValidator{
{Name: "phone", Validator: validatePhone},
})

Register custom field validators for use in binding tags.

  • AddStructValidators([]StructValidator) — Register whole-struct validators
  • AddCustomTypeFuncs([]CustomTypeFunc) — Register custom type functions
  • AddValidateAlias([]ValidateAlias) — Register validation tag aliases

Configuration

Settings

Config key: httpserver.<name>

FieldTypeDefaultDescription
portstring"8080"Listening port. Use "0" for random port.
modestring"release"Gin mode: debug, release, test.
compressionCompressionSettings-Gzip configuration.
corsCorsSettings-CORS middleware configuration.
timeoutTimeoutSettings-IO timeouts.
loggingLoggingSettings-Request logging configuration.
errorsErrorsSettings-Error response privacy configuration.
routerRouterSettings-Router configuration.
max_body_bytesint10485760Maximum incoming request body size in bytes. 0 disables the limit.
concurrencyConcurrencySettings-Request and connection pressure limits.
connection_lifecycleConnectionLifeCycleAdvisorSettings-Connection age and request count limits.
chaosChaosSettings-Chaos middleware for resilience testing.

TimeoutSettings

FieldTypeDefaultDescription
readduration60sMax duration for reading entire request. Min 1s.
writeduration60sMax duration for writing response. Min 1s.
idleduration60sMax idle time for keep-alive. Min 1s.
drainduration0sWait time before graceful shutdown.
shutdownduration60sMax time for graceful shutdown. Min 1s.

CompressionSettings

FieldTypeDefaultDescription
levelstring"default""none", "default", "fast", "best", "0"-"9".
decompressionbooltrueDecompress gzip request bodies.
excludeCompressionExcludeSettings-Paths/extensions to exclude.

CompressionExcludeSettings

FieldTypeDescription
extensionstring arrayFile extensions to exclude.
pathstring arrayExact paths to exclude.
pathRegexstring arrayRegex patterns to exclude.

CorsSettings

FieldTypeDescription
allowed_origin_patternstringRegular expression matched against the full Origin value.
allowed_headersstring arrayAllowed request headers for CORS requests.
allowed_methodsstring arrayAllowed HTTP methods for CORS requests.

The origin pattern is anchored internally. For example, https://example\.com allows https://example.com, but not https://example.com.evil.com.

LoggingSettings

FieldTypeDefaultDescription
request_bodyboolfalseLog request bodies.
request_body_base64boolfalseBase64-encode request body in logs.
request_headersstring array[]Header names to include in logs. Only the configured headers are logged.

ErrorsSettings

FieldTypeDefaultDescription
privacystring"private""private" hides internal error details for 5xx responses. "public" exposes them.

RouterSettings

FieldTypeDefaultDescription
use_raw_pathboolfalseUse the raw URL path (not decoded) for route matching.

ConcurrencySettings

FieldTypeDefaultDescription
max_requestsint0Maximum concurrent requests. 0 disables the limit.
max_connectionsint0Target maximum open TCP connections. 0 disables connection pressure management.
overload_status_codeint503HTTP status code returned when max_requests is reached.
retry_afterduration0Value for the Retry-After response header when overloaded. 0 omits the header.

See Concurrency and connection pressure for detailed behaviour.

ConnectionLifeCycleAdvisorSettings

FieldTypeDefaultDescription
enabledbooltrueEnable connection lifecycle management.
max_ageduration1mMax connection age before signalling close. 0 disables age-based closing.
max_request_countint0Max requests per connection before signalling close. 0 disables count-based closing.

See Concurrency and connection pressure for detailed behaviour.

ChaosSettings

FieldTypeDefaultDescription
enabledboolfalseEnable chaos middleware.
rejectChaosRejectSettings-Random HTTP error responses.
delayChaosDelaySettings-Random pre-processing delays.
dropChaosDropSettings-Random TCP connection drops.
slow_responseChaosSlowResponseSettings-Random trickle responses.
truncateChaosTruncateSettings-Random response truncation.

ChaosRejectSettings

FieldTypeDefaultDescription
percentint3Probability (0–100) of rejection.
status_codesint array[499, 500, 502, 503, 504]Status codes to randomly pick from.

ChaosDelaySettings

FieldTypeDefaultDescription
percentint3Probability (0–100) of delay.
min_durationduration0Minimum delay.
max_durationduration60sMaximum delay.

ChaosDropSettings

FieldTypeDefaultDescription
percentint3Probability (0–100) of connection drop.

ChaosSlowResponseSettings

FieldTypeDefaultDescription
percentint3Probability (0–100) of throttled response.
delayduration1sPause between chunks.
chunk_sizeint64Bytes per chunk.

ChaosTruncateSettings

FieldTypeDefaultDescription
percentint3Probability (0–100) of truncation.
max_bytesint512Maximum body bytes before dropping.

See the Chaos middleware how-to for usage examples and safety notes.