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
| Function | Handler signature | Use 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) error | SSE with input binding |
BindSseR[I] | (ctx, *http.Request, *I, *SseWriter) error | SSE with input + raw request |
BindSseN | (ctx, *SseWriter) error | SSE with no input |
BindSseNR | (ctx, *http.Request, *SseWriter) error | SSE with raw request, no input |
Binding tags
Input structs use standard tags to specify the data source:
| Tag | Source | Example |
|---|---|---|
uri | Path parameters | uri:"id" |
json | JSON body | json:"name" |
form | Query string or form body | form:"limit" |
xml | XML body | xml:"item" |
yaml | YAML body | yaml:"config" |
header | HTTP headers | header:"X-Request-Id" |
binding | Validation rules | binding:"required,email" |
Responses
Response constructors
| Constructor | Description |
|---|---|
NewJsonResponse[T](body T, opts ...ResponseOption) *jsonResponse[T] | JSON response with Content-Type: application/json |
NewTextResponse(text string, opts ...ResponseOption) *response | Plain text response |
NewStatusResponse(statusCode int, opts ...ResponseOption) *response | Status-only response (no body) |
NewResponse(opts ...ResponseOption) *response | Base response constructor |
Response options
| Option | Description |
|---|---|
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
| Method | Description |
|---|---|
Send(data string) error | Send a simple SSE data event |
SendEvent(event SseEvent) error | Send a structured SSE event |
Close() | Close the writer and stop heartbeat |
SseEvent
| Field | Type | Description |
|---|---|---|
Event | string | Event type name |
Data | string | Event payload |
Id | string | Event ID for reconnection |
Retry | int | Reconnection 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)
Creates a CORS middleware from config keys: api_cors_allowed_origin_pattern, api_cors_allowed_headers, api_cors_allowed_methods.
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:
| Middleware | Purpose |
|---|---|
| Sampling | Apply sampling decisions |
| Metric | Record request metrics |
| Logging | Log requests (fingers-crossed) |
| Compression | Gzip compression |
| Max body size | Limit incoming request bodies |
| Error | Catch and format errors |
| Recovery | Panic recovery |
| Connection lifecycle | Manage connection age |
Validation
AddCustomValidators()
httpserver.AddCustomValidators([]httpserver.CustomValidator{
{Name: "phone", Validator: validatePhone},
})
Register custom field validators for use in binding tags.
Related functions
AddStructValidators([]StructValidator)— Register whole-struct validatorsAddCustomTypeFuncs([]CustomTypeFunc)— Register custom type functionsAddValidateAlias([]ValidateAlias)— Register validation tag aliases
Configuration
Settings
Config key: httpserver.<name>
| Field | Type | Default | Description |
|---|---|---|---|
port | string | "8080" | Listening port. Use "0" for random port. |
mode | string | "release" | Gin mode: debug, release, test. |
compression | CompressionSettings | - | Gzip configuration. |
timeout | TimeoutSettings | - | IO timeouts. |
logging | LoggingSettings | - | Request logging configuration. |
router | RouterSettings | - | Router configuration. |
max_body_bytes | int | 10485760 | Maximum incoming request body size in bytes. 0 disables the limit. |
TimeoutSettings
| Field | Type | Default | Description |
|---|---|---|---|
read | duration | 60s | Max duration for reading entire request. Min 1s. |
write | duration | 60s | Max duration for writing response. Min 1s. |
idle | duration | 60s | Max idle time for keep-alive. Min 1s. |
drain | duration | 0s | Wait time before graceful shutdown. |
shutdown | duration | 60s | Max time for graceful shutdown. Min 1s. |
CompressionSettings
| Field | Type | Default | Description |
|---|---|---|---|
level | string | "default" | "none", "default", "fast", "best", "0"-"9". |
decompression | bool | true | Decompress gzip request bodies. |
exclude | CompressionExcludeSettings | - | Paths/extensions to exclude. |
CompressionExcludeSettings
| Field | Type | Description |
|---|---|---|
extension | string array | File extensions to exclude. |
path | string array | Exact paths to exclude. |
pathRegex | string array | Regex patterns to exclude. |
LoggingSettings
| Field | Type | Default | Description |
|---|---|---|---|
request_body | bool | false | Log request bodies. |
request_body_base64 | bool | false | Base64-encode request body in logs. |
request_headers | bool | false | Log request headers. |
ConnectionLifeCycleAdvisorSettings
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | true | Enable connection lifecycle management. |
max_connection_age | duration | 1m | Max connection age before close. |
max_connection_request_count | int | 0 | Max requests per connection (0 = disabled). |