mirror of
https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake.git
synced 2025-10-14 05:11:19 -04:00
Copy appengine directory from meek commit 6057a9e9d6.
This commit is contained in:
parent
afe7716903
commit
a150a991d0
3 changed files with 168 additions and 0 deletions
31
appengine/README
Normal file
31
appengine/README
Normal file
|
@ -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
|
10
appengine/app.yaml
Normal file
10
appengine/app.yaml
Normal file
|
@ -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
|
127
appengine/reflect.go
Normal file
127
appengine/reflect.go
Normal file
|
@ -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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue