[tor-commits] [meek/utls_2] utls: copy default timeouts etc. when using HTTP/1.

dcf at torproject.org dcf at torproject.org
Thu Jan 31 07:30:46 UTC 2019


commit 52bc759ac35ade721c87f53ba2d341a68c715c32
Author: David Fifield <david at bamsoftware.com>
Date:   Thu Jan 31 00:22:11 2019 -0700

    utls: copy default timeouts etc. when using HTTP/1.
    
    Unfortunately I don't know a way to do the same for HTTP/2. Configuring
    an http.Transport and then calling http2.ConfigureTransport on it
    doesn't work; it leads to the same problem of an HTTP/1 client speaking
    to an HTTP/2 server.
---
 meek-client/utls.go | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/meek-client/utls.go b/meek-client/utls.go
index 9076549..2c9c52e 100644
--- a/meek-client/utls.go
+++ b/meek-client/utls.go
@@ -39,6 +39,7 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"reflect"
 	"strings"
 	"sync"
 
@@ -46,6 +47,25 @@ import (
 	"golang.org/x/net/http2"
 )
 
+// Copy the public fields (fields for which CanSet is true) from src to dst.
+// src and dst must be pointers to the same type. We use this to make copies of
+// httpRoundTripper. We cannot use struct assignment, because http.Transport
+// contains private mutexes. The idea of using reflection to copy only the
+// public fields comes from a post by Nick Craig-Wood:
+// https://groups.google.com/d/msg/Golang-Nuts/SDiGYNVE8iY/89hRKTF4BAAJ
+func copyPublicFields(dst, src interface{}) {
+	if reflect.TypeOf(dst) != reflect.TypeOf(src) {
+		panic("unequal types")
+	}
+	dstValue := reflect.ValueOf(dst).Elem()
+	srcValue := reflect.ValueOf(src).Elem()
+	for i := 0; i < dstValue.NumField(); i++ {
+		if dstValue.Field(i).CanSet() {
+			dstValue.Field(i).Set(srcValue.Field(i))
+		}
+	}
+}
+
 // Extract a host:port address from a URL, suitable for passing to net.Dial.
 func addrForDial(url *url.URL) (string, error) {
 	host := url.Hostname()
@@ -164,6 +184,10 @@ func makeRoundTripper(req *http.Request, clientHelloID *utls.ClientHelloID) (htt
 	// Construct an http.Transport or http2.Transport depending on ALPN.
 	switch protocol {
 	case http2.NextProtoTLS:
+		// Unfortunately http2.Transport does not expose the same
+		// configuration options as http.Transport with regard to
+		// timeouts, etc., so we are at the mercy of the defaults.
+		// https://github.com/golang/go/issues/16581
 		return &http2.Transport{
 			DialTLS: func(network, addr string, _ *tls.Config) (net.Conn, error) {
 				// Ignore the *tls.Config parameter; use our
@@ -172,8 +196,13 @@ func makeRoundTripper(req *http.Request, clientHelloID *utls.ClientHelloID) (htt
 			},
 		}, nil
 	default:
-		// TODO: copy public fields from httpRoundTripper?
-		return &http.Transport{DialTLS: dialTLS}, nil
+		// With http.Transport, copy important default fields from
+		// http.DefaultTransport, such as TLSHandshakeTimeout and
+		// IdleConnTimeout.
+		tr := &http.Transport{}
+		copyPublicFields(tr, httpRoundTripper)
+		tr.DialTLS = dialTLS
+		return tr, nil
 	}
 }
 



More information about the tor-commits mailing list