github.com/ck00004/CobaltStrikeParser-Go@v1.0.14/lib/http/httputil/dump.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package httputil 6 7 import ( 8 "bufio" 9 "bytes" 10 "errors" 11 "fmt" 12 "io" 13 "net" 14 "strings" 15 "time" 16 17 "github.com/ck00004/CobaltStrikeParser-Go/lib/url" 18 19 "github.com/ck00004/CobaltStrikeParser-Go/lib/http" 20 ) 21 22 // drainBody reads all of b to memory and then returns two equivalent 23 // ReadClosers yielding the same bytes. 24 // 25 // It returns an error if the initial slurp of all bytes fails. It does not attempt 26 // to make the returned ReadClosers have identical error-matching behavior. 27 func drainBody(b io.ReadCloser) (r1, r2 io.ReadCloser, err error) { 28 if b == nil || b == http.NoBody { 29 // No copying needed. Preserve the magic sentinel meaning of NoBody. 30 return http.NoBody, http.NoBody, nil 31 } 32 var buf bytes.Buffer 33 if _, err = buf.ReadFrom(b); err != nil { 34 return nil, b, err 35 } 36 if err = b.Close(); err != nil { 37 return nil, b, err 38 } 39 return io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil 40 } 41 42 // dumpConn is a net.Conn which writes to Writer and reads from Reader 43 type dumpConn struct { 44 io.Writer 45 io.Reader 46 } 47 48 func (c *dumpConn) Close() error { return nil } 49 func (c *dumpConn) LocalAddr() net.Addr { return nil } 50 func (c *dumpConn) RemoteAddr() net.Addr { return nil } 51 func (c *dumpConn) SetDeadline(t time.Time) error { return nil } 52 func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil } 53 func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil } 54 55 type neverEnding byte 56 57 func (b neverEnding) Read(p []byte) (n int, err error) { 58 for i := range p { 59 p[i] = byte(b) 60 } 61 return len(p), nil 62 } 63 64 // outGoingLength is a copy of the unexported 65 // (*http.Request).outgoingLength method. 66 func outgoingLength(req *http.Request) int64 { 67 if req.Body == nil || req.Body == http.NoBody { 68 return 0 69 } 70 if req.ContentLength != 0 { 71 return req.ContentLength 72 } 73 return -1 74 } 75 76 // DumpRequestOut is like DumpRequest but for outgoing client requests. It 77 // includes any headers that the standard http.Transport adds, such as 78 // User-Agent. 79 func DumpRequestOut(req *http.Request, body bool) ([]byte, error) { 80 save := req.Body 81 dummyBody := false 82 if !body { 83 contentLength := outgoingLength(req) 84 if contentLength != 0 { 85 req.Body = io.NopCloser(io.LimitReader(neverEnding('x'), contentLength)) 86 dummyBody = true 87 } 88 } else { 89 var err error 90 save, req.Body, err = drainBody(req.Body) 91 if err != nil { 92 return nil, err 93 } 94 } 95 96 // Since we're using the actual Transport code to write the request, 97 // switch to http so the Transport doesn't try to do an SSL 98 // negotiation with our dumpConn and its bytes.Buffer & pipe. 99 // The wire format for https and http are the same, anyway. 100 reqSend := req 101 if req.URL.Scheme == "https" { 102 reqSend = new(http.Request) 103 *reqSend = *req 104 reqSend.URL = new(url.URL) 105 *reqSend.URL = *req.URL 106 reqSend.URL.Scheme = "http" 107 } 108 109 // Use the actual Transport code to record what we would send 110 // on the wire, but not using TCP. Use a Transport with a 111 // custom dialer that returns a fake net.Conn that waits 112 // for the full input (and recording it), and then responds 113 // with a dummy response. 114 var buf bytes.Buffer // records the output 115 pr, pw := io.Pipe() 116 defer pr.Close() 117 defer pw.Close() 118 dr := &delegateReader{c: make(chan io.Reader)} 119 120 t := &http.Transport{ 121 Dial: func(net, addr string) (net.Conn, error) { 122 return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil 123 }, 124 } 125 defer t.CloseIdleConnections() 126 127 // We need this channel to ensure that the reader 128 // goroutine exits if t.RoundTrip returns an error. 129 // See golang.org/issue/32571. 130 quitReadCh := make(chan struct{}) 131 // Wait for the request before replying with a dummy response: 132 go func() { 133 req, err := http.ReadRequest(bufio.NewReader(pr)) 134 if err == nil { 135 // Ensure all the body is read; otherwise 136 // we'll get a partial dump. 137 io.Copy(io.Discard, req.Body) 138 req.Body.Close() 139 } 140 select { 141 case dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"): 142 case <-quitReadCh: 143 // Ensure delegateReader.Read doesn't block forever if we get an error. 144 close(dr.c) 145 } 146 }() 147 148 _, err := t.RoundTrip(reqSend) 149 150 req.Body = save 151 if err != nil { 152 pw.Close() 153 dr.err = err 154 close(quitReadCh) 155 return nil, err 156 } 157 dump := buf.Bytes() 158 159 // If we used a dummy body above, remove it now. 160 // TODO: if the req.ContentLength is large, we allocate memory 161 // unnecessarily just to slice it off here. But this is just 162 // a debug function, so this is acceptable for now. We could 163 // discard the body earlier if this matters. 164 if dummyBody { 165 if i := bytes.Index(dump, []byte("\r\n\r\n")); i >= 0 { 166 dump = dump[:i+4] 167 } 168 } 169 return dump, nil 170 } 171 172 // delegateReader is a reader that delegates to another reader, 173 // once it arrives on a channel. 174 type delegateReader struct { 175 c chan io.Reader 176 err error // only used if r is nil and c is closed. 177 r io.Reader // nil until received from c 178 } 179 180 func (r *delegateReader) Read(p []byte) (int, error) { 181 if r.r == nil { 182 var ok bool 183 if r.r, ok = <-r.c; !ok { 184 return 0, r.err 185 } 186 } 187 return r.r.Read(p) 188 } 189 190 // Return value if nonempty, def otherwise. 191 func valueOrDefault(value, def string) string { 192 if value != "" { 193 return value 194 } 195 return def 196 } 197 198 var reqWriteExcludeHeaderDump = map[string]bool{ 199 "Host": true, // not in Header map anyway 200 "Transfer-Encoding": true, 201 "Trailer": true, 202 } 203 204 // DumpRequest returns the given request in its HTTP/1.x wire 205 // representation. It should only be used by servers to debug client 206 // requests. The returned representation is an approximation only; 207 // some details of the initial request are lost while parsing it into 208 // an http.Request. In particular, the order and case of header field 209 // names are lost. The order of values in multi-valued headers is kept 210 // intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their 211 // original binary representations. 212 // 213 // If body is true, DumpRequest also returns the body. To do so, it 214 // consumes req.Body and then replaces it with a new io.ReadCloser 215 // that yields the same bytes. If DumpRequest returns an error, 216 // the state of req is undefined. 217 // 218 // The documentation for http.Request.Write details which fields 219 // of req are included in the dump. 220 func DumpRequest(req *http.Request, body bool) ([]byte, error) { 221 var err error 222 save := req.Body 223 if !body || req.Body == nil { 224 req.Body = nil 225 } else { 226 save, req.Body, err = drainBody(req.Body) 227 if err != nil { 228 return nil, err 229 } 230 } 231 232 var b bytes.Buffer 233 234 // By default, print out the unmodified req.RequestURI, which 235 // is always set for incoming server requests. But because we 236 // previously used req.URL.RequestURI and the docs weren't 237 // always so clear about when to use DumpRequest vs 238 // DumpRequestOut, fall back to the old way if the caller 239 // provides a non-server Request. 240 reqURI := req.RequestURI 241 if reqURI == "" { 242 reqURI = req.URL.RequestURI() 243 } 244 245 fmt.Fprintf(&b, "%s %s HTTP/%d.%d\r\n", valueOrDefault(req.Method, "GET"), 246 reqURI, req.ProtoMajor, req.ProtoMinor) 247 248 absRequestURI := strings.HasPrefix(req.RequestURI, "http://") || strings.HasPrefix(req.RequestURI, "https://") 249 if !absRequestURI { 250 host := req.Host 251 if host == "" && req.URL != nil { 252 host = req.URL.Host 253 } 254 if host != "" { 255 fmt.Fprintf(&b, "Host: %s\r\n", host) 256 } 257 } 258 259 chunked := len(req.TransferEncoding) > 0 && req.TransferEncoding[0] == "chunked" 260 if len(req.TransferEncoding) > 0 { 261 fmt.Fprintf(&b, "Transfer-Encoding: %s\r\n", strings.Join(req.TransferEncoding, ",")) 262 } 263 if req.Close { 264 fmt.Fprintf(&b, "Connection: close\r\n") 265 } 266 267 err = req.Header.WriteSubset(&b, reqWriteExcludeHeaderDump) 268 if err != nil { 269 return nil, err 270 } 271 272 io.WriteString(&b, "\r\n") 273 274 if req.Body != nil { 275 var dest io.Writer = &b 276 if chunked { 277 dest = NewChunkedWriter(dest) 278 } 279 _, err = io.Copy(dest, req.Body) 280 if chunked { 281 dest.(io.Closer).Close() 282 io.WriteString(&b, "\r\n") 283 } 284 } 285 286 req.Body = save 287 if err != nil { 288 return nil, err 289 } 290 return b.Bytes(), nil 291 } 292 293 // errNoBody is a sentinel error value used by failureToReadBody so we 294 // can detect that the lack of body was intentional. 295 var errNoBody = errors.New("sentinel error value") 296 297 // failureToReadBody is a io.ReadCloser that just returns errNoBody on 298 // Read. It's swapped in when we don't actually want to consume 299 // the body, but need a non-nil one, and want to distinguish the 300 // error from reading the dummy body. 301 type failureToReadBody struct{} 302 303 func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody } 304 func (failureToReadBody) Close() error { return nil } 305 306 // emptyBody is an instance of empty reader. 307 var emptyBody = io.NopCloser(strings.NewReader("")) 308 309 // DumpResponse is like DumpRequest but dumps a response. 310 func DumpResponse(resp *http.Response, body bool) ([]byte, error) { 311 var b bytes.Buffer 312 var err error 313 save := resp.Body 314 savecl := resp.ContentLength 315 316 if !body { 317 // For content length of zero. Make sure the body is an empty 318 // reader, instead of returning error through failureToReadBody{}. 319 if resp.ContentLength == 0 { 320 resp.Body = emptyBody 321 } else { 322 resp.Body = failureToReadBody{} 323 } 324 } else if resp.Body == nil { 325 resp.Body = emptyBody 326 } else { 327 save, resp.Body, err = drainBody(resp.Body) 328 if err != nil { 329 return nil, err 330 } 331 } 332 err = resp.Write(&b) 333 if err == errNoBody { 334 err = nil 335 } 336 resp.Body = save 337 resp.ContentLength = savecl 338 if err != nil { 339 return nil, err 340 } 341 return b.Bytes(), nil 342 }