Repository navigation
Expand file tree
/
Copy pathloglayer.go
More file actions
127 lines (111 loc) · 3.81 KB
/
Copy pathloglayer.go
File metadata and controls
127 lines (111 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package httpserver
import (
"bytes"
"io"
"net/http"
"net/http/httputil"
"time"
"github.com/projectdiscovery/gologger"
)
// Convenience globals
var (
EnableUpload bool
EnableVerbose bool
EnableLogUA bool
)
// defaultMaxDumpBodySize bounds the request body kept for logging when
// -max-dump-body-size is not set.
const defaultMaxDumpBodySize = 1024 * 1024
func (t *HTTPServer) maxDumpBodySize() int64 {
if t.options.MaxDumpBodySize > 0 {
return t.options.MaxDumpBodySize
}
return defaultMaxDumpBodySize
}
// dumpRequest dumps r with at most maxDumpBodySize bytes of its body. The
// loglayer runs before authentication, so the size kept must not depend on
// Content-Length (absent for chunked bodies). The bytes read are put back in
// front of the body, so the next handlers still read all of it.
func (t *HTTPServer) dumpRequest(r *http.Request) []byte {
dump, _ := httputil.DumpRequest(r, false)
if r.Body == nil || r.Body == http.NoBody {
return dump
}
prefix, _ := io.ReadAll(io.LimitReader(r.Body, t.maxDumpBodySize()))
r.Body = struct {
io.Reader
io.Closer
}{io.MultiReader(bytes.NewReader(prefix), r.Body), r.Body}
return append(dump, prefix...)
}
func (t *HTTPServer) loglayer(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// the body is read only when a log needs it
var fullRequest []byte
if EnableVerbose || t.jsonLogger != nil {
fullRequest = t.dumpRequest(r)
}
lrw := newLoggingResponseWriter(w, t.options.MaxDumpBodySize)
handler.ServeHTTP(lrw, r)
// Log to JSON file if JSON logger is enabled
if t.jsonLogger != nil {
// Extract headers
headers := make(map[string]string)
for name, values := range r.Header {
if len(values) > 0 {
headers[name] = values[0]
}
}
// Extract request body from fullRequest
requestBody := ""
if len(fullRequest) > 0 {
// Find the double CRLF that separates headers from body
bodyStart := bytes.Index(fullRequest, []byte("\r\n\r\n"))
if bodyStart != -1 && bodyStart+4 < len(fullRequest) {
requestBody = string(fullRequest[bodyStart+4:])
}
}
// Log to JSON file
_ = t.jsonLogger.LogRequest(r, lrw.statusCode, lrw.Size, r.UserAgent(), headers, requestBody, string(lrw.Data))
}
// Continue with existing console logging
if EnableVerbose {
headers := new(bytes.Buffer)
lrw.Header().Write(headers) //nolint
gologger.Print().Msgf("\n[%s]\nRemote Address: %s\n%s\n%s %d %s\n%s\n%s\n", time.Now().Format("2006-01-02 15:04:05"), r.RemoteAddr, string(fullRequest), r.Proto, lrw.statusCode, http.StatusText(lrw.statusCode), headers.String(), string(lrw.Data))
} else {
if EnableLogUA {
gologger.Print().Msgf("[%s] %s \"%s %s %s\" %d %d - %s", time.Now().Format("2006-01-02 15:04:05"), r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, lrw.Size, r.UserAgent())
} else {
gologger.Print().Msgf("[%s] %s \"%s %s %s\" %d %d", time.Now().Format("2006-01-02 15:04:05"), r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, lrw.Size)
}
}
})
}
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
Data []byte
Size int
MaxDumpSize int64
}
func newLoggingResponseWriter(w http.ResponseWriter, maxSize int64) *loggingResponseWriter {
return &loggingResponseWriter{w, http.StatusOK, []byte{}, 0, maxSize}
}
// Write the data
func (lrw *loggingResponseWriter) Write(data []byte) (int, error) {
if len(lrw.Data) < int(lrw.MaxDumpSize) {
lrw.Data = append(lrw.Data, data...)
}
lrw.Size += len(data)
return lrw.ResponseWriter.Write(data)
}
// Header of the response
func (lrw *loggingResponseWriter) Header() http.Header {
return lrw.ResponseWriter.Header()
}
// WriteHeader status code
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}