Add original URI to request context; implement into fastcgi env

This commit is contained in:
Matthew Holt 2019-09-05 13:36:42 -06:00
parent 0830fbad03
commit 80b54f3b9d
No known key found for this signature in database
GPG key ID: 2A349DD577D586A5
2 changed files with 46 additions and 48 deletions

View file

@ -19,12 +19,14 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"path" "path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy" "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
"github.com/caddyserver/caddy/v2/modules/caddytls" "github.com/caddyserver/caddy/v2/modules/caddytls"
@ -37,25 +39,11 @@ func init() {
// Transport facilitates FastCGI communication. // Transport facilitates FastCGI communication.
type Transport struct { type Transport struct {
////////////////////////////// // TODO: Populate these
// TODO: taken from v1 Handler type softwareName string
softwareVersion string
SoftwareName string serverName string
SoftwareVersion string serverPort string
ServerName string
ServerPort string
//////////////////////////
// TODO: taken from v1 Rule type
// The base path to match. Required.
// Path string
// upstream load balancer
// balancer
// Always process files with this extension with fastcgi.
// Ext string
// Use this directory as the fastcgi root directory. Defaults to the root // Use this directory as the fastcgi root directory. Defaults to the root
// directory of the parent virtual host. // directory of the parent virtual host.
@ -67,16 +55,9 @@ type Transport struct {
// PATH_INFO for the CGI script to use. // PATH_INFO for the CGI script to use.
SplitPath string `json:"split_path,omitempty"` SplitPath string `json:"split_path,omitempty"`
// If the URL ends with '/' (which indicates a directory), these index
// files will be tried instead.
// IndexFiles []string
// Environment Variables // Environment Variables
EnvVars [][2]string `json:"env,omitempty"` EnvVars [][2]string `json:"env,omitempty"`
// Ignored paths
// IgnoredSubPaths []string
// The duration used to set a deadline when connecting to an upstream. // The duration used to set a deadline when connecting to an upstream.
DialTimeout caddy.Duration `json:"dial_timeout,omitempty"` DialTimeout caddy.Duration `json:"dial_timeout,omitempty"`
@ -170,7 +151,6 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
ip = strings.Replace(ip, "[", "", 1) ip = strings.Replace(ip, "[", "", 1)
ip = strings.Replace(ip, "]", "", 1) ip = strings.Replace(ip, "]", "", 1)
// TODO: respect index files? or leave that to matcher/rewrite (I prefer that)?
fpath := r.URL.Path fpath := r.URL.Path
// Split path in preparation for env variables. // Split path in preparation for env variables.
@ -194,16 +174,17 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string) pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string)
scriptName = path.Join(pathPrefix, scriptName) scriptName = path.Join(pathPrefix, scriptName)
// TODO: Disabled for now // Get the request URL from context. The context stores the original URL in case
// // Get the request URI from context. The context stores the original URI in case // it was changed by a middleware such as rewrite. By default, we pass the
// // it was changed by a middleware such as rewrite. By default, we pass the // original URI in as the value of REQUEST_URI (the user can overwrite this
// // original URI in as the value of REQUEST_URI (the user can overwrite this // if desired). Most PHP apps seem to want the original URI. Besides, this is
// // if desired). Most PHP apps seem to want the original URI. Besides, this is // how nginx defaults: http://stackoverflow.com/a/12485156/1048862
// // how nginx defaults: http://stackoverflow.com/a/12485156/1048862 reqURL, ok := r.Context().Value(caddyhttp.OriginalURLCtxKey).(url.URL)
// reqURL, _ := r.Context().Value(httpserver.OriginalURLCtxKey).(url.URL) if !ok {
// some requests, like active health checks, don't add this to
// // Retrieve name of remote user that was set by some downstream middleware such as basicauth. // the request context, so we can just use the current URL
// remoteUser, _ := r.Context().Value(httpserver.RemoteUserCtxKey).(string) reqURL = *r.URL
}
requestScheme := "http" requestScheme := "http"
if r.TLS != nil { if r.TLS != nil {
@ -224,19 +205,19 @@ func (t Transport) buildEnv(r *http.Request) (map[string]string, error) {
"REMOTE_HOST": ip, // For speed, remote host lookups disabled "REMOTE_HOST": ip, // For speed, remote host lookups disabled
"REMOTE_PORT": port, "REMOTE_PORT": port,
"REMOTE_IDENT": "", // Not used "REMOTE_IDENT": "", // Not used
// "REMOTE_USER": remoteUser, // TODO: "REMOTE_USER": "", // TODO: once there are authentication handlers, populate this
"REQUEST_METHOD": r.Method, "REQUEST_METHOD": r.Method,
"REQUEST_SCHEME": requestScheme, "REQUEST_SCHEME": requestScheme,
"SERVER_NAME": t.ServerName, "SERVER_NAME": t.ServerName,
"SERVER_PORT": t.ServerPort, "SERVER_PORT": t.ServerPort,
"SERVER_PROTOCOL": r.Proto, "SERVER_PROTOCOL": r.Proto,
"SERVER_SOFTWARE": t.SoftwareName + "/" + t.SoftwareVersion, "SERVER_SOFTWARE": t.SoftwareName + "/" + t.SoftwareVersion,
// Other variables // Other variables
// "DOCUMENT_ROOT": rule.Root, "DOCUMENT_ROOT": t.Root,
"DOCUMENT_URI": docURI, "DOCUMENT_URI": docURI,
"HTTP_HOST": r.Host, // added here, since not always part of headers "HTTP_HOST": r.Host, // added here, since not always part of headers
// "REQUEST_URI": reqURL.RequestURI(), // TODO: "REQUEST_URI": reqURL.RequestURI(),
"SCRIPT_FILENAME": scriptFilename, "SCRIPT_FILENAME": scriptFilename,
"SCRIPT_NAME": scriptName, "SCRIPT_NAME": scriptName,
} }

View file

@ -20,6 +20,7 @@ import (
"log" "log"
"net" "net"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
@ -58,6 +59,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl) ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
ctx = context.WithValue(ctx, ServerCtxKey, s) ctx = context.WithValue(ctx, ServerCtxKey, s)
ctx = context.WithValue(ctx, VarCtxKey, make(map[string]interface{})) ctx = context.WithValue(ctx, VarCtxKey, make(map[string]interface{}))
ctx = context.WithValue(ctx, OriginalURLCtxKey, cloneURL(r.URL))
r = r.WithContext(ctx) r = r.WithContext(ctx)
// once the pointer to the request won't change // once the pointer to the request won't change
@ -228,6 +230,18 @@ type HTTPErrorConfig struct {
Routes RouteList `json:"routes,omitempty"` Routes RouteList `json:"routes,omitempty"`
} }
// cloneURL makes a copy of r.URL and returns a
// new value that doesn't reference the original.
func cloneURL(u *url.URL) url.URL {
urlCopy := *u
if u.User != nil {
userInfo := new(url.Userinfo)
*userInfo = *u.User
urlCopy.User = userInfo
}
return urlCopy
}
// Context keys for HTTP request context values. // Context keys for HTTP request context values.
const ( const (
// For referencing the server instance // For referencing the server instance
@ -235,4 +249,7 @@ const (
// For the request's variable table // For the request's variable table
VarCtxKey caddy.CtxKey = "vars" VarCtxKey caddy.CtxKey = "vars"
// For the unmodified URL that originally came in with a request
OriginalURLCtxKey caddy.CtxKey = "original_url"
) )