commit 15108412e6d608ebaaadbf8df84f62a4da96afe7
Author: David Fifield <[email protected]>
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()
                }



_______________________________________________
tor-commits mailing list
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to