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
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2023-07-21 21:23:49 +00:00
|
|
|
Error(status int, messages ...any) error
|
2023-07-22 09:48:35 +00:00
|
|
|
Get(param string) string
|
2023-07-19 11:01:51 +00:00
|
|
|
Reader(io.Reader) error
|
2023-07-22 10:32:52 +00:00
|
|
|
Request() Request
|
|
|
|
Response() Response
|
2023-07-19 11:01:51 +00:00
|
|
|
String(string) error
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// context represents a request & response context.
|
|
|
|
type context struct {
|
2023-07-22 10:32:52 +00:00
|
|
|
request request
|
|
|
|
response response
|
2023-07-18 16:02:57 +00:00
|
|
|
paramNames [maxParams]string
|
|
|
|
paramValues [maxParams]string
|
|
|
|
paramCount int
|
|
|
|
}
|
|
|
|
|
|
|
|
// newContext returns a new context from the pool.
|
2023-07-22 10:32:52 +00:00
|
|
|
func newContext(req *http.Request, res http.ResponseWriter) *context {
|
2023-07-18 16:02:57 +00:00
|
|
|
ctx := contextPool.Get().(*context)
|
2023-07-22 10:32:52 +00:00
|
|
|
ctx.request.Request = req
|
|
|
|
ctx.response.ResponseWriter = res
|
2023-07-18 16:02:57 +00:00
|
|
|
ctx.paramCount = 0
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes responds with a raw byte slice.
|
|
|
|
func (ctx *context) Bytes(body []byte) error {
|
|
|
|
_, err := ctx.response.Write(body)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error is used for sending error messages to the client.
|
2023-07-21 21:23:49 +00:00
|
|
|
func (ctx *context) 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))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-22 12:14:50 +00:00
|
|
|
ctx.response.WriteHeader(status)
|
2023-07-21 21:23:49 +00:00
|
|
|
return errors.Join(combined...)
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
2023-07-22 09:48:35 +00:00
|
|
|
// Get retrieves a parameter.
|
|
|
|
func (ctx *context) Get(param string) string {
|
|
|
|
for i := 0; i < ctx.paramCount; i++ {
|
|
|
|
if ctx.paramNames[i] == param {
|
|
|
|
return ctx.paramValues[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-07-19 11:01:51 +00:00
|
|
|
// Reader sends the contents of the io.Reader without creating an in-memory copy.
|
|
|
|
func (ctx *context) Reader(reader io.Reader) error {
|
|
|
|
_, err := io.Copy(ctx.response, reader)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-22 10:32:52 +00:00
|
|
|
// Request returns the HTTP request.
|
|
|
|
func (ctx *context) Request() Request {
|
|
|
|
return &ctx.request
|
|
|
|
}
|
|
|
|
|
|
|
|
// Response returns the HTTP response.
|
|
|
|
func (ctx *context) Response() Response {
|
|
|
|
return &ctx.response
|
|
|
|
}
|
|
|
|
|
2023-07-19 11:01:51 +00:00
|
|
|
// String responds with the given string.
|
|
|
|
func (ctx *context) String(body string) error {
|
|
|
|
slice := unsafe.Slice(unsafe.StringData(body), len(body))
|
|
|
|
return ctx.Bytes(slice)
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:02:57 +00:00
|
|
|
// addParameter adds a new parameter to the context.
|
|
|
|
func (ctx *context) addParameter(name string, value string) {
|
|
|
|
ctx.paramNames[ctx.paramCount] = name
|
|
|
|
ctx.paramValues[ctx.paramCount] = value
|
|
|
|
ctx.paramCount++
|
|
|
|
}
|