11package main
22
33import (
4+ "bytes"
5+ "compress/flate"
6+ "compress/gzip"
7+ "compress/zlib"
48 "context"
59 "crypto/tls"
6- "encoding/hex"
710 "flag"
811 "fmt"
912 "io"
1013 "log/slog"
1114 "net"
1215 "net/http"
13- "net/http/httputil"
1416 "os"
1517 "os/signal"
1618 "strings"
@@ -22,15 +24,68 @@ import (
2224 "github.com/josexy/mitmpgo/metadata"
2325)
2426
27+ const CHUNK_SIZE = 512
28+
29+ const (
30+ CHUNK_TYPE_REQ = 1 << iota
31+ CHUNK_TYPE_RSP
32+ )
33+
34+ type bodyDecoder struct {
35+ reader io.ReadCloser
36+ pw * io.PipeWriter
37+ }
38+
39+ func newBodyDecoder (r io.ReadCloser , encoding string , chunkType int ) (io.ReadCloser , error ) {
40+ if encoding == "" {
41+ return newChunkBodyReader (r , CHUNK_SIZE , chunkType ), nil
42+ }
43+
44+ pr , pw := io .Pipe ()
45+ teeReader := io .TeeReader (r , pw )
46+ go func () {
47+ decodedReader , err := getDecodedReader (pr , encoding )
48+ if err != nil {
49+ // if the decoder creation fails, we need to read pr to avoid pw blocking
50+ io .Copy (io .Discard , pr )
51+ return
52+ }
53+ decodedReader = newChunkBodyReader (decodedReader , CHUNK_SIZE , chunkType )
54+ defer decodedReader .Close ()
55+ // need to read all data to avoid pw blocking, but we don't care about the decoded data here, so just discard it
56+ io .Copy (io .Discard , decodedReader )
57+ }()
58+ return & bodyDecoder {
59+ reader : io .NopCloser (teeReader ),
60+ pw : pw ,
61+ }, nil
62+ }
63+
64+ func (b * bodyDecoder ) Close () error {
65+ return b .reader .Close ()
66+ }
67+
68+ func (b * bodyDecoder ) Read (p []byte ) (n int , err error ) {
69+ n , err = b .reader .Read (p )
70+ if err == io .EOF {
71+ // when reader is closed or reaches EOF, we should also close the pipe writer to avoid goroutine leak
72+ b .pw .CloseWithError (err )
73+ }
74+ return
75+ }
76+
2577type chunkBodyReader struct {
2678 io.ReadCloser
27- N int64
79+ N int64
80+ buf bytes.Buffer // or use buf pool?
81+ chunkType int
2882}
2983
30- func newChunkBodyReader (r io.ReadCloser , chunkBodySize int64 ) * chunkBodyReader {
84+ func newChunkBodyReader (r io.ReadCloser , chunkBodySize int64 , chunkType int ) io. ReadCloser {
3185 return & chunkBodyReader {
3286 N : chunkBodySize ,
3387 ReadCloser : r ,
88+ chunkType : chunkType ,
3489 }
3590}
3691
@@ -43,7 +98,11 @@ func (r *chunkBodyReader) Read(p []byte) (n int, err error) {
4398 }
4499 n , err = r .ReadCloser .Read (p )
45100 if n > 0 {
46- fmt .Printf ("--> hex dump(chunk size/data size: %d/%d):\n %s\n " , r .N , n , hex .Dump (p [:n ]))
101+ r .buf .Write (p [:n ])
102+ // fmt.Printf("--> hex dump(chunk size/data size: %d/%d):\n%s\n", r.N, n, hex.Dump(p[:n]))
103+ }
104+ if err == io .EOF {
105+ fmt .Printf ("<<-- [%d]full data dump (%d bytes):\n %s\n " , r .chunkType , r .buf .Len (), r .buf .Bytes ())
47106 }
48107 return
49108}
@@ -178,12 +237,7 @@ func httpInterceptor(ctx context.Context, req *http.Request, invoker mitmpgo.HTT
178237 )
179238 }
180239
181- if md .StreamBody {
182- req .Body = newChunkBodyReader (req .Body , 512 )
183- } else {
184- data , _ := httputil .DumpRequest (req , true )
185- fmt .Println ("request:" , string (data ))
186- }
240+ req .Body , _ = newBodyDecoder (req .Body , "" , CHUNK_TYPE_REQ )
187241
188242 rsp , err := invoker .Invoke (req )
189243 if err != nil {
@@ -198,12 +252,7 @@ func httpInterceptor(ctx context.Context, req *http.Request, invoker mitmpgo.HTT
198252 slog .String ("protocol" , rsp .Proto ),
199253 )
200254
201- if md .StreamBody {
202- rsp .Body = newChunkBodyReader (rsp .Body , 512 )
203- } else {
204- data , _ := httputil .DumpResponse (rsp , true )
205- fmt .Println ("response:" , string (data ))
206- }
255+ rsp .Body , err = newBodyDecoder (rsp .Body , rsp .Header .Get ("Content-Encoding" ), CHUNK_TYPE_RSP )
207256
208257 return rsp , err
209258}
@@ -248,3 +297,18 @@ func websocketInterceptor(ctx context.Context, dir mitmpgo.WSDirection, msgType
248297 // }
249298 return wdi .Invoke (msgType , b )
250299}
300+
301+ func getDecodedReader (r io.Reader , encoding string ) (io.ReadCloser , error ) {
302+ switch encoding {
303+ case "gzip" :
304+ return gzip .NewReader (r )
305+ case "deflate" :
306+ zr , err := zlib .NewReader (r )
307+ if err != nil {
308+ return io .NopCloser (flate .NewReader (r )), nil
309+ }
310+ return zr , nil
311+ default : // other encodings...
312+ return io .NopCloser (r ), nil
313+ }
314+ }
0 commit comments