[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [meek/master] Allow specifying a proxy.
commit 20af52a95d2d07ec43dc2299e1c064fb7c632051
Author: David Fifield <david@xxxxxxxxxxxxxxx>
Date: Tue Feb 19 00:44:15 2019 -0700
Allow specifying a proxy.
Just like with headers, we can only control the proxy through a global
event listener, namely proxy.onRequest. We use the same scheme of
locking modifications to the events so that only one request at a time
is affected.
---
webextension/background.js | 68 ++++++++++++++++++++++++++++++++++++++++-----
webextension/manifest.json | 1 +
webextension/native/main.go | 2 +-
3 files changed, 63 insertions(+), 8 deletions(-)
diff --git a/webextension/background.js b/webextension/background.js
index 5aab472..3b24a37 100644
--- a/webextension/background.js
+++ b/webextension/background.js
@@ -77,6 +77,31 @@ function base64_encode(dec_buf) {
return btoa(dec_str);
}
+// Return a proxy.ProxyInfo according to the given specification.
+//
+// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo
+// The specification may look like:
+// undefined
+// {"type": "http", "host": "example.com", "port": 8080}
+// {"type": "socks5", "host": "example.com", "port": 1080}
+// {"type": "socks4a", "host": "example.com", "port": 1080}
+function makeProxyInfo(spec) {
+ if (spec == null) {
+ return {type: "direct"};
+ }
+ switch (spec.type) {
+ case "http":
+ return {type: "http", host: spec.host, port: spec.port};
+ // What tor calls "socks5", WebExtension calls "socks".
+ case "socks5":
+ return {type: "socks", host: spec.host, port: spec.port, proxyDNS: true};
+ // What tor calls "socks4a", WebExtension calls "socks4".
+ case "socks4a":
+ return {type: "socks4", host: spec.host, port: spec.port, proxyDNS: true};
+ };
+ throw new Error(`unknown proxy type ${spec.type}`);
+}
+
// A Mutex's lock function returns a promise that resolves to a function which,
// when called, allows the next call to lock to proceed.
// https://stackoverflow.com/a/51086893
@@ -95,8 +120,9 @@ function Mutex() {
}
}
-// Enforces exclusive access to onBeforeSendHeaders listeners.
+// Enforce exclusive access to onBeforeSendHeaders and onRequest listeners.
const headersMutex = new Mutex();
+const proxyMutex = new Mutex();
async function roundtrip(request) {
// Process the incoming request spec and convert it into parameters to the
@@ -132,8 +158,6 @@ async function roundtrip(request) {
// Don't follow redirects (we'll get resp.status:0 if there is one).
init.redirect = "manual";
- // TODO: proxy
-
// We need to use a webRequest.onBeforeSendHeaders listener to override
// certain header fields, including Host (passing them to fetch in
// init.headers does not work). But onBeforeSendHeaders is a global setting
@@ -173,6 +197,27 @@ async function roundtrip(request) {
}
}
+ // Similarly, for controlling the proxy for each request, we set a
+ // proxy.onRequest listener, use it for one request, then remove it.
+ let proxyUnlock = await proxyMutex.lock();
+ let proxyCalled = false;
+ // async to make exceptions visible to proxy.onError.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1528873#c1
+ async function proxyFn(details) {
+ try {
+ // Sanity assertion: per-request listeners are called at most once.
+ if (proxyCalled) {
+ throw new Error("proxyFn called more than once");
+ }
+ proxyCalled = true;
+
+ return makeProxyInfo(request.proxy);
+ } finally {
+ browser.proxy.onRequest.removeListener(proxyFn);
+ proxyUnlock();
+ }
+ }
+
try {
// Set our listener that overrides the headers for this request.
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeSendHeaders
@@ -181,18 +226,27 @@ async function roundtrip(request) {
{urls: ["http://*/*", "https://*/*"]},
["blocking", "requestHeaders"]
);
+ // Set our listener that overrides the proxy for this request.
+ // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/onRequest
+ browser.proxy.onRequest.addListener(
+ proxyFn,
+ {urls: ["http://*/*", "https://*/*"]}
+ );
// Now actually do the request and build a response object.
let resp = await fetch(url, init);
let body = await resp.arrayBuffer();
return {status: resp.status, body: base64_encode(body)};
} finally {
- // With certain errors (e.g. an invalid URL), the onBeforeSendHeaders
- // listener may never get called, and therefore never release its lock.
- // Ensure that locks are released and listeners removed in any case.
- // It's safe to release a lock or remove a listener more than once.
+ // With certain errors (e.g. an invalid URL), our onBeforeSendHeaders
+ // and onRequest listeners may never get called, and therefore never
+ // release their locks. Ensure that locks are released and listeners
+ // removed in any case. It's safe to release a lock or remove a listener
+ // more than once.
browser.webRequest.onBeforeSendHeaders.removeListener(headersFn);
headersUnlock();
+ browser.proxy.onRequest.removeListener(proxyFn);
+ proxyUnlock();
}
}
diff --git a/webextension/manifest.json b/webextension/manifest.json
index a079833..fc56723 100644
--- a/webextension/manifest.json
+++ b/webextension/manifest.json
@@ -16,6 +16,7 @@
"permissions": [
"nativeMessaging",
+ "proxy",
"webRequest",
"webRequestBlocking",
"https://*/*",
diff --git a/webextension/native/main.go b/webextension/native/main.go
index 25fdab3..5d071b3 100644
--- a/webextension/native/main.go
+++ b/webextension/native/main.go
@@ -68,7 +68,7 @@ type requestSpec struct {
type proxySpec struct {
Type string `json:"type"`
Host string `json:"host"`
- Port string `json:"port"`
+ Port int `json:"port"`
}
// A specification of an HTTP request or an error, as sent via the socket to
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits