Skip to content

Commit 87c83bf

Browse files
committed
net/http/httptrace: compose ClientTrace without reflect
This is to enable use of package httptrace on TinyGo, which does not have an implementation of reflect.MakeFunc.
1 parent fc9f02c commit 87c83bf

File tree

1 file changed

+78
-27
lines changed

1 file changed

+78
-27
lines changed

src/net/http/httptrace/trace.go

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"internal/nettrace"
1313
"net"
1414
"net/textproto"
15-
"reflect"
1615
"time"
1716
)
1817

@@ -176,34 +175,86 @@ func (t *ClientTrace) compose(old *ClientTrace) {
176175
if old == nil {
177176
return
178177
}
179-
tv := reflect.ValueOf(t).Elem()
180-
ov := reflect.ValueOf(old).Elem()
181-
structType := tv.Type()
182-
for i := 0; i < structType.NumField(); i++ {
183-
tf := tv.Field(i)
184-
hookType := tf.Type()
185-
if hookType.Kind() != reflect.Func {
186-
continue
187-
}
188-
of := ov.Field(i)
189-
if of.IsNil() {
190-
continue
191-
}
192-
if tf.IsNil() {
193-
tf.Set(of)
194-
continue
195-
}
178+
t.GetConn = compose1to0(t.GetConn, old.GetConn)
179+
t.GotConn = compose1to0(t.GotConn, old.GotConn)
180+
t.PutIdleConn = compose1to0(t.PutIdleConn, old.PutIdleConn)
181+
t.GotFirstResponseByte = compose0to0(t.GotFirstResponseByte, old.GotFirstResponseByte)
182+
t.Got100Continue = compose0to0(t.Got100Continue, old.Got100Continue)
183+
t.Got1xxResponse = compose2to1(t.Got1xxResponse, old.Got1xxResponse)
184+
t.DNSStart = compose1to0(t.DNSStart, old.DNSStart)
185+
t.DNSDone = compose1to0(t.DNSDone, old.DNSDone)
186+
t.ConnectStart = compose2to0(t.ConnectStart, old.ConnectStart)
187+
t.ConnectDone = compose3to0(t.ConnectDone, old.ConnectDone)
188+
t.TLSHandshakeStart = compose0to0(t.TLSHandshakeStart, old.TLSHandshakeStart)
189+
t.TLSHandshakeDone = compose2to0(t.TLSHandshakeDone, old.TLSHandshakeDone)
190+
t.WroteHeaderField = compose2to0(t.WroteHeaderField, old.WroteHeaderField)
191+
t.WroteHeaders = compose0to0(t.WroteHeaders, old.WroteHeaders)
192+
t.Wait100Continue = compose0to0(t.Wait100Continue, old.Wait100Continue)
193+
t.WroteRequest = compose1to0(t.WroteRequest, old.WroteRequest)
194+
}
196195

197-
// Make a copy of tf for tf to call. (Otherwise it
198-
// creates a recursive call cycle and stack overflows)
199-
tfCopy := reflect.ValueOf(tf.Interface())
196+
func compose0to0[F func()](f1, f2 F) F {
197+
if f1 == nil {
198+
return f2
199+
}
200+
if f2 == nil {
201+
return f1
202+
}
203+
return func() {
204+
f1()
205+
f2()
206+
}
207+
}
208+
209+
func compose1to0[F func(A), A any](f1, f2 F) F {
210+
if f1 == nil {
211+
return f2
212+
}
213+
if f2 == nil {
214+
return f1
215+
}
216+
return func(a A) {
217+
f1(a)
218+
f2(a)
219+
}
220+
}
200221

201-
// We need to call both tf and of in some order.
202-
newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
203-
tfCopy.Call(args)
204-
return of.Call(args)
205-
})
206-
tv.Field(i).Set(newFunc)
222+
func compose2to0[F func(A, B), A, B any](f1, f2 F) F {
223+
if f1 == nil {
224+
return f2
225+
}
226+
if f2 == nil {
227+
return f1
228+
}
229+
return func(a A, b B) {
230+
f1(a, b)
231+
f2(a, b)
232+
}
233+
}
234+
235+
func compose2to1[F func(A, B) R, A, B, R any](f1, f2 F) F {
236+
if f1 == nil {
237+
return f2
238+
}
239+
if f2 == nil {
240+
return f1
241+
}
242+
return func(a A, b B) R {
243+
f1(a, b)
244+
return f2(a, b)
245+
}
246+
}
247+
248+
func compose3to0[F func(A, B, C), A, B, C any](f1, f2 F) F {
249+
if f1 == nil {
250+
return f2
251+
}
252+
if f2 == nil {
253+
return f1
254+
}
255+
return func(a A, b B, c C) {
256+
f1(a, b, c)
257+
f2(a, b, c)
207258
}
208259
}
209260

0 commit comments

Comments
 (0)