[tor-commits] [meek/master] Refactor the helper into an http.RoundTripper.

dcf at torproject.org dcf at torproject.org
Tue Jan 15 07:42:07 UTC 2019


commit 15108412e6d608ebaaadbf8df84f62a4da96afe7
Author: David Fifield <david at bamsoftware.com>
Date:   Mon Jan 14 22:52:14 2019 -0700

    Refactor the helper into an http.RoundTripper.
---
 meek-client/helper.go      | 82 ++++++++++++++++++++++++++++++++++++++--------
 meek-client/meek-client.go | 23 ++++++++-----
 2 files changed, 83 insertions(+), 22 deletions(-)

diff --git a/meek-client/helper.go b/meek-client/helper.go
index 6b89f5a..f412aaa 100644
--- a/meek-client/helper.go
+++ b/meek-client/helper.go
@@ -9,6 +9,7 @@ import (
 	"io/ioutil"
 	"net"
 	"net/http"
+	"net/textproto"
 	"net/url"
 	"strconv"
 	"time"
@@ -40,6 +41,11 @@ type ProxySpec struct {
 	Port int    `json:"port"`
 }
 
+type HelperRoundTripper struct {
+	ReadTimeout  time.Duration
+	WriteTimeout time.Duration
+}
+
 // Return a ProxySpec suitable for the proxy URL in u.
 func makeProxySpec(u *url.URL) (*ProxySpec, error) {
 	spec := new(ProxySpec)
@@ -80,9 +86,7 @@ func makeProxySpec(u *url.URL) (*ProxySpec, error) {
 	return spec, nil
 }
 
-// Do an HTTP roundtrip through the configured browser extension, using the
-// payload data in buf and the request metadata in info.
-func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error) {
+func (rt *HelperRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 	s, err := net.DialTCP("tcp", nil, options.HelperAddr)
 	if err != nil {
 		return nil, err
@@ -90,28 +94,51 @@ func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error)
 	defer s.Close()
 
 	// Encode our JSON.
-	req := JSONRequest{
-		Method: "POST",
-		URL:    info.URL.String(),
+	jsonReq := JSONRequest{
+		Method: req.Method,
+		URL:    req.URL.String(),
 		Header: make(map[string]string),
-		Body:   buf,
+		Body:   make([]byte, 0),
 	}
-	req.Header["X-Session-Id"] = info.SessionID
-	if info.Host != "" {
-		req.Header["Host"] = info.Host
+
+	// We take only the first value for each header key, due to limitations
+	// in the helper JSON protocol.
+	for key, values := range req.Header {
+		if len(values) == 0 {
+			continue
+		}
+		value := values[0]
+		key = textproto.CanonicalMIMEHeaderKey(key)
+		jsonReq.Header[key] = value
+	}
+	// req.Host overrides req.Header.
+	if req.Host != "" {
+		jsonReq.Header["Host"] = req.Host
 	}
-	req.Proxy, err = makeProxySpec(options.ProxyURL)
+
+	if req.Body != nil {
+		jsonReq.Body, err = ioutil.ReadAll(req.Body)
+		if err != nil {
+			return nil, err
+		}
+		err = req.Body.Close()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	jsonReq.Proxy, err = makeProxySpec(options.ProxyURL)
 	if err != nil {
 		return nil, err
 	}
-	encReq, err := json.Marshal(&req)
+	encReq, err := json.Marshal(&jsonReq)
 	if err != nil {
 		return nil, err
 	}
 	// log.Printf("encoded %s", encReq)
 
 	// Send the request.
-	s.SetWriteDeadline(time.Now().Add(helperWriteTimeout))
+	s.SetWriteDeadline(time.Now().Add(rt.WriteTimeout))
 	err = binary.Write(s, binary.BigEndian, uint32(len(encReq)))
 	if err != nil {
 		return nil, err
@@ -123,7 +150,7 @@ func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error)
 
 	// Read the response.
 	var length uint32
-	s.SetReadDeadline(time.Now().Add(helperReadTimeout))
+	s.SetReadDeadline(time.Now().Add(rt.ReadTimeout))
 	err = binary.Read(s, binary.BigEndian, &length)
 	if err != nil {
 		return nil, err
@@ -159,3 +186,30 @@ func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error)
 	}
 	return &resp, nil
 }
+
+// Do an HTTP roundtrip through the configured browser extension, using the
+// payload data in buf and the request metadata in info.
+func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error) {
+	var body io.Reader
+	if len(buf) > 0 {
+		// Leave body == nil when buf is empty. A nil body is an
+		// explicit signal that the body is empty. An empty
+		// *bytes.Reader or the magic value http.NoBody are supposed to
+		// be equivalent ways to signal an empty body, but in Go 1.8 the
+		// HTTP/2 code only understands nil. Not leaving body == nil
+		// causes the Content-Length header to be omitted from HTTP/2
+		// requests, which in some cases can cause the server to return
+		// a 411 "Length Required" error. See
+		// https://bugs.torproject.org/22865.
+		body = bytes.NewReader(buf)
+	}
+	req, err := http.NewRequest("POST", info.URL.String(), body)
+	if err != nil {
+		return nil, err
+	}
+	if info.Host != "" {
+		req.Host = info.Host
+	}
+	req.Header.Set("X-Session-Id", info.SessionID)
+	return helperRoundTripper.RoundTrip(req)
+}
diff --git a/meek-client/meek-client.go b/meek-client/meek-client.go
index bbacbb4..3923b9a 100644
--- a/meek-client/meek-client.go
+++ b/meek-client/meek-client.go
@@ -83,10 +83,16 @@ const (
 
 var ptInfo pt.ClientInfo
 
-// We use this RoundTripper to make all our requests (when --helper is not
-// used). We use the defaults, except we take control of the Proxy setting
+// We use this RoundTripper to make all our requests when --helper is not
+// in effect. We use the defaults, except we take control of the Proxy setting
 // (notably, disabling the default ProxyFromEnvironment).
-var httpTransport *http.Transport = http.DefaultTransport.(*http.Transport)
+var httpRoundTripper *http.Transport = http.DefaultTransport.(*http.Transport)
+
+// We use this RoundTripper when --helper is in effect.
+var helperRoundTripper = &HelperRoundTripper{
+	ReadTimeout:  helperReadTimeout,
+	WriteTimeout: helperWriteTimeout,
+}
 
 // Store for command line options.
 var options struct {
@@ -137,7 +143,7 @@ func roundTripWithHTTP(buf []byte, info *RequestInfo) (*http.Response, error) {
 		req.Host = info.Host
 	}
 	req.Header.Set("X-Session-Id", info.SessionID)
-	return httpTransport.RoundTrip(req)
+	return httpRoundTripper.RoundTrip(req)
 }
 
 // Do a roundtrip, trying at most limit times if there is an HTTP status other
@@ -411,9 +417,10 @@ func main() {
 		}
 	}
 
-	// Disable the default ProxyFromEnvironment setting. httpTransport.Proxy
-	// is overridden below if options.ProxyURL is set.
-	httpTransport.Proxy = nil
+	// Disable the default ProxyFromEnvironment setting.
+	// httpRoundTripper.Proxy is overridden below if options.ProxyURL is
+	// set.
+	httpRoundTripper.Proxy = nil
 
 	// Command-line proxy overrides managed configuration.
 	if options.ProxyURL == nil {
@@ -427,7 +434,7 @@ func main() {
 			log.Fatal(fmt.Sprintf("proxy error: %s", err))
 		}
 		log.Printf("using proxy %s", options.ProxyURL.String())
-		httpTransport.Proxy = http.ProxyURL(options.ProxyURL)
+		httpRoundTripper.Proxy = http.ProxyURL(options.ProxyURL)
 		if ptInfo.ProxyURL != nil {
 			pt.ProxyDone()
 		}





More information about the tor-commits mailing list