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  }