package server import ( "errors" "io" "net/http" "unsafe" "git.akyoto.dev/go/router" ) // Context represents the interface for a request & response context. type Context interface { Bytes([]byte) error Error(messages ...any) error Get(param string) string Header(key string, value string) Host() string Method() string Next() error Path() string Protocol() string Reader(io.Reader) error RequestHeader(key string) string ResponseHeader(key string) string Scheme() string Status(status int) Context String(string) error Write([]byte) (int, error) WriteString(string) (int, error) } // ctx represents a request & response context. type ctx struct { request *http.Request response http.ResponseWriter server *server params []router.Parameter handlerCount uint8 } // Bytes responds with a raw byte slice. func (c *ctx) Bytes(body []byte) error { _, err := c.response.Write(body) return err } // Error is used for sending error messages to the client. func (ctx *ctx) Error(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)) } } return errors.Join(combined...) } // Get retrieves a parameter. func (ctx *ctx) Get(param string) string { for i := range len(ctx.params) { p := ctx.params[i] if p.Key == param { return p.Value } } return "" } // Next executes the next handler in the middleware chain. func (ctx *ctx) Next() error { ctx.handlerCount++ return ctx.server.handlers[ctx.handlerCount](ctx) } // RequestHeader returns the request header value for the given key. func (ctx *ctx) RequestHeader(key string) string { return ctx.request.Header.Get(key) } // ResponseHeader returns the response header value for the given key. func (ctx *ctx) ResponseHeader(key string) string { return ctx.response.Header().Get(key) } // Header sets the header value for the given key. func (ctx *ctx) Header(key string, value string) { ctx.response.Header().Set(key, value) } // Method returns the request method. func (ctx *ctx) Method() string { return ctx.request.Method } // Protocol returns the request protocol. func (ctx *ctx) Protocol() string { return ctx.request.Proto } // Host returns the requested host. func (ctx *ctx) Host() string { return ctx.request.Host } // Path returns the requested path. func (ctx *ctx) Path() string { return ctx.request.URL.Path } // 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 } // Scheme returns either `http` or `https`. func (ctx *ctx) Scheme() string { return ctx.request.URL.Scheme } // Status sets the HTTP status of the response. func (ctx *ctx) Status(status int) Context { ctx.response.WriteHeader(status) return ctx } // 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) } // Write implements the io.Writer interface. func (ctx *ctx) Write(body []byte) (int, error) { return ctx.response.Write(body) } // WriteString implements the io.StringWriter interface. func (ctx *ctx) WriteString(body string) (int, error) { return ctx.response.(io.StringWriter).WriteString(body) } // addParameter adds a new parameter to the context. func (ctx *ctx) addParameter(key string, value string) { ctx.params = append(ctx.params, router.Parameter{ Key: key, Value: value, }) }