🍨 Implementation-agnostic universal browser dialer. Source of name here.
Warning
To run as efficiently as possible, Appat requires a JS runtime supporting both
WebSocketStreamandReadableStreamin request bodies (e.g. Chromium 124+, Deno unstable).If you want to have a smooth experience for browser dialers on Firefox, please consider upvoting here and expressing yourself in this Firefox issue. This will help high-throughput data processing in general.
{
"m": "PUT",
"u": "https://relay.example.com/madi",
"c": "00000000-0000-0000-0000-000000000000",
"e": {
"p": "WW914oCZcmUgc3dlZXQgb24gdGhlIHBvbnksIGFyZW7igJl0IHlvdT8KVGhhdOKAmXMgaW1wb3NzaWJsZS4gV2XigJlyZSBmcm9tIGRpZmZlcmVudCB3b3JsZHMu",
"r": "https://relay.example.com/dairy",
"h": {
"Accept": "application/xml",
"Authorization": "Bearer RXZlbiB0aG91Z2ggeW914oCZcmUgZ29pbmcgdG8gd2lwZSBteSBtZW1vcmllcywgSeKAmWQgbG92ZSB0byBoZWFyIGFib3V0IHlvdSBncm93aW5nIHVwLiBNYXliZSBtYWtlIHlvdSBsYXVnaCBhZ2Fpbi4"
}
}
}m: Request method. One ofWS,WT,GET,POST,PUTand more.u: Destination URL. Must begin with one ofws://andwss://underWS, andhttp://orhttps://if otherwise.c: Connection UUID. This is set to distinguish relayed connections apart. Each relayed connection will have a different UUID.e: Extra info. This is an optional field.p: Only valid for WebSockets. WebSocket subprotocol, effectively the value of theSec-WebSocket-Protocolheader. This field is currently mostly repurposed to handle early payload data encoded in URL-safe Base64.r: Only valid for non-WebSockets for browsers. Value of theRefererheader. This field is currently mostly repurposed to handle early payload data encoded in URL-safe Base64. The value of this field can change in the dialer.h: Only valid for non-WebSockets for browsers. The map of all of the header key-value pairs to send on request initiation. Should not be one of the forbidden headers in browsers.
{
"m": "APPAT",
"u": "https://relay.example.com/madi",
"c": "00000000-0000-0000-0000-000000000000",
"e": {
"appat": "requestEnd"
}
}When method is set to APPAT, the control message instead signals on how to deal with existing connections, as such c should match one of the existing connections.
e.appat can take one of the following values.
requestEnd: Marks the uploading stream of an existing HTTP request as ended. Beware of racing conditions on the controller side, and remember to always apply backpressure.
Only viable for normal HTTP requests.
{
"quota": 255,
"c": "00000000-0000-0000-0000-000000000000",
"s": 200,
"t": "OK",
"e": "",
"h": {
"Content-Type": "application/octet-stream"
}
}quota: Number of connections still able to be accepted.c: Connection UUID. The same UUID assigned by the dialer controller.s: Numeric status.t: Status text.e: Optional field. Whensis set to0(raw error) or1(Appat status), this field will contain detailed info on the error.appat.halfDuplex: Marks the client runtime as having no support for full-duplex web requests.appat.cidCollision: Marks the disconnecting/disconnected/connecting data socket as failed due to having a connection ID collision.
h: A list of response headers.
- The browser visits the page served by the dialer controller (e.g.
127.0.0.1:5779), and generates a random "page UUID" (e.g.krsw). Thetokenquery parameter or the second command line argument is taken as the CSRF token if it's unset. - Dialer connects to the control socket (e.g.
ws://127.0.0.1:5779/krsw/ctrl?token=ookoe) to receive commands. - The data sockets are located under the page UUID (e.g.
ws://127.0.0.1:5779/krsw/00000000-0000-0000-0000-000000000000?token=ookoe). Upon receiving commands to relay connections...
- For
WSconnections, the browser dialer simply forwards messages back and forth without any processing. - For
GETrequests, the browser dialer simply connects to the data socket, then forwards the response body to it once ready. The dialer will close the connection once the response stream closes. - For other requests, the browser dialer first connects to the data socket, then initiates a bidirectional
fetchforwarding with passthrough.
This section is strictly for this reference implementation.
| Protocol | Supported? |
|---|---|
WS |
Yes |
WT |
No |
GET |
Yes |
POST etc. |
Yes |
Most, but not all. gRPC and GET requests with bodies are examples that cannot be tunneled via Appat or any other browser dialers. However, we encourage any project to build a headless dialer contemplating this.
Depending on the actual runtime used with the dialer. If in a browser, none of the current browser supports fully duplex connections. If in headless runtimes like Deno however, it is fully duplex.
- For Chromium: Use
--disable-web-security. A temporary data directory is also required with--user-data-dir=tmpData. Perfect for headless use as well. Example:chromium --disable-web-security --user-data-dir=tmpData "http://127.0.0.1:5779/" - For Firefox: Set
security.fileuri.strict_origin_policytofalse(headless friendly), or install the "CORS Everywhere" extension. - For Deno: Just go nuts!
Of course! Apart from Deno support, Appat targets headless mode specifically.
- For Chromium:
--headless. Example:chromium --headless "http://127.0.0.1:5779/" - For Firefox:
-headless. Example:firefox -headless "http://127.0.0.1:5779/" - For Deno: Run it as-is, support available built-in.