github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zhttp/debug.go (about) 1 package zhttp 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "net/http/httputil" 12 "net/url" 13 "strings" 14 "time" 15 16 "github.com/sohaha/zlsgo/zstring" 17 "github.com/sohaha/zlsgo/zutil" 18 ) 19 20 const ( 21 rn = "\r\n\r\n" 22 ) 23 24 var ( 25 Debug = zutil.NewBool(false) 26 ) 27 28 type dumpConn struct { 29 io.Writer 30 io.Reader 31 } 32 33 func (c *dumpConn) Close() error { return nil } 34 func (c *dumpConn) LocalAddr() net.Addr { return nil } 35 func (c *dumpConn) RemoteAddr() net.Addr { return nil } 36 func (c *dumpConn) SetDeadline(t time.Time) error { return nil } 37 func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil } 38 func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil } 39 40 type ( 41 dummyBody struct { 42 N int 43 off int 44 } 45 46 delegateReader struct { 47 c chan io.Reader 48 r io.Reader 49 } 50 ) 51 52 func (r *delegateReader) Read(p []byte) (int, error) { 53 if r.r == nil { 54 r.r = <-r.c 55 } 56 return r.r.Read(p) 57 } 58 59 func (d *dummyBody) Read(p []byte) (n int, err error) { 60 if d.N <= 0 { 61 err = io.EOF 62 return 63 } 64 left := d.N - d.off 65 if left <= 0 { 66 err = io.EOF 67 return 68 } 69 70 if l := len(p); l > 0 { 71 if l >= left { 72 n = left 73 err = io.EOF 74 } else { 75 n = l 76 } 77 d.off += n 78 for i := 0; i < n; i++ { 79 p[i] = '*' 80 } 81 } 82 83 return 84 } 85 86 func (d *dummyBody) Close() error { 87 return nil 88 } 89 90 type dumpBuffer struct { 91 bytes.Buffer 92 } 93 94 func (b *dumpBuffer) Write(p []byte) { 95 if b.Len() > 0 { 96 b.Buffer.WriteString(rn) 97 } 98 b.Buffer.Write(p) 99 } 100 101 func (b *dumpBuffer) WriteString(s string) { 102 b.Write([]byte(s)) 103 } 104 105 func (r *Res) dumpRequest(dump *dumpBuffer) { 106 head := r.r.flag&BitReqHead != 0 107 body := r.r.flag&BitReqBody != 0 108 109 if head { 110 r.dumpReqHead(dump) 111 } 112 if body { 113 if r.multipartHelper != nil { 114 dump.Write(r.multipartHelper.Dump()) 115 } else if len(r.requesterBody) > 0 { 116 dump.Write(r.requesterBody) 117 } 118 } 119 } 120 121 func (r *Res) dumpReqHead(dump *dumpBuffer) { 122 reqSend := new(http.Request) 123 *reqSend = *r.req 124 if reqSend.URL.Scheme == "https" { 125 reqSend.URL = new(url.URL) 126 *reqSend.URL = *r.req.URL 127 reqSend.URL.Scheme = "http" 128 } 129 130 if reqSend.ContentLength > 0 { 131 reqSend.Body = &dummyBody{N: int(reqSend.ContentLength)} 132 } else { 133 reqSend.Body = &dummyBody{N: 1} 134 } 135 136 var buf bytes.Buffer 137 pr, pw := io.Pipe() 138 defer pw.Close() 139 dr := &delegateReader{c: make(chan io.Reader)} 140 141 t := &http.Transport{ 142 Dial: func(_, _ string) (net.Conn, error) { 143 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil 144 }, 145 } 146 defer t.CloseIdleConnections() 147 148 client := new(http.Client) 149 *client = *r.client 150 client.Transport = t 151 152 go func() { 153 req, err := http.ReadRequest(bufio.NewReader(pr)) 154 if err == nil { 155 _, _ = io.Copy(ioutil.Discard, req.Body) 156 _ = req.Body.Close() 157 } 158 159 dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n") 160 _ = pr.Close() 161 }() 162 163 _, err := client.Do(reqSend) 164 if err != nil { 165 dump.WriteString(err.Error()) 166 } else { 167 reqDump := buf.Bytes() 168 if i := bytes.Index(reqDump, []byte(rn)); i >= 0 { 169 reqDump = reqDump[:i] 170 } 171 dump.Write(reqDump) 172 } 173 } 174 175 func (r *Res) dumpResonse(dump *dumpBuffer) { 176 head := r.r.flag&BitRespHead != 0 177 body := r.r.flag&BitRespBody != 0 178 if head && r.resp != nil { 179 responseBodyDump, err := httputil.DumpResponse(r.resp, false) 180 if err != nil { 181 dump.WriteString(err.Error()) 182 } else { 183 if i := bytes.Index(responseBodyDump, []byte(rn)); i >= 0 { 184 responseBodyDump = responseBodyDump[:i] 185 } 186 dump.Write(responseBodyDump) 187 } 188 } 189 if body && len(r.Bytes()) > 0 { 190 dump.Write(r.Bytes()) 191 } 192 } 193 194 func (r *Res) Cost() time.Duration { 195 return r.cost 196 } 197 198 func (r *Res) Dump() string { 199 dump := new(dumpBuffer) 200 if r.r.flag&BitTime != 0 { 201 dump.WriteString(fmt.Sprint(r.cost)) 202 } 203 r.dumpRequest(dump) 204 l := dump.Len() 205 if l > 0 { 206 dump.WriteString(zstring.Pad("", 30, "=", zstring.PadRight)) 207 } 208 209 r.dumpResonse(dump) 210 211 return dump.String() 212 }