]> Cypherpunks repositories - gostls13.git/commitdiff
net/http/httptrace: compose ClientTrace without reflect
authorRandy Reddig <randy.reddig@fastly.com>
Wed, 3 Dec 2025 20:33:09 +0000 (20:33 +0000)
committerSean Liao <sean@liao.dev>
Wed, 4 Feb 2026 22:44:58 +0000 (14:44 -0800)
This is to enable use of package httptrace on TinyGo, which does not have an implementation of reflect.MakeFunc.

Change-Id: I4f1f94352797bfe3553c1c2d4d1d3dca44b03e43
GitHub-Last-Rev: 4587368ad875bd55e6a8cbdcd7e13738a8d02400
GitHub-Pull-Request: golang/go#69172
Reviewed-on: https://go-review.googlesource.com/c/go/+/609763
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sean Liao <sean@liao.dev>
src/net/http/httptrace/trace.go

index cee13d2da8345d3838fd865a41999d944ff78ddd..e835c324045d30b45fc03994bd8c7787382dc28c 100644 (file)
@@ -12,7 +12,6 @@ import (
        "internal/nettrace"
        "net"
        "net/textproto"
-       "reflect"
        "time"
 )
 
@@ -176,34 +175,86 @@ func (t *ClientTrace) compose(old *ClientTrace) {
        if old == nil {
                return
        }
-       tv := reflect.ValueOf(t).Elem()
-       ov := reflect.ValueOf(old).Elem()
-       structType := tv.Type()
-       for i := 0; i < structType.NumField(); i++ {
-               tf := tv.Field(i)
-               hookType := tf.Type()
-               if hookType.Kind() != reflect.Func {
-                       continue
-               }
-               of := ov.Field(i)
-               if of.IsNil() {
-                       continue
-               }
-               if tf.IsNil() {
-                       tf.Set(of)
-                       continue
-               }
+       t.GetConn = compose1to0(t.GetConn, old.GetConn)
+       t.GotConn = compose1to0(t.GotConn, old.GotConn)
+       t.PutIdleConn = compose1to0(t.PutIdleConn, old.PutIdleConn)
+       t.GotFirstResponseByte = compose0to0(t.GotFirstResponseByte, old.GotFirstResponseByte)
+       t.Got100Continue = compose0to0(t.Got100Continue, old.Got100Continue)
+       t.Got1xxResponse = compose2to1(t.Got1xxResponse, old.Got1xxResponse)
+       t.DNSStart = compose1to0(t.DNSStart, old.DNSStart)
+       t.DNSDone = compose1to0(t.DNSDone, old.DNSDone)
+       t.ConnectStart = compose2to0(t.ConnectStart, old.ConnectStart)
+       t.ConnectDone = compose3to0(t.ConnectDone, old.ConnectDone)
+       t.TLSHandshakeStart = compose0to0(t.TLSHandshakeStart, old.TLSHandshakeStart)
+       t.TLSHandshakeDone = compose2to0(t.TLSHandshakeDone, old.TLSHandshakeDone)
+       t.WroteHeaderField = compose2to0(t.WroteHeaderField, old.WroteHeaderField)
+       t.WroteHeaders = compose0to0(t.WroteHeaders, old.WroteHeaders)
+       t.Wait100Continue = compose0to0(t.Wait100Continue, old.Wait100Continue)
+       t.WroteRequest = compose1to0(t.WroteRequest, old.WroteRequest)
+}
 
-               // Make a copy of tf for tf to call. (Otherwise it
-               // creates a recursive call cycle and stack overflows)
-               tfCopy := reflect.ValueOf(tf.Interface())
+func compose0to0[F func()](f1, f2 F) F {
+       if f1 == nil {
+               return f2
+       }
+       if f2 == nil {
+               return f1
+       }
+       return func() {
+               f1()
+               f2()
+       }
+}
+
+func compose1to0[F func(A), A any](f1, f2 F) F {
+       if f1 == nil {
+               return f2
+       }
+       if f2 == nil {
+               return f1
+       }
+       return func(a A) {
+               f1(a)
+               f2(a)
+       }
+}
 
-               // We need to call both tf and of in some order.
-               newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
-                       tfCopy.Call(args)
-                       return of.Call(args)
-               })
-               tv.Field(i).Set(newFunc)
+func compose2to0[F func(A, B), A, B any](f1, f2 F) F {
+       if f1 == nil {
+               return f2
+       }
+       if f2 == nil {
+               return f1
+       }
+       return func(a A, b B) {
+               f1(a, b)
+               f2(a, b)
+       }
+}
+
+func compose2to1[F func(A, B) R, A, B, R any](f1, f2 F) F {
+       if f1 == nil {
+               return f2
+       }
+       if f2 == nil {
+               return f1
+       }
+       return func(a A, b B) R {
+               f1(a, b)
+               return f2(a, b)
+       }
+}
+
+func compose3to0[F func(A, B, C), A, B, C any](f1, f2 F) F {
+       if f1 == nil {
+               return f2
+       }
+       if f2 == nil {
+               return f1
+       }
+       return func(a A, b B, c C) {
+               f1(a, b, c)
+               f2(a, b, c)
        }
 }