diff --git a/README.md b/README.md index afdb25f..7225b92 100644 --- a/README.md +++ b/README.md @@ -13,5 +13,7 @@ - [x] 忽略某些错误例如:broken pipe, reset by peer, timeout - [x] websocket - [x] 插件机制 +- [x] 命令行参数控制 dump 至文件 +- [ ] dump level - [ ] support get method with body - [ ] http2 diff --git a/addon/dumper.go b/addon/dumper.go new file mode 100644 index 0000000..06d9daa --- /dev/null +++ b/addon/dumper.go @@ -0,0 +1,65 @@ +package addon + +import ( + "bytes" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/lqqyt2423/go-mitmproxy/flow" +) + +type Dumper struct { + Base + Out io.Writer +} + +func NewDumperWithFile(file string) *Dumper { + out, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) + if err != nil { + panic(err) + } + return &Dumper{Out: out} +} + +func (d *Dumper) Requestheaders(f *flow.Flow) { + log := log.WithField("in", "Dumper") + + go func() { + <-f.Done() + + // 参考 httputil.DumpRequest + + buf := bytes.NewBuffer(make([]byte, 0)) + fmt.Fprintf(buf, "%s %s %s\r\n", f.Request.Method, f.Request.URL.RequestURI(), f.Request.Proto) + fmt.Fprintf(buf, "Host: %s\r\n", f.Request.URL.Host) + if len(f.Request.Raw().TransferEncoding) > 0 { + fmt.Fprintf(buf, "Transfer-Encoding: %s\r\n", strings.Join(f.Request.Raw().TransferEncoding, ",")) + } + if f.Request.Raw().Close { + fmt.Fprintf(buf, "Connection: close\r\n") + } + + err := f.Request.Header.WriteSubset(buf, nil) + if err != nil { + log.Error(err) + } + buf.WriteString("\r\n") + + if f.Response != nil { + fmt.Fprintf(buf, "%v %v %v\r\n", f.Request.Proto, f.Response.StatusCode, http.StatusText(f.Response.StatusCode)) + err = f.Response.Header.WriteSubset(buf, nil) + if err != nil { + log.Error(err) + } + buf.WriteString("\r\n") + } + + _, err = d.Out.Write(buf.Bytes()) + if err != nil { + log.Error(err) + } + }() +} diff --git a/cmd/mitmproxy/main.go b/cmd/mitmproxy/main.go index f2f96ea..da31d6d 100644 --- a/cmd/mitmproxy/main.go +++ b/cmd/mitmproxy/main.go @@ -1,12 +1,29 @@ package main import ( + "flag" "os" + "github.com/lqqyt2423/go-mitmproxy/addon" "github.com/lqqyt2423/go-mitmproxy/proxy" log "github.com/sirupsen/logrus" ) +type Config struct { + addr string + dump string // dump filename +} + +func loadConfig() *Config { + config := new(Config) + + flag.StringVar(&config.addr, "addr", ":9080", "proxy listen addr") + flag.StringVar(&config.dump, "dump", "", "dump filename") + flag.Parse() + + return config +} + func main() { log.SetLevel(log.InfoLevel) log.SetReportCaller(false) @@ -15,8 +32,10 @@ func main() { FullTimestamp: true, }) + config := loadConfig() + opts := &proxy.Options{ - Addr: ":9080", + Addr: config.addr, StreamLargeBodies: 1024 * 1024 * 5, } @@ -25,5 +44,10 @@ func main() { log.Fatal(err) } + if config.dump != "" { + dumper := addon.NewDumperWithFile(config.dump) + p.AddAddon(dumper) + } + log.Fatal(p.Start()) } diff --git a/flow/flow.go b/flow/flow.go index 2a099a1..a63521a 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -3,18 +3,30 @@ package flow import ( "net/http" "net/url" - - _log "github.com/sirupsen/logrus" ) -var log = _log.WithField("at", "flow") - type Request struct { Method string URL *url.URL Proto string Header http.Header Body []byte + + raw *http.Request +} + +func NewRequest(req *http.Request) *Request { + return &Request{ + Method: req.Method, + URL: req.URL, + Proto: req.Proto, + Header: req.Header, + raw: req, + } +} + +func (r *Request) Raw() *http.Request { + return r.raw } type Response struct { diff --git a/proxy/proxy.go b/proxy/proxy.go index 7dc43b9..336968b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -155,12 +155,7 @@ func (proxy *Proxy) ServeHTTP(res http.ResponseWriter, req *http.Request) { }() f := flow.NewFlow() - f.Request = &flow.Request{ - Method: req.Method, - URL: req.URL, - Proto: req.Proto, - Header: req.Header, - } + f.Request = flow.NewRequest(req) defer f.Finish() // trigger addon event Requestheaders