diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 98ebf8de..3a379c75 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -194,6 +194,7 @@ import ( "crypto/tls" "crypto/x509" "flag" + "fmt" "log" "net" "net/http" @@ -247,6 +248,7 @@ var ( validity = flag.Duration("validity", time.Hour, "window of time that MITM certificates are valid") allowCORS = flag.Bool("cors", false, "allow CORS requests to configure the proxy") harLogging = flag.Bool("har", false, "enable HAR logging API") + harFile = flag.String("harFile", "./martian.har", "har file path") marblLogging = flag.Bool("marbl", false, "enable MARBL logging API") trafficShaping = flag.Bool("traffic-shaping", false, "enable traffic shaping API") skipTLSVerify = flag.Bool("skip-tls-verify", false, "skip TLS server verification; insecure") @@ -389,6 +391,7 @@ func main() { configure("/logs", har.NewExportHandler(hl), mux) configure("/logs/reset", har.NewResetHandler(hl), mux) + configure("/logs/dump", har.NewDumpHandler(hl, *harFile), mux) } logger := martianlog.NewLogger() @@ -442,6 +445,18 @@ func main() { <-sigc + if *harLogging { + log.Println("martian: dumping har file") + u := url.URL{} + proxy, _ := u.Parse(fmt.Sprintf("http://%v", *addr)) + transport := http.Transport{} + transport.Proxy = http.ProxyURL(proxy) // set proxy + + client := &http.Client{} + client.Transport = &transport + client.Get("http://martian.proxy/logs/dump") + } + log.Println("martian: shutting down") os.Exit(0) } diff --git a/har/har_handlers.go b/har/har_handlers.go index aca49914..342b0e1b 100644 --- a/har/har_handlers.go +++ b/har/har_handlers.go @@ -18,6 +18,8 @@ import ( "encoding/json" "net/http" "net/url" + "os" + "path/filepath" "strconv" "github.com/google/martian/v3/log" @@ -31,6 +33,11 @@ type resetHandler struct { logger *Logger } +type dumpHandler struct { + logger *Logger + filePath string +} + // NewExportHandler returns an http.Handler for requesting HAR logs. func NewExportHandler(l *Logger) http.Handler { return &exportHandler{ @@ -45,6 +52,43 @@ func NewResetHandler(l *Logger) http.Handler { } } +// NewDumpHandler returns an http.Handler for dumping log entries to file. +func NewDumpHandler(l *Logger, path string) http.Handler { + return &dumpHandler{ + logger: l, + filePath: path, + } +} + +// ServeHTTP writes the log in HAR format to a file. +func (h *dumpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if req.Method != "GET" { + rw.Header().Add("Allow", "GET") + rw.WriteHeader(http.StatusMethodNotAllowed) + log.Errorf("har.ServeHTTP: method not allowed: %s", req.Method) + return + } + log.Debugf("exportHandler.ServeHTTP: writing HAR logs to ResponseWriter") + rw.Header().Set("Content-Type", "application/json; charset=utf-8") + + dir := filepath.Dir(h.filePath) + err := os.MkdirAll(dir, 0764) + if err != nil { + log.Errorf("mkdir", err) + } + + file, err := os.Create(h.filePath) + if err != nil { + log.Errorf("create file", err) + return + } + defer file.Close() + + encoder := json.NewEncoder(file) + encoder.SetIndent("", "\t") + encoder.Encode(h.logger.Export()) +} + // ServeHTTP writes the log in HAR format to the response body. func (h *exportHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req.Method != "GET" { @@ -70,7 +114,6 @@ func (h *resetHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { return } - v, err := parseBoolQueryParam(req.URL.Query(), "return") if err != nil { log.Errorf("har: invalid value for return param: %s", err)