package server import ( "errors" "io" "net/http" "unsafe" ) // maxParams defines the maximum number of parameters per route. const maxParams = 16 // Context represents the interface for a request & response context. type Context interface { Bytes([]byte) error Error(status int, messages ...any) error Get(param string) string Reader(io.Reader) error Request() Request Response() Response String(string) error } // ctx represents a request & response context. type ctx struct { request request response response paramNames [maxParams]string paramValues [maxParams]string paramCount int } // newContext returns a new context from the pool. func newContext(req *http.Request, res http.ResponseWriter) *ctx { ctx := contextPool.Get().(*ctx) ctx.request.Request = req ctx.response.ResponseWriter = res ctx.paramCount = 0 return ctx } // Bytes responds with a raw byte slice. func (ctx *ctx) Bytes(body []byte) error { _, err := ctx.response.Write(body) return err } // Error is used for sending error messages to the client. func (ctx *ctx) Error(status int, messages ...any) error { var combined []error for _, msg := range messages { switch err := msg.(type) { case error: combined = append(combined, err) case string: combined = append(combined, errors.New(err)) } } ctx.response.WriteHeader(status) return errors.Join(combined...) } // Get retrieves a parameter. func (ctx *ctx) Get(param string) string { for i := 0; i < ctx.paramCount; i++ { if ctx.paramNames[i] == param { return ctx.paramValues[i] } } return "" } // Reader sends the contents of the io.Reader without creating an in-memory copy. func (ctx *ctx) Reader(reader io.Reader) error { _, err := io.Copy(ctx.response, reader) return err } // Request returns the HTTP request. func (ctx *ctx) Request() Request { return &ctx.request } // Response returns the HTTP response. func (ctx *ctx) Response() Response { return &ctx.response } // String responds with the given string. func (ctx *ctx) String(body string) error { slice := unsafe.Slice(unsafe.StringData(body), len(body)) return ctx.Bytes(slice) } // addParameter adds a new parameter to the context. func (ctx *ctx) addParameter(name string, value string) { ctx.paramNames[ctx.paramCount] = name ctx.paramValues[ctx.paramCount] = value ctx.paramCount++ }