Skip to content

Commit 983d88d

Browse files
talentlessguydeadprogram
authored andcommitted
add stub httptrace methods and interfaces
1 parent 77be396 commit 983d88d

File tree

2 files changed

+228
-0
lines changed

2 files changed

+228
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ src/net
3232
│   │   ├── httptest.go *
3333
│   │   ├── recorder.go
3434
│   │   └── server.go *
35+
│   ├── httptest
36+
│   │   └── trace.go *
3537
│   ├── client.go *
3638
│   ├── clone.go
3739
│   ├── cookie.go

http/httptrace/trace.go

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// TINYGO: The following is copied and modified from Go 1.21.5 official implementation.
2+
3+
// Copyright 2016 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Package httptrace provides mechanisms to trace the events within
8+
// HTTP client requests.
9+
10+
package httptrace
11+
12+
import (
13+
"context"
14+
"crypto/tls"
15+
"net"
16+
"net/textproto"
17+
"reflect"
18+
"time"
19+
)
20+
21+
// unique type to prevent assignment.
22+
type clientEventContextKey struct{}
23+
24+
// ContextClientTrace returns the [ClientTrace] associated with the
25+
// provided context. If none, it returns nil.
26+
func ContextClientTrace(ctx context.Context) *ClientTrace {
27+
trace, _ := ctx.Value(clientEventContextKey{}).(*ClientTrace)
28+
return trace
29+
}
30+
31+
// WithClientTrace returns a new context based on the provided parent
32+
// ctx. HTTP client requests made with the returned context will use
33+
// the provided trace hooks, in addition to any previous hooks
34+
// registered with ctx. Any hooks defined in the provided trace will
35+
// be called first.
36+
func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
37+
if trace == nil {
38+
panic("nil trace")
39+
}
40+
old := ContextClientTrace(ctx)
41+
trace.compose(old)
42+
43+
ctx = context.WithValue(ctx, clientEventContextKey{}, trace)
44+
45+
return ctx
46+
}
47+
48+
// ClientTrace is a set of hooks to run at various stages of an outgoing
49+
// HTTP request. Any particular hook may be nil. Functions may be
50+
// called concurrently from different goroutines and some may be called
51+
// after the request has completed or failed.
52+
//
53+
// ClientTrace currently traces a single HTTP request & response
54+
// during a single round trip and has no hooks that span a series
55+
// of redirected requests.
56+
//
57+
// See https://blog.golang.org/http-tracing for more.
58+
type ClientTrace struct {
59+
// GetConn is called before a connection is created or
60+
// retrieved from an idle pool. The hostPort is the
61+
// "host:port" of the target or proxy. GetConn is called even
62+
// if there's already an idle cached connection available.
63+
GetConn func(hostPort string)
64+
65+
// GotConn is called after a successful connection is
66+
// obtained. There is no hook for failure to obtain a
67+
// connection; instead, use the error from
68+
// Transport.RoundTrip.
69+
GotConn func(GotConnInfo)
70+
71+
// PutIdleConn is called when the connection is returned to
72+
// the idle pool. If err is nil, the connection was
73+
// successfully returned to the idle pool. If err is non-nil,
74+
// it describes why not. PutIdleConn is not called if
75+
// connection reuse is disabled via Transport.DisableKeepAlives.
76+
// PutIdleConn is called before the caller's Response.Body.Close
77+
// call returns.
78+
// For HTTP/2, this hook is not currently used.
79+
PutIdleConn func(err error)
80+
81+
// GotFirstResponseByte is called when the first byte of the response
82+
// headers is available.
83+
GotFirstResponseByte func()
84+
85+
// Got100Continue is called if the server replies with a "100
86+
// Continue" response.
87+
Got100Continue func()
88+
89+
// Got1xxResponse is called for each 1xx informational response header
90+
// returned before the final non-1xx response. Got1xxResponse is called
91+
// for "100 Continue" responses, even if Got100Continue is also defined.
92+
// If it returns an error, the client request is aborted with that error value.
93+
Got1xxResponse func(code int, header textproto.MIMEHeader) error
94+
95+
// DNSStart is called when a DNS lookup begins.
96+
DNSStart func(DNSStartInfo)
97+
98+
// DNSDone is called when a DNS lookup ends.
99+
DNSDone func(DNSDoneInfo)
100+
101+
// ConnectStart is called when a new connection's Dial begins.
102+
// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
103+
// enabled, this may be called multiple times.
104+
ConnectStart func(network, addr string)
105+
106+
// ConnectDone is called when a new connection's Dial
107+
// completes. The provided err indicates whether the
108+
// connection completed successfully.
109+
// If net.Dialer.DualStack ("Happy Eyeballs") support is
110+
// enabled, this may be called multiple times.
111+
ConnectDone func(network, addr string, err error)
112+
113+
// TLSHandshakeStart is called when the TLS handshake is started. When
114+
// connecting to an HTTPS site via an HTTP proxy, the handshake happens
115+
// after the CONNECT request is processed by the proxy.
116+
TLSHandshakeStart func()
117+
118+
// TLSHandshakeDone is called after the TLS handshake with either the
119+
// successful handshake's connection state, or a non-nil error on handshake
120+
// failure.
121+
TLSHandshakeDone func(tls.ConnectionState, error)
122+
123+
// WroteHeaderField is called after the Transport has written
124+
// each request header. At the time of this call the values
125+
// might be buffered and not yet written to the network.
126+
WroteHeaderField func(key string, value []string)
127+
128+
// WroteHeaders is called after the Transport has written
129+
// all request headers.
130+
WroteHeaders func()
131+
132+
// Wait100Continue is called if the Request specified
133+
// "Expect: 100-continue" and the Transport has written the
134+
// request headers but is waiting for "100 Continue" from the
135+
// server before writing the request body.
136+
Wait100Continue func()
137+
138+
// WroteRequest is called with the result of writing the
139+
// request and any body. It may be called multiple times
140+
// in the case of retried requests.
141+
WroteRequest func(WroteRequestInfo)
142+
}
143+
144+
// WroteRequestInfo contains information provided to the WroteRequest
145+
// hook.
146+
type WroteRequestInfo struct {
147+
// Err is any error encountered while writing the Request.
148+
Err error
149+
}
150+
151+
// compose modifies t such that it respects the previously-registered hooks in old,
152+
// subject to the composition policy requested in t.Compose.
153+
func (t *ClientTrace) compose(old *ClientTrace) {
154+
if old == nil {
155+
return
156+
}
157+
tv := reflect.ValueOf(t).Elem()
158+
ov := reflect.ValueOf(old).Elem()
159+
structType := tv.Type()
160+
for i := 0; i < structType.NumField(); i++ {
161+
tf := tv.Field(i)
162+
hookType := tf.Type()
163+
if hookType.Kind() != reflect.Func {
164+
continue
165+
}
166+
of := ov.Field(i)
167+
if of.IsNil() {
168+
continue
169+
}
170+
if tf.IsNil() {
171+
tf.Set(of)
172+
continue
173+
}
174+
175+
// Make a copy of tf for tf to call. (Otherwise it
176+
// creates a recursive call cycle and stack overflows)
177+
tfCopy := reflect.ValueOf(tf.Interface())
178+
179+
// We need to call both tf and of in some order.
180+
newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
181+
tfCopy.Call(args)
182+
return of.Call(args)
183+
})
184+
tv.Field(i).Set(newFunc)
185+
}
186+
}
187+
188+
// DNSStartInfo contains information about a DNS request.
189+
type DNSStartInfo struct {
190+
Host string
191+
}
192+
193+
// DNSDoneInfo contains information about the results of a DNS lookup.
194+
type DNSDoneInfo struct {
195+
// Addrs are the IPv4 and/or IPv6 addresses found in the DNS
196+
// lookup. The contents of the slice should not be mutated.
197+
Addrs []net.IPAddr
198+
199+
// Err is any error that occurred during the DNS lookup.
200+
Err error
201+
202+
// Coalesced is whether the Addrs were shared with another
203+
// caller who was doing the same DNS lookup concurrently.
204+
Coalesced bool
205+
}
206+
207+
// GotConnInfo is the argument to the [ClientTrace.GotConn] function and
208+
// contains information about the obtained connection.
209+
type GotConnInfo struct {
210+
// Conn is the connection that was obtained. It is owned by
211+
// the http.Transport and should not be read, written or
212+
// closed by users of ClientTrace.
213+
Conn net.Conn
214+
215+
// Reused is whether this connection has been previously
216+
// used for another HTTP request.
217+
Reused bool
218+
219+
// WasIdle is whether this connection was obtained from an
220+
// idle pool.
221+
WasIdle bool
222+
223+
// IdleTime reports how long the connection was previously
224+
// idle, if WasIdle is true.
225+
IdleTime time.Duration
226+
}

0 commit comments

Comments
 (0)