|
|
@ -10,7 +10,6 @@ import (
|
|
10
|
10
|
"io/ioutil"
|
|
11
|
11
|
"log"
|
|
12
|
12
|
"net/http"
|
|
13
|
|
"net/http/httputil"
|
|
14
|
13
|
"net/url"
|
|
15
|
14
|
"os"
|
|
16
|
15
|
"os/exec"
|
|
|
@ -58,7 +57,7 @@ func main() {
|
|
58
|
57
|
responsesPath = flag.Arg(0)
|
|
59
|
58
|
}
|
|
60
|
59
|
|
|
61
|
|
requestLog := make([]LogEntry, 0)
|
|
|
60
|
requestLog := Log(make([]LogEntry, 0))
|
|
62
|
61
|
|
|
63
|
62
|
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
|
64
|
63
|
responses.Load(responsesPath)
|
|
|
@ -73,12 +72,6 @@ func main() {
|
|
73
|
72
|
userAgent := req.Header.Get("User-Agent")
|
|
74
|
73
|
log.Printf("%s %s - %d (%s, %q)", req.Method, req.URL, resp.StatusCode, req.RemoteAddr, userAgent)
|
|
75
|
74
|
|
|
76
|
|
out, err := httputil.DumpRequest(req, true)
|
|
77
|
|
if err != nil {
|
|
78
|
|
log.Printf("Error: Dumping request: %s", err)
|
|
79
|
|
return
|
|
80
|
|
}
|
|
81
|
|
|
|
82
|
75
|
if resp.Header.Get("Content-Type") == "application/json" {
|
|
83
|
76
|
pretty, err := prettyfyJSON(resp.Body)
|
|
84
|
77
|
if err != nil {
|
|
|
@ -89,17 +82,14 @@ func main() {
|
|
89
|
82
|
}
|
|
90
|
83
|
}
|
|
91
|
84
|
|
|
92
|
|
requestLog = append(requestLog, LogEntry{
|
|
93
|
|
Request: out,
|
|
94
|
|
Response: asResponse(req, resp),
|
|
95
|
|
})
|
|
|
85
|
requestLog.Log(req, resp)
|
|
96
|
86
|
})
|
|
97
|
87
|
|
|
98
|
88
|
http.HandleFunc("/_log", func(w http.ResponseWriter, req *http.Request) {
|
|
99
|
89
|
if strings.Contains(req.Header.Get("Accept"), "application/yaml") {
|
|
100
|
90
|
rs := make([]Response, len(requestLog))
|
|
101
|
91
|
for i, log := range requestLog {
|
|
102
|
|
rs[i] = log.Response
|
|
|
92
|
rs[i] = log.AsResponse()
|
|
103
|
93
|
}
|
|
104
|
94
|
err := renderYAML(w, rs)
|
|
105
|
95
|
if err != nil {
|
|
|
@ -110,9 +100,9 @@ func main() {
|
|
110
|
100
|
|
|
111
|
101
|
for i := len(requestLog) - 1; i >= 0; i-- {
|
|
112
|
102
|
w.Write([]byte("------------------------------------------------------\n"))
|
|
113
|
|
w.Write(requestLog[i].Request)
|
|
114
|
|
w.Write([]byte("\n\n"))
|
|
115
|
|
requestLog[i].Response.AsHTTP().Write(w)
|
|
|
103
|
requestLog[i].Request().Write(w)
|
|
|
104
|
w.Write([]byte("\n"))
|
|
|
105
|
requestLog[i].Response().Write(w)
|
|
116
|
106
|
w.Write([]byte("\n"))
|
|
117
|
107
|
}
|
|
118
|
108
|
})
|
|
|
@ -253,10 +243,51 @@ func renderYAML(w http.ResponseWriter, responses []Response) error {
|
|
253
|
243
|
return nil
|
|
254
|
244
|
}
|
|
255
|
245
|
|
|
256
|
|
// LogEntry is a request/respond pair for logging.
|
|
|
246
|
// Log collects HTTP requests/responses for later display and
|
|
|
247
|
// processing.
|
|
|
248
|
type Log []LogEntry
|
|
|
249
|
|
|
|
250
|
// Log logs the request/response pair.
|
|
|
251
|
func (l *Log) Log(req *http.Request, resp *http.Response) {
|
|
|
252
|
e := LogEntry{
|
|
|
253
|
request: req,
|
|
|
254
|
requestBody: new(bytes.Buffer),
|
|
|
255
|
response: resp,
|
|
|
256
|
responseBody: new(bytes.Buffer),
|
|
|
257
|
}
|
|
|
258
|
io.Copy(e.requestBody, req.Body)
|
|
|
259
|
io.Copy(e.responseBody, resp.Body)
|
|
|
260
|
*l = append(*l, e)
|
|
|
261
|
}
|
|
|
262
|
|
|
|
263
|
// LogEntry is a request/response pair for logging.
|
|
257
|
264
|
type LogEntry struct {
|
|
258
|
|
Request Request
|
|
259
|
|
Response Response
|
|
|
265
|
request *http.Request
|
|
|
266
|
requestBody *bytes.Buffer
|
|
|
267
|
response *http.Response
|
|
|
268
|
responseBody *bytes.Buffer
|
|
|
269
|
}
|
|
|
270
|
|
|
|
271
|
// AsResponse returns a Response representation of the entry.
|
|
|
272
|
func (e LogEntry) AsResponse() Response {
|
|
|
273
|
return Response{
|
|
|
274
|
Method: e.request.Method,
|
|
|
275
|
Path: e.request.URL.Path,
|
|
|
276
|
Status: e.response.StatusCode,
|
|
|
277
|
Body: e.responseBody.String(),
|
|
|
278
|
}
|
|
|
279
|
}
|
|
|
280
|
|
|
|
281
|
// Request returns the stored http.Request.
|
|
|
282
|
func (e LogEntry) Request() *http.Request {
|
|
|
283
|
e.request.Body = ioutil.NopCloser(bytes.NewReader(e.requestBody.Bytes()))
|
|
|
284
|
return e.request
|
|
|
285
|
}
|
|
|
286
|
|
|
|
287
|
// Response returns the stored http.Response.
|
|
|
288
|
func (e LogEntry) Response() *http.Response {
|
|
|
289
|
e.response.Body = ioutil.NopCloser(bytes.NewReader(e.responseBody.Bytes()))
|
|
|
290
|
return e.response
|
|
260
|
291
|
}
|
|
261
|
292
|
|
|
262
|
293
|
// Request is a stored serialized HTTP request.
|