From 8a8ee1bcfd4ba9de55ff521675295ffa73622d25 Mon Sep 17 00:00:00 2001 From: Nicholas Jackson Date: Sat, 9 Mar 2024 11:39:49 -0800 Subject: [PATCH] feat: move some URI methods to Request --- client/request_test.go | 4 +- ctx.go | 77 ++++++--------------------------------- ctx_interface.go | 1 + request.go | 83 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 69 deletions(-) diff --git a/client/request_test.go b/client/request_test.go index 943c5c66fdb..fe592bae591 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -975,7 +975,7 @@ func Test_Request_Body_With_Server(t *testing.T) { t.Parallel() testRequest(t, func(c fiber.Ctx) error { - require.Equal(t, "application/json", string(c.Get(fiber.HeaderContentType))) + require.Equal(t, "application/json", c.Get(fiber.HeaderContentType)) return c.SendString(string(c.Req().BodyRaw())) }, func(agent *Request) { @@ -1010,7 +1010,7 @@ func Test_Request_Body_With_Server(t *testing.T) { t.Parallel() testRequest(t, func(c fiber.Ctx) error { - require.Equal(t, fiber.MIMEApplicationForm, string(c.Get(fiber.HeaderContentType))) + require.Equal(t, fiber.MIMEApplicationForm, c.Get(fiber.HeaderContentType)) return c.Send([]byte("foo=" + c.FormValue("foo") + "&bar=" + c.FormValue("bar") + "&fiber=" + c.FormValue("fiber"))) }, func(agent *Request) { diff --git a/ctx.go b/ctx.go index f35a29c1d76..38de5efb224 100644 --- a/ctx.go +++ b/ctx.go @@ -175,25 +175,19 @@ func (c *DefaultCtx) Attachment(filename ...string) { c.setCanonical(HeaderContentDisposition, "attachment") } -// BaseURL returns (protocol + host + base path). +// BaseURL is an alias of [Request.BaseURL]. func (c *DefaultCtx) BaseURL() string { - // TODO: Could be improved: 53.8 ns/op 32 B/op 1 allocs/op - // Should work like https://codeigniter.com/user_guide/helpers/url_helper.html - if c.baseURI != "" { - return c.baseURI - } - c.baseURI = c.Scheme() + "://" + c.Host() - return c.baseURI + return c.req.BaseURL() } // BodyRaw is an alias of [Request.BodyRaw]. func (c *DefaultCtx) BodyRaw() []byte { - return c.Req().BodyRaw() + return c.req.BodyRaw() } // Body is an alias of [Request.Body]. func (c *DefaultCtx) Body() []byte { - return c.Req().Body() + return c.req.Body() } // ClearCookie expires a specific cookie by key on the client side. @@ -457,7 +451,7 @@ func (c *DefaultCtx) Fresh() bool { // Get is an alias of [Request.Get]. func (c *DefaultCtx) Get(key string, defaultValue ...string) string { - return c.Req().Get(key, defaultValue...) + return c.req.Get(key, defaultValue...) } // GetRespHeader returns the HTTP response header specified by field. @@ -492,21 +486,9 @@ func (c *DefaultCtx) GetReqHeaders() map[string][]string { return headers } -// Host contains the host derived from the X-Forwarded-Host or Host HTTP header. -// Returned value is only valid within the handler. Do not store any references. -// Make copies or use the Immutable setting instead. -// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +// Host is an alias of [Request.Host]. func (c *DefaultCtx) Host() string { - if c.IsProxyTrusted() { - if host := c.Get(HeaderXForwardedHost); len(host) > 0 { - commaPos := strings.Index(host, ",") - if commaPos != -1 { - return host[:commaPos] - } - return host - } - } - return c.app.getString(c.fasthttp.Request.URI().Host()) + return c.req.Host() } // Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method. @@ -849,9 +831,7 @@ func (c *DefaultCtx) RestartRouting() error { return err } -// OriginalURL contains the original request URL. -// Returned value is only valid within the handler. Do not store any references. -// Make copies or use the Immutable setting to use the value outside the Handler. +// OriginalURL is an alias of [Request.OriginalURL] func (c *DefaultCtx) OriginalURL() string { return c.req.OriginalURL() } @@ -913,47 +893,14 @@ func (c *DefaultCtx) Path(override ...string) string { return c.path } -// Scheme contains the request protocol string: http or https for TLS requests. -// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +// Scheme is an alias of [Request.Scheme]. func (c *DefaultCtx) Scheme() string { - if c.fasthttp.IsTLS() { - return schemeHTTPS - } - if !c.IsProxyTrusted() { - return schemeHTTP - } - - scheme := schemeHTTP - const lenXHeaderName = 12 - c.fasthttp.Request.Header.VisitAll(func(key, val []byte) { - if len(key) < lenXHeaderName { - return // Neither "X-Forwarded-" nor "X-Url-Scheme" - } - switch { - case bytes.HasPrefix(key, []byte("X-Forwarded-")): - if bytes.Equal(key, []byte(HeaderXForwardedProto)) || - bytes.Equal(key, []byte(HeaderXForwardedProtocol)) { - v := c.app.getString(val) - commaPos := strings.Index(v, ",") - if commaPos != -1 { - scheme = v[:commaPos] - } else { - scheme = v - } - } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { - scheme = schemeHTTPS - } - - case bytes.Equal(key, []byte(HeaderXUrlScheme)): - scheme = c.app.getString(val) - } - }) - return scheme + return c.req.Scheme() } -// Protocol returns the HTTP protocol of request: HTTP/1.1 and HTTP/2. +// Protocol is an alias of [Request.Protocol]. func (c *DefaultCtx) Protocol() string { - return utils.UnsafeString(c.fasthttp.Request.Header.Protocol()) + return c.req.Protocol() } // Query returns the query string parameter in the url. diff --git a/ctx_interface.go b/ctx_interface.go index 31e0f73f3af..d0f72574abd 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -504,6 +504,7 @@ func (c *DefaultCtx) setReq(fctx *fasthttp.RequestCtx) { c.fasthttp = fctx c.req = Request{ app: c.app, + ctx: c, fasthttp: &c.fasthttp.Request, } // c.res = &Response{app: c.app} diff --git a/request.go b/request.go index c353642f801..66fd0c26274 100644 --- a/request.go +++ b/request.go @@ -1,23 +1,102 @@ package fiber import ( + "bytes" + "strings" + "github.com/gofiber/utils/v2" "github.com/valyala/fasthttp" ) type Request struct { - app *App - fasthttp *fasthttp.Request + app *App // Reference to the parent App. + ctx Ctx // Reference to the parent Ctx. + fasthttp *fasthttp.Request // Reference to the underlying fasthttp.Request object. + baseURI string // HTTP base URI for memoization. } func (r *Request) App() *App { return r.app } +// OriginalURL contains the original request URL. +// Returned value is only valid within the handler. Do not store any references. +// Make copies or use the Immutable setting to use the value outside the Handler. func (r *Request) OriginalURL() string { return r.app.getString(r.fasthttp.Header.RequestURI()) } +// BaseURL returns (protocol + host + base path). +func (r *Request) BaseURL() string { + // TODO: Could be improved: 53.8 ns/op 32 B/op 1 allocs/op + // Should work like https://codeigniter.com/user_guide/helpers/url_helper.html + if r.baseURI != "" { + return r.baseURI + } + r.baseURI = r.Scheme() + "://" + r.Host() + return r.baseURI +} + +// Protocol returns the HTTP protocol of request: HTTP/1.1 and HTTP/2. +func (r *Request) Protocol() string { + return r.app.getString(r.fasthttp.Header.Protocol()) +} + +// Scheme contains the request protocol string: http or https for TLS requests. +// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +func (r *Request) Scheme() string { + if string(r.fasthttp.URI().Scheme()) == "https" { + return schemeHTTPS + } + if !r.ctx.IsProxyTrusted() { + return schemeHTTP + } + + scheme := schemeHTTP + const lenXHeaderName = 12 + r.fasthttp.Header.VisitAll(func(key, val []byte) { + if len(key) < lenXHeaderName { + return // Neither "X-Forwarded-" nor "X-Url-Scheme" + } + switch { + case bytes.HasPrefix(key, []byte("X-Forwarded-")): + if string(key) == HeaderXForwardedProto || + string(key) == HeaderXForwardedProtocol { + v := r.app.getString(val) + commaPos := strings.IndexByte(v, ',') + if commaPos != -1 { + scheme = v[:commaPos] + } else { + scheme = v + } + } else if string(key) == HeaderXForwardedSsl && string(val) == "on" { + scheme = schemeHTTPS + } + + case string(key) == HeaderXUrlScheme: + scheme = r.app.getString(val) + } + }) + return scheme +} + +// Host contains the host derived from the X-Forwarded-Host or Host HTTP header. +// Returned value is only valid within the handler. Do not store any references. +// Make copies or use the Immutable setting instead. +// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +func (r *Request) Host() string { + if r.ctx.IsProxyTrusted() { + if host := r.Get(HeaderXForwardedHost); len(host) > 0 { + commaPos := strings.Index(host, ",") + if commaPos != -1 { + return host[:commaPos] + } + return host + } + } + return r.app.getString(r.fasthttp.URI().Host()) +} + // BodyRaw contains the raw body submitted in a POST request. // Returned value is only valid within the handler. Do not store any references. // Make copies or use the Immutable setting instead.