2019-07-01 06:07:58 +08:00
|
|
|
// Copyright 2015 Matthew Holt and The Caddy Authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2019-05-21 00:59:20 +08:00
|
|
|
package caddyhttp
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2019-05-23 04:14:26 +08:00
|
|
|
"net"
|
2019-05-21 00:59:20 +08:00
|
|
|
"net/http"
|
2019-09-06 03:36:42 +08:00
|
|
|
"net/url"
|
2019-05-21 11:21:33 +08:00
|
|
|
"strconv"
|
2019-06-27 06:03:29 +08:00
|
|
|
"strings"
|
2019-10-29 04:39:37 +08:00
|
|
|
"time"
|
2019-05-21 00:59:20 +08:00
|
|
|
|
2019-07-03 02:37:06 +08:00
|
|
|
"github.com/caddyserver/caddy/v2"
|
|
|
|
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
2019-09-12 08:49:21 +08:00
|
|
|
"github.com/lucas-clemente/quic-go/http3"
|
2019-10-29 04:39:37 +08:00
|
|
|
"go.uber.org/zap"
|
|
|
|
"go.uber.org/zap/zapcore"
|
2019-05-21 00:59:20 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// Server is an HTTP server.
|
|
|
|
type Server struct {
|
2019-06-27 00:49:32 +08:00
|
|
|
Listen []string `json:"listen,omitempty"`
|
|
|
|
ReadTimeout caddy.Duration `json:"read_timeout,omitempty"`
|
|
|
|
ReadHeaderTimeout caddy.Duration `json:"read_header_timeout,omitempty"`
|
|
|
|
WriteTimeout caddy.Duration `json:"write_timeout,omitempty"`
|
|
|
|
IdleTimeout caddy.Duration `json:"idle_timeout,omitempty"`
|
|
|
|
MaxHeaderBytes int `json:"max_header_bytes,omitempty"`
|
|
|
|
Routes RouteList `json:"routes,omitempty"`
|
|
|
|
Errors *HTTPErrorConfig `json:"errors,omitempty"`
|
|
|
|
TLSConnPolicies caddytls.ConnectionPolicies `json:"tls_connection_policies,omitempty"`
|
|
|
|
AutoHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"`
|
2019-07-12 12:02:47 +08:00
|
|
|
MaxRehandles *int `json:"max_rehandles,omitempty"`
|
2019-09-18 13:13:21 +08:00
|
|
|
StrictSNIHost *bool `json:"strict_sni_host,omitempty"`
|
2019-10-29 04:39:37 +08:00
|
|
|
Logs *ServerLogConfig `json:"logs,omitempty"`
|
2019-05-21 00:59:20 +08:00
|
|
|
|
2019-09-10 22:03:37 +08:00
|
|
|
// This field is not subject to compatibility promises
|
|
|
|
ExperimentalHTTP3 bool `json:"experimental_http3,omitempty"`
|
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
tlsApp *caddytls.TLS
|
|
|
|
logger *zap.Logger
|
|
|
|
accessLogger *zap.Logger
|
|
|
|
errorLogger *zap.Logger
|
2019-09-12 08:49:21 +08:00
|
|
|
|
|
|
|
h3server *http3.Server
|
2019-05-21 00:59:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ServeHTTP is the entry point for all HTTP requests.
|
|
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
2019-06-29 09:28:47 +08:00
|
|
|
w.Header().Set("Server", "Caddy")
|
|
|
|
|
2019-09-12 08:49:21 +08:00
|
|
|
if s.h3server != nil {
|
|
|
|
err := s.h3server.SetQuicHeaders(w.Header())
|
|
|
|
if err != nil {
|
2019-10-29 04:39:37 +08:00
|
|
|
s.logger.Error("setting HTTP/3 Alt-Svc header", zap.Error(err))
|
2019-09-12 08:49:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 13:48:43 +08:00
|
|
|
// set up the context for the request
|
2019-06-15 01:58:28 +08:00
|
|
|
repl := caddy.NewReplacer()
|
|
|
|
ctx := context.WithValue(r.Context(), caddy.ReplacerCtxKey, repl)
|
2019-06-19 01:13:12 +08:00
|
|
|
ctx = context.WithValue(ctx, ServerCtxKey, s)
|
2019-10-29 04:39:37 +08:00
|
|
|
ctx = context.WithValue(ctx, VarsCtxKey, make(map[string]interface{}))
|
|
|
|
var url2 url.URL // avoid letting this escape to the heap
|
|
|
|
ctx = context.WithValue(ctx, OriginalRequestCtxKey, originalRequest(r, &url2))
|
2019-05-21 00:59:20 +08:00
|
|
|
r = r.WithContext(ctx)
|
|
|
|
|
2019-05-21 13:48:43 +08:00
|
|
|
// once the pointer to the request won't change
|
|
|
|
// anymore, finish setting up the replacer
|
|
|
|
addHTTPVarsToReplacer(repl, r, w)
|
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
loggableReq := LoggableHTTPRequest{r}
|
|
|
|
errLog := s.errorLogger.With(
|
|
|
|
// encode the request for logging purposes before
|
|
|
|
// it enters any handler chain; this is necessary
|
|
|
|
// to capture the original request in case it gets
|
|
|
|
// modified during handling
|
|
|
|
zap.Object("request", loggableReq),
|
|
|
|
)
|
|
|
|
|
|
|
|
if s.Logs != nil {
|
|
|
|
wrec := NewResponseRecorder(w, nil, nil)
|
|
|
|
w = wrec
|
|
|
|
accLog := s.accessLogger.With(
|
|
|
|
// capture the original version of the request
|
|
|
|
zap.Object("request", loggableReq),
|
|
|
|
)
|
|
|
|
start := time.Now()
|
|
|
|
defer func() {
|
|
|
|
latency := time.Since(start)
|
|
|
|
|
|
|
|
repl.Set("http.response.status", strconv.Itoa(wrec.Status()))
|
|
|
|
repl.Set("http.response.size", strconv.Itoa(wrec.Size()))
|
|
|
|
repl.Set("http.response.latency", latency.String())
|
|
|
|
|
|
|
|
logger := accLog
|
|
|
|
if s.Logs.LoggerNames != nil {
|
|
|
|
logger = logger.Named(s.Logs.LoggerNames[r.Host])
|
|
|
|
}
|
|
|
|
|
|
|
|
log := logger.Info
|
|
|
|
if wrec.Status() >= 400 {
|
|
|
|
log = logger.Error
|
|
|
|
}
|
|
|
|
|
|
|
|
log("request",
|
|
|
|
zap.String("common_log", repl.ReplaceAll(CommonLogFormat, "-")),
|
|
|
|
zap.Duration("latency", latency),
|
|
|
|
zap.Int("size", wrec.Size()),
|
|
|
|
zap.Int("status", wrec.Status()),
|
|
|
|
)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// guarantee ACME HTTP challenges; handle them
|
|
|
|
// separately from any user-defined handlers
|
|
|
|
if s.tlsApp.HandleHTTPChallenge(w, r) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// build and execute the primary handler chain
|
2019-07-12 12:02:47 +08:00
|
|
|
err := s.executeCompositeRoute(w, r, s.Routes)
|
2019-05-21 00:59:20 +08:00
|
|
|
if err != nil {
|
2019-10-29 04:39:37 +08:00
|
|
|
// prepare the error log
|
|
|
|
logger := errLog
|
2019-10-29 14:08:06 +08:00
|
|
|
if s.Logs != nil && s.Logs.LoggerNames != nil {
|
2019-10-29 04:39:37 +08:00
|
|
|
logger = logger.Named(s.Logs.LoggerNames[r.Host])
|
2019-05-21 11:21:33 +08:00
|
|
|
}
|
2019-05-21 00:59:20 +08:00
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
// get the values that will be used to log the error
|
|
|
|
errStatus, errMsg, errFields := errLogValues(err)
|
|
|
|
|
|
|
|
// add HTTP error information to request context
|
|
|
|
r = s.Errors.WithError(r, err)
|
|
|
|
|
2019-05-24 04:42:14 +08:00
|
|
|
if s.Errors != nil && len(s.Errors.Routes) > 0 {
|
2019-10-29 04:39:37 +08:00
|
|
|
// execute user-defined error handling route
|
|
|
|
err2 := s.executeCompositeRoute(w, r, s.Errors.Routes)
|
|
|
|
if err2 == nil {
|
|
|
|
// user's error route handled the error response
|
|
|
|
// successfully, so now just log the error
|
|
|
|
logger.Error(errMsg, errFields...)
|
2019-05-29 08:52:21 +08:00
|
|
|
} else {
|
2019-10-29 04:39:37 +08:00
|
|
|
// well... this is awkward
|
|
|
|
errFields = append([]zapcore.Field{
|
|
|
|
zap.String("error", err2.Error()),
|
|
|
|
zap.Namespace("first_error"),
|
|
|
|
zap.String("msg", errMsg),
|
|
|
|
}, errFields...)
|
|
|
|
logger.Error("error handling handler error", errFields...)
|
2019-05-24 04:42:14 +08:00
|
|
|
}
|
2019-10-29 04:39:37 +08:00
|
|
|
} else {
|
|
|
|
logger.Error(errMsg, errFields...)
|
|
|
|
w.WriteHeader(errStatus)
|
2019-05-21 00:59:20 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-12 12:02:47 +08:00
|
|
|
// executeCompositeRoute compiles a composite route from routeList and executes
|
|
|
|
// it using w and r. This function handles the sentinel ErrRehandle error value,
|
|
|
|
// which reprocesses requests through the stack again. Any error value returned
|
|
|
|
// from this function would be an actual error that needs to be handled.
|
|
|
|
func (s *Server) executeCompositeRoute(w http.ResponseWriter, r *http.Request, routeList RouteList) error {
|
|
|
|
maxRehandles := 0
|
|
|
|
if s.MaxRehandles != nil {
|
|
|
|
maxRehandles = *s.MaxRehandles
|
|
|
|
}
|
2019-05-21 00:59:20 +08:00
|
|
|
var err error
|
2019-07-12 12:02:47 +08:00
|
|
|
for i := -1; i <= maxRehandles; i++ {
|
2019-05-21 00:59:20 +08:00
|
|
|
// we started the counter at -1 because we
|
|
|
|
// always want to run this at least once
|
2019-07-12 12:02:47 +08:00
|
|
|
|
|
|
|
// the purpose of rehandling is often to give
|
|
|
|
// matchers a chance to re-evaluate on the
|
|
|
|
// changed version of the request, so compile
|
|
|
|
// the handler stack anew in each iteration
|
|
|
|
stack := routeList.BuildCompositeRoute(r)
|
|
|
|
stack = s.wrapPrimaryRoute(stack)
|
|
|
|
|
|
|
|
// only loop if rehandling is required
|
2019-05-21 00:59:20 +08:00
|
|
|
err = stack.ServeHTTP(w, r)
|
|
|
|
if err != ErrRehandle {
|
|
|
|
break
|
|
|
|
}
|
2019-07-12 12:02:47 +08:00
|
|
|
if i >= maxRehandles-1 {
|
2019-05-21 00:59:20 +08:00
|
|
|
return fmt.Errorf("too many rehandles")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-06-27 06:03:29 +08:00
|
|
|
// wrapPrimaryRoute wraps stack (a compiled middleware handler chain)
|
|
|
|
// in s.enforcementHandler which performs crucial security checks, etc.
|
|
|
|
func (s *Server) wrapPrimaryRoute(stack Handler) Handler {
|
|
|
|
return HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return s.enforcementHandler(w, r, stack)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// enforcementHandler is an implicit middleware which performs
|
|
|
|
// standard checks before executing the HTTP middleware chain.
|
|
|
|
func (s *Server) enforcementHandler(w http.ResponseWriter, r *http.Request, next Handler) error {
|
|
|
|
// enforce strict host matching, which ensures that the SNI
|
|
|
|
// value (if any), matches the Host header; essential for
|
|
|
|
// servers that rely on TLS ClientAuth sharing a listener
|
|
|
|
// with servers that do not; if not enforced, client could
|
|
|
|
// bypass by sending benign SNI then restricted Host header
|
2019-09-18 13:13:21 +08:00
|
|
|
if s.StrictSNIHost != nil && *s.StrictSNIHost && r.TLS != nil {
|
2019-06-27 06:03:29 +08:00
|
|
|
hostname, _, err := net.SplitHostPort(r.Host)
|
|
|
|
if err != nil {
|
|
|
|
hostname = r.Host // OK; probably lacked port
|
|
|
|
}
|
2019-10-16 05:37:46 +08:00
|
|
|
if !strings.EqualFold(r.TLS.ServerName, hostname) {
|
2019-06-27 06:03:29 +08:00
|
|
|
err := fmt.Errorf("strict host matching: TLS ServerName (%s) and HTTP Host (%s) values differ",
|
|
|
|
r.TLS.ServerName, hostname)
|
|
|
|
r.Close = true
|
|
|
|
return Error(http.StatusForbidden, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
2019-07-10 02:58:39 +08:00
|
|
|
// listenersUseAnyPortOtherThan returns true if there are any
|
|
|
|
// listeners in s that use a port which is not otherPort.
|
2019-05-23 04:14:26 +08:00
|
|
|
func (s *Server) listenersUseAnyPortOtherThan(otherPort int) bool {
|
|
|
|
for _, lnAddr := range s.Listen {
|
2019-09-06 03:14:39 +08:00
|
|
|
_, addrs, err := caddy.ParseNetworkAddress(lnAddr)
|
2019-05-23 04:14:26 +08:00
|
|
|
if err == nil {
|
|
|
|
for _, a := range addrs {
|
|
|
|
_, port, err := net.SplitHostPort(a)
|
|
|
|
if err == nil && port != strconv.Itoa(otherPort) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-09 22:25:48 +08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-09-19 08:01:32 +08:00
|
|
|
func (s *Server) hasListenerAddress(fullAddr string) bool {
|
|
|
|
netw, addrs, err := caddy.ParseNetworkAddress(fullAddr)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if len(addrs) != 1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
addr := addrs[0]
|
2019-09-09 22:25:48 +08:00
|
|
|
for _, lnAddr := range s.Listen {
|
2019-09-19 08:01:32 +08:00
|
|
|
thisNetw, thisAddrs, err := caddy.ParseNetworkAddress(lnAddr)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if thisNetw != netw {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, a := range thisAddrs {
|
|
|
|
if a == addr {
|
|
|
|
return true
|
2019-09-09 22:25:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-23 04:14:26 +08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-09-03 23:35:36 +08:00
|
|
|
func (s *Server) hasTLSClientAuth() bool {
|
|
|
|
for _, cp := range s.TLSConnPolicies {
|
2019-09-08 04:25:04 +08:00
|
|
|
if cp.ClientAuthentication != nil && cp.ClientAuthentication.Active() {
|
2019-09-03 23:35:36 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-06-27 00:49:32 +08:00
|
|
|
// AutoHTTPSConfig is used to disable automatic HTTPS
|
|
|
|
// or certain aspects of it for a specific server.
|
|
|
|
type AutoHTTPSConfig struct {
|
|
|
|
// If true, automatic HTTPS will be entirely disabled.
|
|
|
|
Disabled bool `json:"disable,omitempty"`
|
|
|
|
|
|
|
|
// If true, only automatic HTTP->HTTPS redirects will
|
|
|
|
// be disabled.
|
|
|
|
DisableRedir bool `json:"disable_redirects,omitempty"`
|
|
|
|
|
|
|
|
// Hosts/domain names listed here will not be included
|
|
|
|
// in automatic HTTPS (they will not have certificates
|
|
|
|
// loaded nor redirects applied).
|
|
|
|
Skip []string `json:"skip,omitempty"`
|
2019-06-27 00:57:18 +08:00
|
|
|
|
|
|
|
// Hosts/domain names listed here will still be enabled
|
|
|
|
// for automatic HTTPS (unless in the Skip list), except
|
|
|
|
// that certificates will not be provisioned and managed
|
|
|
|
// for these names.
|
|
|
|
SkipCerts []string `json:"skip_certificates,omitempty"`
|
2019-08-10 02:05:47 +08:00
|
|
|
|
|
|
|
// By default, automatic HTTPS will obtain and renew
|
|
|
|
// certificates for qualifying hostnames. However, if
|
|
|
|
// a certificate with a matching SAN is already loaded
|
|
|
|
// into the cache, certificate management will not be
|
|
|
|
// enabled. To force automated certificate management
|
|
|
|
// regardless of loaded certificates, set this to true.
|
|
|
|
IgnoreLoadedCerts bool `json:"ignore_loaded_certificates,omitempty"`
|
2019-06-27 00:49:32 +08:00
|
|
|
}
|
|
|
|
|
2019-06-27 00:57:18 +08:00
|
|
|
// Skipped returns true if name is in skipSlice, which
|
|
|
|
// should be one of the Skip* fields on ahc.
|
|
|
|
func (ahc AutoHTTPSConfig) Skipped(name string, skipSlice []string) bool {
|
|
|
|
for _, n := range skipSlice {
|
2019-06-27 00:49:32 +08:00
|
|
|
if name == n {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTPErrorConfig determines how to handle errors
|
|
|
|
// from the HTTP handlers.
|
|
|
|
type HTTPErrorConfig struct {
|
2019-05-23 02:32:36 +08:00
|
|
|
Routes RouteList `json:"routes,omitempty"`
|
2019-05-21 00:59:20 +08:00
|
|
|
}
|
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
// WithError makes a shallow copy of r to add the error to its
|
|
|
|
// context, and sets placeholders on the request's replacer
|
|
|
|
// related to err. It returns the modified request which has
|
|
|
|
// the error information in its context and replacer. It
|
|
|
|
// overwrites any existing error values that are stored.
|
|
|
|
func (*HTTPErrorConfig) WithError(r *http.Request, err error) *http.Request {
|
|
|
|
// add the raw error value to the request context
|
|
|
|
// so it can be accessed by error handlers
|
|
|
|
c := context.WithValue(r.Context(), ErrorCtxKey, err)
|
|
|
|
r = r.WithContext(c)
|
|
|
|
|
|
|
|
// add error values to the replacer
|
|
|
|
repl := r.Context().Value(caddy.ReplacerCtxKey).(caddy.Replacer)
|
|
|
|
repl.Set("http.error", err.Error())
|
|
|
|
if handlerErr, ok := err.(HandlerError); ok {
|
|
|
|
repl.Set("http.error.status_code", strconv.Itoa(handlerErr.StatusCode))
|
|
|
|
repl.Set("http.error.status_text", http.StatusText(handlerErr.StatusCode))
|
|
|
|
repl.Set("http.error.trace", handlerErr.Trace)
|
|
|
|
repl.Set("http.error.id", handlerErr.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServerLogConfig describes a server's logging configuration.
|
|
|
|
type ServerLogConfig struct {
|
|
|
|
LoggerNames map[string]string `json:"logger_names,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// errLogValues inspects err and returns the status code
|
|
|
|
// to use, the error log message, and any extra fields.
|
|
|
|
// If err is a HandlerError, the returned values will
|
|
|
|
// have richer information.
|
|
|
|
func errLogValues(err error) (status int, msg string, fields []zapcore.Field) {
|
|
|
|
if handlerErr, ok := err.(HandlerError); ok {
|
|
|
|
status = handlerErr.StatusCode
|
2019-10-31 11:41:52 +08:00
|
|
|
if handlerErr.Err == nil {
|
|
|
|
msg = err.Error()
|
|
|
|
} else {
|
|
|
|
msg = handlerErr.Err.Error()
|
|
|
|
}
|
2019-10-29 04:39:37 +08:00
|
|
|
fields = []zapcore.Field{
|
|
|
|
zap.Int("status", handlerErr.StatusCode),
|
|
|
|
zap.String("err_id", handlerErr.ID),
|
|
|
|
zap.String("err_trace", handlerErr.Trace),
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
status = http.StatusInternalServerError
|
|
|
|
msg = err.Error()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// originalRequest returns a partial, shallow copy of
|
|
|
|
// req, including: req.Method, deep copy of req.URL
|
|
|
|
// (into the urlCopy parameter, which should be on the
|
|
|
|
// stack), and req.RequestURI. Notably, headers are not
|
|
|
|
// copied. This function is designed to be very fast
|
|
|
|
// and efficient, and useful primarly for read-only
|
|
|
|
// logging purposes.
|
|
|
|
func originalRequest(req *http.Request, urlCopy *url.URL) http.Request {
|
|
|
|
urlCopy = cloneURL(req.URL)
|
|
|
|
return http.Request{
|
|
|
|
Method: req.Method,
|
|
|
|
RequestURI: req.RequestURI,
|
|
|
|
URL: urlCopy,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-06 03:36:42 +08:00
|
|
|
// cloneURL makes a copy of r.URL and returns a
|
|
|
|
// new value that doesn't reference the original.
|
2019-10-29 04:39:37 +08:00
|
|
|
func cloneURL(u *url.URL) *url.URL {
|
2019-09-06 03:36:42 +08:00
|
|
|
urlCopy := *u
|
|
|
|
if u.User != nil {
|
|
|
|
userInfo := new(url.Userinfo)
|
|
|
|
*userInfo = *u.User
|
|
|
|
urlCopy.User = userInfo
|
|
|
|
}
|
2019-10-29 04:39:37 +08:00
|
|
|
return &urlCopy
|
2019-09-06 03:36:42 +08:00
|
|
|
}
|
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
const (
|
|
|
|
// CommonLogFormat is the common log format. https://en.wikipedia.org/wiki/Common_Log_Format
|
|
|
|
CommonLogFormat = `{http.request.remote.host} ` + CommonLogEmptyValue + ` {http.handlers.authentication.user.id} [{time.now.common_log}] "{http.request.orig_method} {http.request.orig_uri} {http.request.proto}" {http.response.status} {http.response.size}`
|
|
|
|
|
|
|
|
// CommonLogEmptyValue is the common empty log value.
|
|
|
|
CommonLogEmptyValue = "-"
|
|
|
|
)
|
|
|
|
|
2019-06-27 00:49:32 +08:00
|
|
|
// Context keys for HTTP request context values.
|
|
|
|
const (
|
|
|
|
// For referencing the server instance
|
|
|
|
ServerCtxKey caddy.CtxKey = "server"
|
2019-06-19 01:13:12 +08:00
|
|
|
|
2019-08-10 02:05:47 +08:00
|
|
|
// For the request's variable table
|
2019-10-29 04:39:37 +08:00
|
|
|
VarsCtxKey caddy.CtxKey = "vars"
|
2019-09-06 03:36:42 +08:00
|
|
|
|
2019-10-29 04:39:37 +08:00
|
|
|
// For a partial copy of the unmodified request that
|
|
|
|
// originally came into the server's entry handler
|
|
|
|
OriginalRequestCtxKey caddy.CtxKey = "original_request"
|
2019-06-27 00:49:32 +08:00
|
|
|
)
|