// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package httputil

import (
	
	
	
	
	
	
	
	
	
	
)

// drainBody reads all of b to memory and then returns two equivalent
// ReadClosers yielding the same bytes.
//
// It returns an error if the initial slurp of all bytes fails. It does not attempt
// to make the returned ReadClosers have identical error-matching behavior.
func drainBody( io.ReadCloser) (,  io.ReadCloser,  error) {
	if  == nil ||  == http.NoBody {
		// No copying needed. Preserve the magic sentinel meaning of NoBody.
		return http.NoBody, http.NoBody, nil
	}
	var  bytes.Buffer
	if _,  = .ReadFrom();  != nil {
		return nil, , 
	}
	if  = .Close();  != nil {
		return nil, , 
	}
	return io.NopCloser(&), io.NopCloser(bytes.NewReader(.Bytes())), nil
}

// dumpConn is a net.Conn which writes to Writer and reads from Reader
type dumpConn struct {
	io.Writer
	io.Reader
}

func ( *dumpConn) () error                       { return nil }
func ( *dumpConn) () net.Addr                { return nil }
func ( *dumpConn) () net.Addr               { return nil }
func ( *dumpConn) ( time.Time) error      { return nil }
func ( *dumpConn) ( time.Time) error  { return nil }
func ( *dumpConn) ( time.Time) error { return nil }

type neverEnding byte

func ( neverEnding) ( []byte) ( int,  error) {
	for  := range  {
		[] = byte()
	}
	return len(), nil
}

// outgoingLength is a copy of the unexported
// (*http.Request).outgoingLength method.
func outgoingLength( *http.Request) int64 {
	if .Body == nil || .Body == http.NoBody {
		return 0
	}
	if .ContentLength != 0 {
		return .ContentLength
	}
	return -1
}

// DumpRequestOut is like [DumpRequest] but for outgoing client requests. It
// includes any headers that the standard [http.Transport] adds, such as
// User-Agent.
func ( *http.Request,  bool) ([]byte, error) {
	 := .Body
	 := false
	if ! {
		 := outgoingLength()
		if  != 0 {
			.Body = io.NopCloser(io.LimitReader(neverEnding('x'), ))
			 = true
		}
	} else {
		var  error
		, .Body,  = drainBody(.Body)
		if  != nil {
			return nil, 
		}
	}

	// Since we're using the actual Transport code to write the request,
	// switch to http so the Transport doesn't try to do an SSL
	// negotiation with our dumpConn and its bytes.Buffer & pipe.
	// The wire format for https and http are the same, anyway.
	 := 
	if .URL.Scheme == "https" {
		 = new(http.Request)
		* = *
		.URL = new(url.URL)
		*.URL = *.URL
		.URL.Scheme = "http"
	}

	// Use the actual Transport code to record what we would send
	// on the wire, but not using TCP.  Use a Transport with a
	// custom dialer that returns a fake net.Conn that waits
	// for the full input (and recording it), and then responds
	// with a dummy response.
	var  bytes.Buffer // records the output
	,  := io.Pipe()
	defer .Close()
	defer .Close()
	 := &delegateReader{c: make(chan io.Reader)}

	 := &http.Transport{
		Dial: func(,  string) (net.Conn, error) {
			return &dumpConn{io.MultiWriter(&, ), }, nil
		},
	}
	defer .CloseIdleConnections()

	// We need this channel to ensure that the reader
	// goroutine exits if t.RoundTrip returns an error.
	// See golang.org/issue/32571.
	 := make(chan struct{})
	// Wait for the request before replying with a dummy response:
	go func() {
		,  := http.ReadRequest(bufio.NewReader())
		if  == nil {
			// Ensure all the body is read; otherwise
			// we'll get a partial dump.
			io.Copy(io.Discard, .Body)
			.Body.Close()
		}
		select {
		case .c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"):
		case <-:
			// Ensure delegateReader.Read doesn't block forever if we get an error.
			close(.c)
		}
	}()

	,  := .RoundTrip()

	.Body = 
	if  != nil {
		.Close()
		.err = 
		close()
		return nil, 
	}
	 := .Bytes()

	// If we used a dummy body above, remove it now.
	// TODO: if the req.ContentLength is large, we allocate memory
	// unnecessarily just to slice it off here. But this is just
	// a debug function, so this is acceptable for now. We could
	// discard the body earlier if this matters.
	if  {
		if  := bytes.Index(, []byte("\r\n\r\n"));  >= 0 {
			 = [:+4]
		}
	}
	return , nil
}

// delegateReader is a reader that delegates to another reader,
// once it arrives on a channel.
type delegateReader struct {
	c   chan io.Reader
	err error     // only used if r is nil and c is closed.
	r   io.Reader // nil until received from c
}

func ( *delegateReader) ( []byte) (int, error) {
	if .r == nil {
		var  bool
		if .r,  = <-.c; ! {
			return 0, .err
		}
	}
	return .r.Read()
}

// Return value if nonempty, def otherwise.
func valueOrDefault(,  string) string {
	if  != "" {
		return 
	}
	return 
}

var reqWriteExcludeHeaderDump = map[string]bool{
	"Host":              true, // not in Header map anyway
	"Transfer-Encoding": true,
	"Trailer":           true,
}

// DumpRequest returns the given request in its HTTP/1.x wire
// representation. It should only be used by servers to debug client
// requests. The returned representation is an approximation only;
// some details of the initial request are lost while parsing it into
// an [http.Request]. In particular, the order and case of header field
// names are lost. The order of values in multi-valued headers is kept
// intact. HTTP/2 requests are dumped in HTTP/1.x form, not in their
// original binary representations.
//
// If body is true, DumpRequest also returns the body. To do so, it
// consumes req.Body and then replaces it with a new [io.ReadCloser]
// that yields the same bytes. If DumpRequest returns an error,
// the state of req is undefined.
//
// The documentation for [http.Request.Write] details which fields
// of req are included in the dump.
func ( *http.Request,  bool) ([]byte, error) {
	var  error
	 := .Body
	if ! || .Body == nil {
		.Body = nil
	} else {
		, .Body,  = drainBody(.Body)
		if  != nil {
			return nil, 
		}
	}

	var  bytes.Buffer

	// By default, print out the unmodified req.RequestURI, which
	// is always set for incoming server requests. But because we
	// previously used req.URL.RequestURI and the docs weren't
	// always so clear about when to use DumpRequest vs
	// DumpRequestOut, fall back to the old way if the caller
	// provides a non-server Request.
	 := .RequestURI
	if  == "" {
		 = .URL.RequestURI()
	}

	fmt.Fprintf(&, "%s %s HTTP/%d.%d\r\n", valueOrDefault(.Method, "GET"),
		, .ProtoMajor, .ProtoMinor)

	 := strings.HasPrefix(.RequestURI, "http://") || strings.HasPrefix(.RequestURI, "https://")
	if ! {
		 := .Host
		if  == "" && .URL != nil {
			 = .URL.Host
		}
		if  != "" {
			fmt.Fprintf(&, "Host: %s\r\n", )
		}
	}

	 := len(.TransferEncoding) > 0 && .TransferEncoding[0] == "chunked"
	if len(.TransferEncoding) > 0 {
		fmt.Fprintf(&, "Transfer-Encoding: %s\r\n", strings.Join(.TransferEncoding, ","))
	}

	 = .Header.WriteSubset(&, reqWriteExcludeHeaderDump)
	if  != nil {
		return nil, 
	}

	io.WriteString(&, "\r\n")

	if .Body != nil {
		var  io.Writer = &
		if  {
			 = NewChunkedWriter()
		}
		_,  = io.Copy(, .Body)
		if  {
			.(io.Closer).Close()
			io.WriteString(&, "\r\n")
		}
	}

	.Body = 
	if  != nil {
		return nil, 
	}
	return .Bytes(), nil
}

// errNoBody is a sentinel error value used by failureToReadBody so we
// can detect that the lack of body was intentional.
var errNoBody = errors.New("sentinel error value")

// failureToReadBody is an io.ReadCloser that just returns errNoBody on
// Read. It's swapped in when we don't actually want to consume
// the body, but need a non-nil one, and want to distinguish the
// error from reading the dummy body.
type failureToReadBody struct{}

func (failureToReadBody) ([]byte) (int, error) { return 0, errNoBody }
func (failureToReadBody) () error             { return nil }

// emptyBody is an instance of empty reader.
var emptyBody = io.NopCloser(strings.NewReader(""))

// DumpResponse is like DumpRequest but dumps a response.
func ( *http.Response,  bool) ([]byte, error) {
	var  bytes.Buffer
	var  error
	 := .Body
	 := .ContentLength

	if ! {
		// For content length of zero. Make sure the body is an empty
		// reader, instead of returning error through failureToReadBody{}.
		if .ContentLength == 0 {
			.Body = emptyBody
		} else {
			.Body = failureToReadBody{}
		}
	} else if .Body == nil {
		.Body = emptyBody
	} else {
		, .Body,  = drainBody(.Body)
		if  != nil {
			return nil, 
		}
	}
	 = .Write(&)
	if  == errNoBody {
		 = nil
	}
	.Body = 
	.ContentLength = 
	if  != nil {
		return nil, 
	}
	return .Bytes(), nil
}