2023-07-18 19:48:38 +00:00
|
|
|
package server
|
2023-07-18 16:02:57 +00:00
|
|
|
|
|
|
|
import (
|
2023-07-21 21:23:49 +00:00
|
|
|
"errors"
|
2023-07-19 11:01:51 +00:00
|
|
|
"io"
|
2023-07-18 16:02:57 +00:00
|
|
|
"net/http"
|
2023-07-19 11:01:51 +00:00
|
|
|
"unsafe"
|
2023-07-18 16:02:57 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Context represents the interface for a request & response context.
|
|
|
|
type Context interface {
|
|
|
|
Bytes([]byte) error
|
2024-03-13 15:57:36 +00:00
|
|
|
Error(messages ...any) error
|
2023-07-22 09:48:35 +00:00
|
|
|
Get(param string) string
|
2024-03-13 19:18:01 +00:00
|
|
|
Header(key string, value string)
|
|
|
|
Host() string
|
|
|
|
Method() string
|
|
|
|
Next() error
|
|
|
|
Path() string
|
|
|
|
Protocol() string
|
2023-07-19 11:01:51 +00:00
|
|
|
Reader(io.Reader) error
|
2024-03-13 19:18:01 +00:00
|
|
|
RequestHeader(key string) string
|
|
|
|
ResponseHeader(key string) string
|
2024-03-14 11:52:03 +00:00
|
|
|
Scheme() string
|
2024-03-13 15:57:36 +00:00
|
|
|
Status(status int) Context
|
2023-07-19 11:01:51 +00:00
|
|
|
String(string) error
|
2024-03-13 21:49:57 +00:00
|
|
|
Write([]byte) (int, error)
|
2024-03-14 11:52:03 +00:00
|
|
|
WriteString(string) (int, error)
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
2024-03-12 21:31:45 +00:00
|
|
|
// ctx represents a request & response context.
|
|
|
|
type ctx struct {
|
2024-03-13 19:18:01 +00:00
|
|
|
request *http.Request
|
|
|
|
response http.ResponseWriter
|
2024-03-14 11:52:03 +00:00
|
|
|
server *server
|
2024-03-14 14:11:00 +00:00
|
|
|
params []param
|
|
|
|
handlerCount uint8
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes responds with a raw byte slice.
|
2024-03-14 14:11:00 +00:00
|
|
|
func (c *ctx) Bytes(body []byte) error {
|
|
|
|
_, err := c.response.Write(body)
|
2023-07-18 16:02:57 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error is used for sending error messages to the client.
|
2024-03-13 15:57:36 +00:00
|
|
|
func (ctx *ctx) Error(messages ...any) error {
|
2023-07-21 21:23:49 +00:00
|
|
|
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...)
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
2023-07-22 09:48:35 +00:00
|
|
|
// Get retrieves a parameter.
|
2024-03-12 21:31:45 +00:00
|
|
|
func (ctx *ctx) Get(param string) string {
|
2024-03-14 14:11:00 +00:00
|
|
|
for i := range len(ctx.params) {
|
|
|
|
p := ctx.params[i]
|
|
|
|
|
|
|
|
if p.Name == param {
|
|
|
|
return p.Value
|
2023-07-22 09:48:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2024-03-13 19:18:01 +00:00
|
|
|
// 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)
|
2023-07-19 11:01:51 +00:00
|
|
|
}
|
|
|
|
|
2024-03-13 19:18:01 +00:00
|
|
|
// Method returns the request method.
|
|
|
|
func (ctx *ctx) Method() string {
|
|
|
|
return ctx.request.Method
|
2023-07-22 10:32:52 +00:00
|
|
|
}
|
|
|
|
|
2024-03-13 19:18:01 +00:00
|
|
|
// 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
|
2023-07-22 10:32:52 +00:00
|
|
|
}
|
|
|
|
|
2024-03-14 11:52:03 +00:00
|
|
|
// Scheme returns either `http` or `https`.
|
|
|
|
func (ctx *ctx) Scheme() string {
|
|
|
|
return ctx.request.URL.Scheme
|
|
|
|
}
|
|
|
|
|
2024-03-13 15:57:36 +00:00
|
|
|
// Status sets the HTTP status of the response.
|
|
|
|
func (ctx *ctx) Status(status int) Context {
|
|
|
|
ctx.response.WriteHeader(status)
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2023-07-19 11:01:51 +00:00
|
|
|
// String responds with the given string.
|
2024-03-12 21:31:45 +00:00
|
|
|
func (ctx *ctx) String(body string) error {
|
2023-07-19 11:01:51 +00:00
|
|
|
slice := unsafe.Slice(unsafe.StringData(body), len(body))
|
|
|
|
return ctx.Bytes(slice)
|
|
|
|
}
|
|
|
|
|
2024-03-13 21:49:57 +00:00
|
|
|
// Write implements the io.Writer interface.
|
|
|
|
func (ctx *ctx) Write(body []byte) (int, error) {
|
|
|
|
return ctx.response.Write(body)
|
|
|
|
}
|
|
|
|
|
2024-03-14 11:52:03 +00:00
|
|
|
// WriteString implements the io.StringWriter interface.
|
|
|
|
func (ctx *ctx) WriteString(body string) (int, error) {
|
|
|
|
return ctx.response.(io.StringWriter).WriteString(body)
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:02:57 +00:00
|
|
|
// addParameter adds a new parameter to the context.
|
2024-03-12 21:31:45 +00:00
|
|
|
func (ctx *ctx) addParameter(name string, value string) {
|
2024-03-14 14:11:00 +00:00
|
|
|
ctx.params = append(ctx.params, param{name, value})
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|