[tor-commits] [snowflake/master] Copy appengine directory from meek commit 6057a9e9d6.

dcf at torproject.org dcf at torproject.org
Thu Aug 3 02:35:44 UTC 2017


commit a150a991d0c2ba66ca9e567bd088c603c9434775
Author: David Fifield <david at bamsoftware.com>
Date:   Sat Jul 15 11:48:28 2017 -0700

    Copy appengine directory from meek commit 6057a9e9d6.
---
 appengine/README     |  31 +++++++++++++
 appengine/app.yaml   |  10 ++++
 appengine/reflect.go | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+)

diff --git a/appengine/README b/appengine/README
new file mode 100644
index 0000000..d71277a
--- /dev/null
+++ b/appengine/README
@@ -0,0 +1,31 @@
+This component runs on Google App Engine. It lies between meek-client
+and meek-server. The App Engine component receives requests from the
+client and forwards them to the server, then receives responses from the
+server and forwards them to the client.
+
+You need the Go App Engine SDK in order to deploy the app.
+	https://cloud.google.com/sdk/docs/#linux
+After unpacking, install the app-engine-go component:
+	google-cloud-sdk/bin/gcloud components install app-engine-go
+
+To test locally, run
+	google-cloud-sdk/bin/dev_appserver.py app.yaml
+The app will be running at http://127.0.0.1:8080/. You can test broker
+forwarding function by browsing to http://127.0.0.1:8000/ip.
+
+To deploy to App Engine, first create a new project and app. You have to
+think of a unique name (marked as "<appname>" in the commands). You only
+have to do the "create" step once; subsequent times you can go straight
+to the "deploy" step. This command will open a browser window so you can
+log in to a Google account.
+	google-cloud-sdk/bin/gcloud projects create <appname>
+	google-cloud-sdk/bin/gcloud app create --project=<appname>
+Then to deploy the project, run:
+	google-cloud-sdk/bin/gcloud app deploy --project=<appname>
+
+To configure meek-client to talk to the App Engine app, provide
+"https://<appname>.appspot.com/" as the url and "www.google.com" as the
+front domain.
+	UseBridges 1
+	Bridge meek 0.0.2.0:1 url=https://example.appspot.com/ front=www.google.com
+	ClientTransportPlugin meek exec ./meek-client --log meek-client.log
diff --git a/appengine/app.yaml b/appengine/app.yaml
new file mode 100644
index 0000000..ff6efc1
--- /dev/null
+++ b/appengine/app.yaml
@@ -0,0 +1,10 @@
+runtime: go
+api_version: go1
+automatic_scaling:
+  max_idle_instances: 2
+  min_pending_latency: 1000ms
+
+handlers:
+- url: /.*
+  script: _go_app
+  secure: always
diff --git a/appengine/reflect.go b/appengine/reflect.go
new file mode 100644
index 0000000..ccf11f3
--- /dev/null
+++ b/appengine/reflect.go
@@ -0,0 +1,127 @@
+// A web app for Google App Engine that proxies HTTP requests and responses to a
+// Tor relay running meek-server.
+package reflect
+
+import (
+	"io"
+	"net"
+	"net/http"
+	"net/url"
+	"time"
+
+	"appengine"
+	"appengine/urlfetch"
+)
+
+const (
+	forwardURL = "https://meek.bamsoftware.com/"
+	// A timeout of 0 means to use the App Engine default (5 seconds).
+	urlFetchTimeout = 20 * time.Second
+)
+
+var context appengine.Context
+
+// Join two URL paths.
+func pathJoin(a, b string) string {
+	if len(a) > 0 && a[len(a)-1] == '/' {
+		a = a[:len(a)-1]
+	}
+	if len(b) == 0 || b[0] != '/' {
+		b = "/" + b
+	}
+	return a + b
+}
+
+// We reflect only a whitelisted set of header fields. In requests, the full
+// list includes things like User-Agent and X-Appengine-Country that the Tor
+// bridge doesn't need to know. In responses, there may be things like
+// Transfer-Encoding that interfere with App Engine's own hop-by-hop headers.
+var reflectedHeaderFields = []string{
+	"Content-Type",
+	"X-Session-Id",
+}
+
+// Get the original client IP address as a string. When using the standard
+// net/http server, Request.RemoteAddr is a "host:port" string; however App
+// Engine seems to use just "host". We check for both to be safe.
+func getClientAddr(r *http.Request) string {
+	host, _, err := net.SplitHostPort(r.RemoteAddr)
+	if err == nil {
+		return host
+	}
+	return r.RemoteAddr
+}
+
+// Make a copy of r, with the URL being changed to be relative to forwardURL,
+// and including only the headers in reflectedHeaderFields.
+func copyRequest(r *http.Request) (*http.Request, error) {
+	u, err := url.Parse(forwardURL)
+	if err != nil {
+		return nil, err
+	}
+	// Append the requested path to the path in forwardURL, so that
+	// forwardURL can be something like "https://example.com/reflect".
+	u.Path = pathJoin(u.Path, r.URL.Path)
+	c, err := http.NewRequest(r.Method, u.String(), r.Body)
+	if err != nil {
+		return nil, err
+	}
+	for _, key := range reflectedHeaderFields {
+		values, ok := r.Header[key]
+		if ok {
+			for _, value := range values {
+				c.Header.Add(key, value)
+			}
+		}
+	}
+	// Set the original client IP address in a Meek-IP header. We would use
+	// X-Forwarded-For, but App Engine prohibits setting that header:
+	// https://cloud.google.com/appengine/docs/standard/go/outbound-requests#request_headers
+	// We could use Forwarded from RFC 7239, but other CDNs already use
+	// X-Forwarded-For and this way we only need one parser.
+	c.Header.Add("Meek-IP", getClientAddr(r))
+	return c, nil
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	context = appengine.NewContext(r)
+	fr, err := copyRequest(r)
+	if err != nil {
+		context.Errorf("copyRequest: %s", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	// Use urlfetch.Transport directly instead of urlfetch.Client because we
+	// want only a single HTTP transaction, not following redirects.
+	transport := urlfetch.Transport{
+		Context: context,
+		// Despite the name, Transport.Deadline is really a timeout and
+		// not an absolute deadline as used in the net package. In
+		// other words it is a time.Duration, not a time.Time.
+		Deadline: urlFetchTimeout,
+	}
+	resp, err := transport.RoundTrip(fr)
+	if err != nil {
+		context.Errorf("RoundTrip: %s", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	defer resp.Body.Close()
+	for _, key := range reflectedHeaderFields {
+		values, ok := resp.Header[key]
+		if ok {
+			for _, value := range values {
+				w.Header().Add(key, value)
+			}
+		}
+	}
+	w.WriteHeader(resp.StatusCode)
+	n, err := io.Copy(w, resp.Body)
+	if err != nil {
+		context.Errorf("io.Copy after %d bytes: %s", n, err)
+	}
+}
+
+func init() {
+	http.HandleFunc("/", handler)
+}





More information about the tor-commits mailing list