github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gorilla/handlers/logging.go (about)

     1  // Copyright 2013 The Gorilla Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package handlers
     6  
     7  import (
     8  	"github.com/hellobchain/newcryptosm/http"
     9  	"io"
    10  	"net"
    11  	"net/url"
    12  	"strconv"
    13  	"time"
    14  	"unicode/utf8"
    15  )
    16  
    17  // Logging
    18  
    19  // FormatterParams is the structure any formatter will be handed when time to log comes
    20  type LogFormatterParams struct {
    21  	Request    *http.Request
    22  	URL        url.URL
    23  	TimeStamp  time.Time
    24  	StatusCode int
    25  	Size       int
    26  }
    27  
    28  // LogFormatter gives the signature of the formatter function passed to CustomLoggingHandler
    29  type LogFormatter func(writer io.Writer, params LogFormatterParams)
    30  
    31  // loggingHandler is the http.Handler implementation for LoggingHandlerTo and its
    32  // friends
    33  
    34  type loggingHandler struct {
    35  	writer    io.Writer
    36  	handler   http.Handler
    37  	formatter LogFormatter
    38  }
    39  
    40  func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    41  	t := time.Now()
    42  	logger := makeLogger(w)
    43  	url := *req.URL
    44  
    45  	h.handler.ServeHTTP(logger, req)
    46  
    47  	params := LogFormatterParams{
    48  		Request:    req,
    49  		URL:        url,
    50  		TimeStamp:  t,
    51  		StatusCode: logger.Status(),
    52  		Size:       logger.Size(),
    53  	}
    54  
    55  	h.formatter(h.writer, params)
    56  }
    57  
    58  func makeLogger(w http.ResponseWriter) loggingResponseWriter {
    59  	var logger loggingResponseWriter = &responseLogger{w: w, status: http.StatusOK}
    60  	if _, ok := w.(http.Hijacker); ok {
    61  		logger = &hijackLogger{responseLogger{w: w, status: http.StatusOK}}
    62  	}
    63  	h, ok1 := logger.(http.Hijacker)
    64  	c, ok2 := w.(http.CloseNotifier)
    65  	if ok1 && ok2 {
    66  		return hijackCloseNotifier{logger, h, c}
    67  	}
    68  	if ok2 {
    69  		return &closeNotifyWriter{logger, c}
    70  	}
    71  	return logger
    72  }
    73  
    74  type commonLoggingResponseWriter interface {
    75  	http.ResponseWriter
    76  	http.Flusher
    77  	Status() int
    78  	Size() int
    79  }
    80  
    81  const lowerhex = "0123456789abcdef"
    82  
    83  func appendQuoted(buf []byte, s string) []byte {
    84  	var runeTmp [utf8.UTFMax]byte
    85  	for width := 0; len(s) > 0; s = s[width:] {
    86  		r := rune(s[0])
    87  		width = 1
    88  		if r >= utf8.RuneSelf {
    89  			r, width = utf8.DecodeRuneInString(s)
    90  		}
    91  		if width == 1 && r == utf8.RuneError {
    92  			buf = append(buf, `\x`...)
    93  			buf = append(buf, lowerhex[s[0]>>4])
    94  			buf = append(buf, lowerhex[s[0]&0xF])
    95  			continue
    96  		}
    97  		if r == rune('"') || r == '\\' { // always backslashed
    98  			buf = append(buf, '\\')
    99  			buf = append(buf, byte(r))
   100  			continue
   101  		}
   102  		if strconv.IsPrint(r) {
   103  			n := utf8.EncodeRune(runeTmp[:], r)
   104  			buf = append(buf, runeTmp[:n]...)
   105  			continue
   106  		}
   107  		switch r {
   108  		case '\a':
   109  			buf = append(buf, `\a`...)
   110  		case '\b':
   111  			buf = append(buf, `\b`...)
   112  		case '\f':
   113  			buf = append(buf, `\f`...)
   114  		case '\n':
   115  			buf = append(buf, `\n`...)
   116  		case '\r':
   117  			buf = append(buf, `\r`...)
   118  		case '\t':
   119  			buf = append(buf, `\t`...)
   120  		case '\v':
   121  			buf = append(buf, `\v`...)
   122  		default:
   123  			switch {
   124  			case r < ' ':
   125  				buf = append(buf, `\x`...)
   126  				buf = append(buf, lowerhex[s[0]>>4])
   127  				buf = append(buf, lowerhex[s[0]&0xF])
   128  			case r > utf8.MaxRune:
   129  				r = 0xFFFD
   130  				fallthrough
   131  			case r < 0x10000:
   132  				buf = append(buf, `\u`...)
   133  				for s := 12; s >= 0; s -= 4 {
   134  					buf = append(buf, lowerhex[r>>uint(s)&0xF])
   135  				}
   136  			default:
   137  				buf = append(buf, `\U`...)
   138  				for s := 28; s >= 0; s -= 4 {
   139  					buf = append(buf, lowerhex[r>>uint(s)&0xF])
   140  				}
   141  			}
   142  		}
   143  	}
   144  	return buf
   145  
   146  }
   147  
   148  // buildCommonLogLine builds a log entry for req in Apache Common Log Format.
   149  // ts is the timestamp with which the entry should be logged.
   150  // status and size are used to provide the response HTTP status and size.
   151  func buildCommonLogLine(req *http.Request, url url.URL, ts time.Time, status int, size int) []byte {
   152  	username := "-"
   153  	if url.User != nil {
   154  		if name := url.User.Username(); name != "" {
   155  			username = name
   156  		}
   157  	}
   158  
   159  	host, _, err := net.SplitHostPort(req.RemoteAddr)
   160  
   161  	if err != nil {
   162  		host = req.RemoteAddr
   163  	}
   164  
   165  	uri := req.RequestURI
   166  
   167  	// Requests using the CONNECT method over HTTP/2.0 must use
   168  	// the authority field (aka r.Host) to identify the target.
   169  	// Refer: https://httpwg.github.io/specs/rfc7540.html#CONNECT
   170  	if req.ProtoMajor == 2 && req.Method == "CONNECT" {
   171  		uri = req.Host
   172  	}
   173  	if uri == "" {
   174  		uri = url.RequestURI()
   175  	}
   176  
   177  	buf := make([]byte, 0, 3*(len(host)+len(username)+len(req.Method)+len(uri)+len(req.Proto)+50)/2)
   178  	buf = append(buf, host...)
   179  	buf = append(buf, " - "...)
   180  	buf = append(buf, username...)
   181  	buf = append(buf, " ["...)
   182  	buf = append(buf, ts.Format("02/Jan/2006:15:04:05 -0700")...)
   183  	buf = append(buf, `] "`...)
   184  	buf = append(buf, req.Method...)
   185  	buf = append(buf, " "...)
   186  	buf = appendQuoted(buf, uri)
   187  	buf = append(buf, " "...)
   188  	buf = append(buf, req.Proto...)
   189  	buf = append(buf, `" `...)
   190  	buf = append(buf, strconv.Itoa(status)...)
   191  	buf = append(buf, " "...)
   192  	buf = append(buf, strconv.Itoa(size)...)
   193  	return buf
   194  }
   195  
   196  // writeLog writes a log entry for req to w in Apache Common Log Format.
   197  // ts is the timestamp with which the entry should be logged.
   198  // status and size are used to provide the response HTTP status and size.
   199  func writeLog(writer io.Writer, params LogFormatterParams) {
   200  	buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
   201  	buf = append(buf, '\n')
   202  	writer.Write(buf)
   203  }
   204  
   205  // writeCombinedLog writes a log entry for req to w in Apache Combined Log Format.
   206  // ts is the timestamp with which the entry should be logged.
   207  // status and size are used to provide the response HTTP status and size.
   208  func writeCombinedLog(writer io.Writer, params LogFormatterParams) {
   209  	buf := buildCommonLogLine(params.Request, params.URL, params.TimeStamp, params.StatusCode, params.Size)
   210  	buf = append(buf, ` "`...)
   211  	buf = appendQuoted(buf, params.Request.Referer())
   212  	buf = append(buf, `" "`...)
   213  	buf = appendQuoted(buf, params.Request.UserAgent())
   214  	buf = append(buf, '"', '\n')
   215  	writer.Write(buf)
   216  }
   217  
   218  // CombinedLoggingHandler return a http.Handler that wraps h and logs requests to out in
   219  // Apache Combined Log Format.
   220  //
   221  // See http://httpd.apache.org/docs/2.2/logs.html#combined for a description of this format.
   222  //
   223  // LoggingHandler always sets the ident field of the log to -
   224  func CombinedLoggingHandler(out io.Writer, h http.Handler) http.Handler {
   225  	return loggingHandler{out, h, writeCombinedLog}
   226  }
   227  
   228  // LoggingHandler return a http.Handler that wraps h and logs requests to out in
   229  // Apache Common Log Format (CLF).
   230  //
   231  // See http://httpd.apache.org/docs/2.2/logs.html#common for a description of this format.
   232  //
   233  // LoggingHandler always sets the ident field of the log to -
   234  //
   235  // Example:
   236  //
   237  //  r := mux.NewRouter()
   238  //  r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
   239  //  	w.Write([]byte("This is a catch-all route"))
   240  //  })
   241  //  loggedRouter := handlers.LoggingHandler(os.Stdout, r)
   242  //  http.ListenAndServe(":1123", loggedRouter)
   243  //
   244  func LoggingHandler(out io.Writer, h http.Handler) http.Handler {
   245  	return loggingHandler{out, h, writeLog}
   246  }
   247  
   248  // CustomLoggingHandler provides a way to supply a custom log formatter
   249  // while taking advantage of the mechanisms in this package
   250  func CustomLoggingHandler(out io.Writer, h http.Handler, f LogFormatter) http.Handler {
   251  	return loggingHandler{out, h, f}
   252  }