mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-23 17:41:05 +01:00
143 lines
2.9 KiB
Go
143 lines
2.9 KiB
Go
package routes
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/url"
|
|
"reflect"
|
|
"unsafe"
|
|
|
|
"github.com/yusing/godoxy/internal/types"
|
|
)
|
|
|
|
type RouteContextKey struct{}
|
|
|
|
type RouteContext struct {
|
|
context.Context
|
|
|
|
Route types.HTTPRoute
|
|
}
|
|
|
|
var routeContextKey = RouteContextKey{}
|
|
|
|
func (r *RouteContext) Value(key any) any {
|
|
if key == routeContextKey {
|
|
return r.Route
|
|
}
|
|
return r.Context.Value(key)
|
|
}
|
|
|
|
func WithRouteContext(r *http.Request, route types.HTTPRoute) *http.Request {
|
|
// we don't want to copy the request object every fucking requests
|
|
// return r.WithContext(context.WithValue(r.Context(), routeContextKey, route))
|
|
(*requestInternal)(unsafe.Pointer(r)).ctx = &RouteContext{
|
|
Context: r.Context(),
|
|
Route: route,
|
|
}
|
|
return r
|
|
}
|
|
|
|
func TryGetRoute(r *http.Request) types.HTTPRoute {
|
|
if route, ok := r.Context().Value(routeContextKey).(types.HTTPRoute); ok {
|
|
return route
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func tryGetURL(r *http.Request) *url.URL {
|
|
if route := TryGetRoute(r); route != nil {
|
|
u := route.TargetURL()
|
|
if u != nil {
|
|
return &u.URL
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func TryGetUpstreamName(r *http.Request) string {
|
|
if route := TryGetRoute(r); route != nil {
|
|
return route.Name()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TryGetUpstreamScheme(r *http.Request) string {
|
|
if u := tryGetURL(r); u != nil {
|
|
return u.Scheme
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TryGetUpstreamHost(r *http.Request) string {
|
|
if u := tryGetURL(r); u != nil {
|
|
return u.Hostname()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TryGetUpstreamPort(r *http.Request) string {
|
|
if u := tryGetURL(r); u != nil {
|
|
return u.Port()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TryGetUpstreamAddr(r *http.Request) string {
|
|
if u := tryGetURL(r); u != nil {
|
|
return u.Host
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func TryGetUpstreamURL(r *http.Request) string {
|
|
if u := tryGetURL(r); u != nil {
|
|
return u.String()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
type requestInternal struct {
|
|
Method string
|
|
URL *url.URL
|
|
Proto string
|
|
ProtoMajor int
|
|
ProtoMinor int
|
|
Header http.Header
|
|
Body io.ReadCloser
|
|
GetBody func() (io.ReadCloser, error)
|
|
ContentLength int64
|
|
TransferEncoding []string
|
|
Close bool
|
|
Host string
|
|
Form url.Values
|
|
PostForm url.Values
|
|
MultipartForm *multipart.Form
|
|
Trailer http.Header
|
|
RemoteAddr string
|
|
RequestURI string
|
|
TLS *tls.ConnectionState
|
|
Cancel <-chan struct{}
|
|
Response *http.Response
|
|
Pattern string
|
|
ctx context.Context
|
|
}
|
|
|
|
func init() {
|
|
// make sure ctx has the same offset as http.Request
|
|
f, ok := reflect.TypeFor[requestInternal]().FieldByName("ctx")
|
|
if !ok {
|
|
panic("ctx field not found")
|
|
}
|
|
f2, ok := reflect.TypeFor[http.Request]().FieldByName("ctx")
|
|
if !ok {
|
|
panic("ctx field not found")
|
|
}
|
|
if f.Offset != f2.Offset {
|
|
panic(fmt.Sprintf("ctx has different offset than http.Request: %d != %d", f.Offset, f2.Offset))
|
|
}
|
|
}
|