github.com/safing/portbase@v0.19.5/api/enriched-response.go (about)

     1  package api
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"net"
     7  	"net/http"
     8  
     9  	"github.com/safing/portbase/log"
    10  )
    11  
    12  // LoggingResponseWriter is a wrapper for http.ResponseWriter for better request logging.
    13  type LoggingResponseWriter struct {
    14  	ResponseWriter http.ResponseWriter
    15  	Request        *http.Request
    16  	Status         int
    17  }
    18  
    19  // NewLoggingResponseWriter wraps a http.ResponseWriter.
    20  func NewLoggingResponseWriter(w http.ResponseWriter, r *http.Request) *LoggingResponseWriter {
    21  	return &LoggingResponseWriter{
    22  		ResponseWriter: w,
    23  		Request:        r,
    24  	}
    25  }
    26  
    27  // Header wraps the original Header method.
    28  func (lrw *LoggingResponseWriter) Header() http.Header {
    29  	return lrw.ResponseWriter.Header()
    30  }
    31  
    32  // Write wraps the original Write method.
    33  func (lrw *LoggingResponseWriter) Write(b []byte) (int, error) {
    34  	return lrw.ResponseWriter.Write(b)
    35  }
    36  
    37  // WriteHeader wraps the original WriteHeader method to extract information.
    38  func (lrw *LoggingResponseWriter) WriteHeader(code int) {
    39  	lrw.Status = code
    40  	lrw.ResponseWriter.WriteHeader(code)
    41  }
    42  
    43  // Hijack wraps the original Hijack method, if available.
    44  func (lrw *LoggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    45  	hijacker, ok := lrw.ResponseWriter.(http.Hijacker)
    46  	if ok {
    47  		c, b, err := hijacker.Hijack()
    48  		if err != nil {
    49  			return nil, nil, err
    50  		}
    51  		log.Tracer(lrw.Request.Context()).Infof("api request: %s HIJ %s", lrw.Request.RemoteAddr, lrw.Request.RequestURI)
    52  		return c, b, nil
    53  	}
    54  	return nil, nil, errors.New("response does not implement http.Hijacker")
    55  }
    56  
    57  // RequestLogger is a logging middleware.
    58  func RequestLogger(next http.Handler) http.Handler {
    59  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    60  		log.Tracer(r.Context()).Tracef("api request: %s ___ %s", r.RemoteAddr, r.RequestURI)
    61  		lrw := NewLoggingResponseWriter(w, r)
    62  		next.ServeHTTP(lrw, r)
    63  		if lrw.Status != 0 {
    64  			// request may have been hijacked
    65  			log.Tracer(r.Context()).Infof("api request: %s %d %s", lrw.Request.RemoteAddr, lrw.Status, lrw.Request.RequestURI)
    66  		}
    67  	})
    68  }