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)

Since httpserver v0.6.4, handlers registered through With that implement io.Closer are closed after the server stops serving requests. Implement Close() error as blocking, idempotent cleanup. See the released registration contract.

Request binding​

Bind functions​

FunctionHandler signatureUse case
Bind[I, O any]func(ctx context.Context, input *I) (O, error)Bind request to input and render typed output
BindR[I, O any]func(ctx context.Context, request *http.Request, input *I) (O, error)Bind with raw request access
BindN[O any]func(ctx context.Context) (O, error)No input binding
BindNR[O any]func(ctx context.Context, request *http.Request) (O, 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

The four response binding helpers accept a typed result O and negotiate its representation from the request's Accept header. The default renderer supports JSON only, uses JSON when Accept is missing, and returns 406 Not Acceptable when no accepted media type is supported. See Customizing responses in Build an HTTP service for response metadata and explicit Response overrides.

Typed outputs are negotiated before their StatusCode() metadata is applied. For a bodyless 204 No Content response that must be preserved when Accept is unsupported, return NewStatusResponse(http.StatusNoContent); explicit responses bypass negotiation. Other typed outputs, including those with custom status codes, remain negotiated.

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) any {
return map[string]any{"error": err.Error()}
})

Pass this option to NewServer or NewServerWithSettings, or add it to ServerDefinition.Options for RunServers. The callback runs after middleware selects a status from the last recorded error.

Status selection checks ErrorWithStatus first, then the first matching WithErrorMapper. Validation errors use 400 Bad Request. All other errors use 500 Internal Server Error.

The callback returns the body to send to the client, not another Go error. The endpoint still returns its error through its normal (output, error) result. A returned struct or map keeps the selected HTTP status. Private 5xx privacy passes internal server error to the callback.

A struct, map, or other serializable value that does not implement httpserver.Response uses the server's response negotiator. It selects a format from Accept, serializes the value, and sets Content-Type. JSON is the default. XML needs a negotiator that includes XMLRepresentation() and a value that encoding/xml can encode. In v0.6.4, error middleware falls back to JSON with the selected status if negotiation or encoding fails.

An explicit httpserver.Response bypasses negotiation and sends its format regardless of Accept. It owns the status and headers, including Content-Type. It does not inherit the selected error status. For fixed JSON with that status, return httpserver.NewJsonResponse(body, httpserver.WithStatusCode(statusCode)).

NewErrorWithStatus()​

Return a zero output and a wrapped error to set an expected client-error status:

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

For a struct output, replace nil with that struct's zero value.

Middleware can attach the same wrapped error to the Gin context:

ginCtx.Error(httpserver.NewErrorWithStatus(http.StatusBadRequest, err))

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.
path_regexstring 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.