Skip to content

Commit 571fae2

Browse files
authored
Add traceWroteRequest & traceGotConn
Copied from quic-go#4749, for XTLS/Xray-core#4150
1 parent 0b033db commit 571fae2

File tree

2 files changed

+39
-1
lines changed

2 files changed

+39
-1
lines changed

http3/client.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,20 @@ func (c *SingleDestinationRoundTripper) sendRequestBody(str Stream, body io.Read
283283
return err
284284
}
285285

286+
func traceWroteRequest(ctx context.Context, err error) {
287+
trace := httptrace.ContextClientTrace(ctx)
288+
if trace != nil && trace.WroteRequest != nil {
289+
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
290+
}
291+
}
292+
286293
func (c *SingleDestinationRoundTripper) doRequest(req *http.Request, str *requestStream) (*http.Response, error) {
287294
if err := str.SendRequestHeader(req); err != nil {
295+
traceWroteRequest(req.Context(), err)
288296
return nil, err
289297
}
290298
if req.Body == nil {
299+
traceWroteRequest(req.Context(), nil)
291300
str.Close()
292301
} else {
293302
// send the request body asynchronously
@@ -298,7 +307,9 @@ func (c *SingleDestinationRoundTripper) doRequest(req *http.Request, str *reques
298307
if req.ContentLength > 0 {
299308
contentLength = req.ContentLength
300309
}
301-
if err := c.sendRequestBody(str, req.Body, contentLength); err != nil {
310+
err := c.sendRequestBody(str, req.Body, contentLength)
311+
traceWroteRequest(req.Context(), err)
312+
if err != nil {
302313
if c.Logger != nil {
303314
c.Logger.Debug("error writing request", "error", err)
304315
}

http3/roundtrip.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import (
88
"io"
99
"net"
1010
"net/http"
11+
"net/http/httptrace"
1112
"strings"
1213
"sync"
1314
"sync/atomic"
15+
"time"
1416

1517
"golang.org/x/net/http/httpguts"
1618

@@ -114,6 +116,30 @@ var (
114116
// ErrNoCachedConn is returned when RoundTripper.OnlyCachedConn is set
115117
var ErrNoCachedConn = errors.New("http3: no cached connection was available")
116118

119+
// fakeConn is a wrapper for quic.EarlyConnection
120+
// because the quic connection does not implement net.Conn.
121+
type fakeConn struct {
122+
conn quic.EarlyConnection
123+
}
124+
125+
func (c *fakeConn) Close() error { panic("connection operation prohibited") }
126+
func (c *fakeConn) Read(p []byte) (int, error) { panic("connection operation prohibited") }
127+
func (c *fakeConn) Write(p []byte) (int, error) { panic("connection operation prohibited") }
128+
func (c *fakeConn) SetDeadline(t time.Time) error { panic("connection operation prohibited") }
129+
func (c *fakeConn) SetReadDeadline(t time.Time) error { panic("connection operation prohibited") }
130+
func (c *fakeConn) SetWriteDeadline(t time.Time) error { panic("connection operation prohibited") }
131+
func (c *fakeConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
132+
func (c *fakeConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
133+
134+
func traceGotConn(trace *httptrace.ClientTrace, conn quic.EarlyConnection, reused bool) {
135+
if trace != nil && trace.GotConn != nil {
136+
trace.GotConn(httptrace.GotConnInfo{
137+
Conn: &fakeConn{conn: conn},
138+
Reused: reused,
139+
})
140+
}
141+
}
142+
117143
// RoundTripOpt is like RoundTrip, but takes options.
118144
func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
119145
r.initOnce.Do(func() { r.initErr = r.init() })
@@ -169,6 +195,7 @@ func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.
169195
r.removeClient(hostname)
170196
return nil, cl.dialErr
171197
}
198+
traceGotConn(httptrace.ContextClientTrace(req.Context()), cl.conn, isReused)
172199
defer cl.useCount.Add(-1)
173200
rsp, err := cl.rt.RoundTrip(req)
174201
if err != nil {

0 commit comments

Comments
 (0)