package wsutil

import (
	
	
	
	
	
	
	

	
)

// DebugDialer is a wrapper around ws.Dialer. It tracks i/o of WebSocket
// handshake. That is, it gives ability to receive copied HTTP request and
// response bytes that made inside Dialer.Dial().
//
// Note that it must not be used in production applications that requires
// Dial() to be efficient.
type DebugDialer struct {
	// Dialer contains WebSocket connection establishment options.
	Dialer ws.Dialer

	// OnRequest and OnResponse are the callbacks that will be called with the
	// HTTP request and response respectively.
	OnRequest, OnResponse func([]byte)
}

// Dial connects to the url host and upgrades connection to WebSocket. It makes
// it by calling d.Dialer.Dial().
func ( *DebugDialer) ( context.Context,  string) ( net.Conn,  *bufio.Reader,  ws.Handshake,  error) {
	// Need to copy Dialer to prevent original object mutation.
	 := .Dialer
	var (
		 bytes.Buffer
		 bytes.Buffer

		 int64
	)
	 := .WrapConn
	.WrapConn = func( net.Conn) net.Conn {
		if  != nil {
			 = ()
		}

		// Save the pointer to the raw connection.
		 = 

		var (
			 io.Reader = 
			 io.Writer = 
		)
		if .OnResponse != nil {
			 = &prefetchResponseReader{
				source:        ,
				buffer:        &,
				contentLength: &,
			}
		}
		if .OnRequest != nil {
			 = io.MultiWriter(, &)
		}
		return rwConn{, , }
	}

	_, , ,  = .Dial(, )

	if  := .OnRequest;  != nil {
		(.Bytes())
	}
	if  := .OnResponse;  != nil {
		// We must split response inside buffered bytes from other received
		// bytes from server.
		 := .Bytes()
		 := bytes.Index(, headEnd)
		 :=  + len(headEnd)         // Head end index.
		 =  + int() // Body end index.

		([:])

		if  != nil {
			// If br is non-nil, then it mean two things. First is that
			// handshake is OK and server has sent additional bytes – probably
			// immediate sent frames (or weird but possible response body).
			// Second, the bad one, is that br buffer's source is now rwConn
			// instance from above WrapConn call. It is incorrect, so we must
			// fix it.
			var  io.Reader = 
			if len() >  {
				// Buffer contains more than just HTTP headers bytes.
				 = io.MultiReader(
					bytes.NewReader([:]),
					,
				)
			}
			.Reset()
			// Must make br.Buffered() to be non-zero.
			.Peek(len([:]))
		}
	}

	return , , , 
}

type rwConn struct {
	net.Conn

	r io.Reader
	w io.Writer
}

func ( rwConn) ( []byte) (int, error) {
	return .r.Read()
}

func ( rwConn) ( []byte) (int, error) {
	return .w.Write()
}

var headEnd = []byte("\r\n\r\n")

type prefetchResponseReader struct {
	source io.Reader // Original connection source.
	reader io.Reader // Wrapped reader used to read from by clients.
	buffer *bytes.Buffer

	contentLength *int64
}

func ( *prefetchResponseReader) ( []byte) (int, error) {
	if .reader == nil {
		,  := http.ReadResponse(bufio.NewReader(
			io.TeeReader(.source, .buffer),
		), nil)
		if  == nil {
			*.contentLength, _ = io.Copy(ioutil.Discard, .Body)
			.Body.Close()
		}
		 := .buffer.Bytes()
		.reader = io.MultiReader(
			bytes.NewReader(),
			.source,
		)
	}
	return .reader.Read()
}