commit bb22b14120357f084d7e63d26c3ee493bc270236
Author: David Fifield <david(a)bamsoftware.com>
Date: Sat May 24 22:37:17 2014 -0700
Honor an http proxy when roundtripping through the helper.
---
meek-client/helper.go | 55 ++++++++++++++++++++++++++++++++++++++++++++
meek-client/helper_test.go | 50 ++++++++++++++++++++++++++++++++++++++++
meek-client/meek-client.go | 4 +++-
3 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/meek-client/helper.go b/meek-client/helper.go
index 49423fb..d85ac8d 100644
--- a/meek-client/helper.go
+++ b/meek-client/helper.go
@@ -10,6 +10,8 @@ import (
"io/ioutil"
"net"
"net/http"
+ "net/url"
+ "strconv"
"time"
)
@@ -21,6 +23,7 @@ type JSONRequest struct {
URL string `json:"url,omitempty"`
Header map[string]string `json:"header,omitempty"`
Body []byte `json:"body,omitempty"`
+ Proxy *ProxySpec `json:"proxy,omitempty"`
}
type JSONResponse struct {
@@ -29,6 +32,54 @@ type JSONResponse struct {
Body []byte `json:"body"`
}
+// ProxySpec encodes information we need to connect through a proxy.
+type ProxySpec struct {
+ // Acceptable values for Type are as in proposal 232: "http", "socks5",
+ // or "socks4a".
+ Type string `json:"type"`
+ Host string `json:"host"`
+ Port int `json:"port"`
+}
+
+// Return a ProxySpec suitable for the proxy URL in u.
+func makeProxySpec(u *url.URL) (*ProxySpec, error) {
+ spec := new(ProxySpec)
+ var err error
+ var portStr string
+ var port uint64
+
+ if u == nil {
+ // No proxy.
+ return nil, nil
+ }
+
+ // Firefox's nsIProxyInfo doesn't allow credentials.
+ if u.User != nil {
+ return nil, errors.New("proxy URLs with a username or password can't be used with the helper")
+ }
+
+ if u.Scheme == "http" {
+ spec.Type = "http"
+ } else {
+ return nil, errors.New("unknown scheme")
+ }
+
+ spec.Host, portStr, err = net.SplitHostPort(u.Host)
+ if err != nil {
+ return nil, err
+ }
+ if spec.Host == "" {
+ return nil, errors.New("missing host")
+ }
+ port, err = strconv.ParseUint(portStr, 10, 16)
+ if err != nil {
+ return nil, err
+ }
+ spec.Port = int(port)
+
+ 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) {
@@ -49,6 +100,10 @@ func roundTripWithHelper(buf []byte, info *RequestInfo) (*http.Response, error)
if info.Host != "" {
req.Header["Host"] = info.Host
}
+ req.Proxy, err = makeProxySpec(options.ProxyURL)
+ if err != nil {
+ return nil, err
+ }
encReq, err := json.Marshal(&req)
if err != nil {
return nil, err
diff --git a/meek-client/helper_test.go b/meek-client/helper_test.go
new file mode 100644
index 0000000..4a34e35
--- /dev/null
+++ b/meek-client/helper_test.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "net/url"
+ "testing"
+)
+
+func TestMakeProxySpec(t *testing.T) {
+ badTests := [...]url.URL{
+ url.URL{Scheme: "http"},
+ url.URL{Scheme: "http", Host: ":"},
+ url.URL{Scheme: "http", Host: "localhost"},
+ url.URL{Scheme: "http", Host: "localhost:"},
+ url.URL{Scheme: "http", Host: ":8080"},
+ url.URL{Scheme: "http", Host: "localhost:https"},
+ url.URL{Scheme: "http", Host: "localhost:8080", User: url.User("username")},
+ url.URL{Scheme: "http", Host: "localhost:8080", User: url.UserPassword("username", "password")},
+ url.URL{Scheme: "http", User: url.User("username"), Host: "localhost:8080"},
+ url.URL{Scheme: "http", User: url.UserPassword("username", "password"), Host: "localhost:8080"},
+ url.URL{Scheme: "http", Host: "localhost:-1"},
+ url.URL{Scheme: "http", Host: "localhost:65536"},
+ url.URL{Scheme: "unknown", Host: "localhost:9999"},
+ }
+ goodTests := [...]struct {
+ input url.URL
+ expected ProxySpec
+ }{
+ {
+ url.URL{Scheme: "http", Host: "localhost:8080"},
+ ProxySpec{"http", "localhost", 8080},
+ },
+ }
+
+ for _, input := range badTests {
+ _, err := makeProxySpec(&input)
+ if err == nil {
+ t.Errorf("%q unexpectedly succeeded", input)
+ }
+ }
+
+ for _, test := range goodTests {
+ spec, err := makeProxySpec(&test.input)
+ if err != nil {
+ t.Fatalf("%q unexpectedly returned an error: %s", test.input, err)
+ }
+ if *spec != test.expected {
+ t.Errorf("%q → %q (expected %q)", test.input, spec, test.expected)
+ }
+ }
+}
diff --git a/meek-client/meek-client.go b/meek-client/meek-client.go
index 5f7228e..72e379f 100644
--- a/meek-client/meek-client.go
+++ b/meek-client/meek-client.go
@@ -320,7 +320,9 @@ func checkProxyURL(u *url.URL) error {
return errors.New(fmt.Sprintf("don't understand proxy URL scheme %q", options.ProxyURL.Scheme))
}
if options.HelperAddr != nil {
- return errors.New("--helper can't be used with an upstream proxy")
+ if options.ProxyURL.User != nil {
+ return errors.New("a proxy URL with a username or password can't be used with --helper")
+ }
}
return nil
}