- Run global middleware for all requests, including OPTIONS preflights, NotFound, and MethodNotAllowed — previously bypassed by httprouter's internal handling - Implement Hijacker, Flusher, and Pusher on the response writer for WebSocket, SSE, and HTTP/2 push support - Fix CORS: echo a single matching origin, handle AllowCredentials with wildcard, append Vary: Origin - Logger logs from a defer to capture correct status on panicked requests - Static and StaticFS accept route middleware; add Context.AddHeader; warn on NotFound handler override
169 lines
3.4 KiB
Go
169 lines
3.4 KiB
Go
package kite
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"mime/multipart"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/julienschmidt/httprouter"
|
|
)
|
|
|
|
type contextKey string
|
|
|
|
type Context struct {
|
|
w *responseWriter
|
|
r *http.Request
|
|
p httprouter.Params
|
|
}
|
|
|
|
func (c *Context) GetRequest() *http.Request {
|
|
return c.r
|
|
}
|
|
|
|
func (c *Context) GetResponse() http.ResponseWriter {
|
|
return c.w
|
|
}
|
|
|
|
func (c *Context) GetStatusCode() int {
|
|
return c.w.statusCode
|
|
}
|
|
|
|
func (c *Context) GetContext() context.Context {
|
|
return c.r.Context()
|
|
}
|
|
|
|
func (c *Context) GetPathParam(key string) string {
|
|
return c.p.ByName(key)
|
|
}
|
|
|
|
func (c *Context) GetQueryParam(key string) string {
|
|
return c.r.URL.Query().Get(key)
|
|
}
|
|
|
|
func (c *Context) SetValue(key string, v any) {
|
|
c.r = c.r.WithContext(context.WithValue(c.r.Context(), contextKey(key), v))
|
|
}
|
|
|
|
func (c *Context) GetValue(key string) any {
|
|
return c.r.Context().Value(contextKey(key))
|
|
}
|
|
|
|
func (c *Context) AddHeader(key, v string) {
|
|
c.w.Header().Add(key, v)
|
|
}
|
|
|
|
func (c *Context) SetHeader(key, v string) {
|
|
c.w.Header().Set(key, v)
|
|
}
|
|
|
|
func (c *Context) GetHeader(key string) string {
|
|
return c.r.Header.Get(key)
|
|
}
|
|
|
|
func (c *Context) SetCookie(cookie *http.Cookie) {
|
|
http.SetCookie(c.w, cookie)
|
|
}
|
|
|
|
func (c *Context) GetCookie(key string) (*http.Cookie, error) {
|
|
return c.r.Cookie(key)
|
|
}
|
|
|
|
func (c *Context) GetIP() string {
|
|
if ip := c.r.Header.Get("X-Forwarded-For"); ip != "" {
|
|
return strings.Split(ip, ",")[0]
|
|
}
|
|
|
|
if ip := c.r.Header.Get("X-Real-IP"); ip != "" {
|
|
return ip
|
|
}
|
|
|
|
ip, _, _ := net.SplitHostPort(c.r.RemoteAddr)
|
|
return ip
|
|
}
|
|
|
|
func (c *Context) GetMethod() string {
|
|
return c.r.Method
|
|
}
|
|
|
|
func (c *Context) GetPath() string {
|
|
return c.r.URL.Path
|
|
}
|
|
|
|
func (c *Context) GetForm() (url.Values, error) {
|
|
if err := c.r.ParseForm(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.r.Form, nil
|
|
}
|
|
|
|
func (c *Context) GetFormValue(key string) string {
|
|
return c.r.FormValue(key)
|
|
}
|
|
|
|
func (c *Context) GetMultipartForm(maxMemory int64) (*multipart.Form, error) {
|
|
if err := c.r.ParseMultipartForm(maxMemory); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.r.MultipartForm, nil
|
|
}
|
|
|
|
func (c *Context) GetMultipartFormFile(key string) (multipart.File, *multipart.FileHeader, error) {
|
|
return c.r.FormFile(key)
|
|
}
|
|
|
|
func (c *Context) IsMethod(method string) bool {
|
|
return c.r.Method == method
|
|
}
|
|
|
|
func (c *Context) WriteBytes(statusCode int, v []byte) error {
|
|
c.w.WriteHeader(statusCode)
|
|
_, err := c.w.Write(v)
|
|
return err
|
|
}
|
|
|
|
func (c *Context) WriteString(statusCode int, v string) error {
|
|
c.w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
c.w.WriteHeader(statusCode)
|
|
_, err := c.w.Write([]byte(v))
|
|
return err
|
|
}
|
|
|
|
func (c *Context) WriteJSON(statusCode int, v any) error {
|
|
c.w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
c.w.WriteHeader(statusCode)
|
|
return json.NewEncoder(c.w).Encode(v)
|
|
}
|
|
|
|
func (c *Context) WriteXML(statusCode int, v any) error {
|
|
c.w.Header().Set("Content-Type", "application/xml; charset=utf-8")
|
|
c.w.WriteHeader(statusCode)
|
|
return xml.NewEncoder(c.w).Encode(v)
|
|
}
|
|
|
|
func (c *Context) WriteNoContent() error {
|
|
c.w.WriteHeader(http.StatusNoContent)
|
|
return nil
|
|
}
|
|
|
|
func (c *Context) Redirect(statusCode int, url string) error {
|
|
http.Redirect(c.w, c.r, url, statusCode)
|
|
return nil
|
|
}
|
|
|
|
func (c *Context) BindJSON(v any) error {
|
|
defer c.r.Body.Close()
|
|
return json.NewDecoder(c.r.Body).Decode(v)
|
|
}
|
|
|
|
func (c *Context) BindXML(v any) error {
|
|
defer c.r.Body.Close()
|
|
return xml.NewDecoder(c.r.Body).Decode(v)
|
|
}
|