This is a web browser extension that demonstrates how an external
program can use a browser as an instrument for making HTTP requests.
Conceptually, it exposes a JSON-based interface to the fetch API
(https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) on a
localhost socket.

An in-browser JavaScript extension cannot open a socket by itself, so
the extension is in two parts: the browser part and the native part. The
native part opens a socket and relays messages to and from the browser.
The browser part is what actually does the fetch. The two parts
communicate with each other using the native messaging API
(https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging),
which requires some manual setup additional to installing the extension
into the browser (see instructions below).

Once you have it all installed, you can send JSON-encoded fetch
parameters to 127.0.0.1:9901 and see the responses. See below for more
usage examples.

	$ echo '{"input": "https://example.com/"}' | nc 127.0.0.1 9901
	{"response":{"body":"PCFk...Cg==","headers":{},"ok":true,"redirected":false,"status":200,"statusText":"OK","type":"basic","url":"https://example.com/"}}

David Fifield <david@bamsoftware.com>
Public domain


== How to build the extension ==

Native part: enter the native/ directory and run "go build".

Browser part: enter the browser/ directory and run "make". This will
produce a "packed" xpi extension. You can alternatively "Load Temporary
Add-On" in Firefox or "Load unpacked" in Chromium, and then you don't
need this step.


== How to install in Firefox ==

To make the native messaging work, you have to install an "app manifest"
that points to the native executable. The file fetch.rpc.json is a
sample. Edit the file and change the "path" property to the absolute
path to the native executable:
	"path": "/path/to/fetch-rpc/native/native",
For more information:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#App_manifest

Then, copy fetch.rpc.json to an OS-specific location, creating the
directory if necessary.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_manifests#Manifest_location
macOS:
	~/Library/Application Support/Mozilla/NativeMessagingHosts/
other Unix:
	~/.mozilla/native-messaging-hosts/
Windows:
	On Windows you don't have to copy fetch.rpc.json anywhere. Open
	regedit.exe and create a new registry key:
		HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\fetch.rpc
	Set the default value to the absolute path to fetch.rpc:
		C:\path\to\fetch-rpc\fetch.rpc

There are two ways to install the extension. The first is to go to
about:debugging and click the "Load Temporary Add-On" button. Browse to
the browser/manifest.json file and click "Open".
https://developer.mozilla.org/en-US/docs/Tools/about:debugging#Loading_a_temporary_add-on

The second way is to install an unsigned xpi file. For this way to work,
you need to be using Firefox Developer Edition
(https://www.mozilla.org/en-US/firefox/developer/) because the standard
edition doesn't allow installing unsigned extensions. You will
additionally need to go to about:config and set the preference
	xpinstall.signatures.required=false
To load the extension, go to about:addons, click the gear icon, then
"Install Add-On From File...". Browse to the browser/fetch-rpc.xpi file
you built earlier.

Open the browser console (Ctrl+Shift+J) to see debugging output. You can
run Firefox without a UI by providing the --headless option on the
command line.


== How to install in Chromium ==

Chromium also requires editing the fetch.rpc app manifest file, but it
needs an extension ID that we don't know until after loading the
extension once. Go to chrome://extensions and turn on Developer mode.
Click the "Load unpacked" button and browse to the browser/ directory.
https://developer.chrome.com/extensions/getstarted#manifest

A listing for "fetch RPC" will appear with an ID string like
"aaaabbbbccccddddeeeeffffgggghhhh". Edit fetch.rpc.json and change the
"path" to point to the native executable:
	"path": "/path/to/fetch-rpc/native/native",
Also change the "allowed_extensions" property to "allowed_origins" and
change the value to "chrome-extension://aaaabbbbccccddddeeeeffffgggghhhh/"
(using the ID shown on the chrome://extensions page):
	"allowed_origins": [
		"chrome-extension://aaaabbbbccccddddeeeeffffgggghhhh/"
	]

Then, copy fetch.rpc.json to an OS-specific location, creating the
directory if necessary. The paths differ depending on whether you are
using Chromium or Chrome.
https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-location
macOS:
	~/Library/Application Support/Chromium/NativeMessagingHosts/
	~/Library/Application Support/Google/Chrome/NativeMessagingHosts/
other Unix:
	~/.config/chromium/NativeMessagingHosts/
	~/.config/google-chrome/NativeMessagingHosts/
Windows:
	On Windows you don't have to copy fetch.rpc.json anywhere. Open
	regedit.exe and create a new registry key:
		HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\fetch.rpc
	Set the default value to the absolute path to fetch.rpc:
		C:\path\to\fetch-rpc\fetch.rpc

Now click the arrow to reload the extension.

For debugging, click the "background page" link on chrome://extensions
to expose a console output. For more intensive debugging, enable stderr
logging output: https://www.chromium.org/for-testers/enable-logging.


== Examples ==

The interface is designed to map to the fetch API. See:
https://developer.mozilla.org/en-US/docs/Web/API/Request/Request#Parameters
https://developer.mozilla.org/en-US/docs/Web/API/Response#Properties

A request consists of an "input" URL string and an optional "init" object
with additional options. If provided, the request.init.body and response.body
properties are base64 encoded.

A simple request:
	{
	  "input": "https://example.com/"
	}
The corresponding response:
	{
	  "response": {
	    "body": "PCFk...Cg==",
	    "headers": {},
	    "ok": true,
	    "redirected": false,
	    "status": 200,
	    "statusText": "OK",
	    "type": "basic",
	    "url": "https://example.com/"
	  }
	}

A request that results in an error:
	{
	  "input": "https://example.com:999/"
	}
The error result:
	{
	  "error": {
	    "message": "NetworkError when attempting to fetch resource.",
	    "name": "TypeError"
	  }
	}

Overriding headers and other options (note that overriding the Host
header works on Firefox but not on Chrome):
	{
	  "input": "https://example.com/",
	  "init": {
	    "cache": "no-cache",
	    "headers": {
	      "Host": "example.org"
	    }
	  }
	}

Sending a POST with a body:
	{
	  "input": "https://example.com/",
	  "init": {
	    "method": "POST",
	    "body": "SGVsbG8gd29ybGQ="
	  }
	}
