-
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: http fetch tunnel over webrtc datachannel
- Loading branch information
Showing
5 changed files
with
196 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/** | ||
* Emulates HTML Fetch API over peer-to-peer | ||
* DataConnection (WebRTC DataChannel) | ||
*/ | ||
export class PeerFetch { | ||
constructor (dataConnection) { | ||
// the DataConnection that PeerFetch rides on | ||
this._dataConnection = dataConnection | ||
// Map of pending requests awaiting responses | ||
this._requestMap = new Map() | ||
// incrementing counter to the next available | ||
// unused ticket number | ||
// Each request is assigned a ticket | ||
// which can be used to claim the response | ||
this._nextAvailableTicket = 0 | ||
// points to the next ticket assigned to a pending request | ||
// Requests are processed in FIFO order. | ||
this._nextTicketInLine = 0 | ||
this._configureDataConnection() | ||
} | ||
|
||
/** | ||
Return the next available ticket number | ||
and simultaneously increment the ticket counter. | ||
*/ | ||
_drawNewTicket () { | ||
return this._nextAvailableTicket++ | ||
} | ||
|
||
/** | ||
Move on to next pending ticket | ||
*/ | ||
_ticketProcessed (ticket) { | ||
const errorMsg = 'response received out of order!' | ||
const nextTicket = this._nextTicketInLine | ||
console.assert( | ||
ticket === nextTicket, | ||
{ ticket, nextTicket, errorMsg } | ||
) | ||
// remove entry from pending request map | ||
this._requestMap.delete(ticket) | ||
this._nextTicketInLine++ | ||
} | ||
|
||
_configureDataConnection () { | ||
// Handle incoming data (messages only since this is the signal sender) | ||
const peerFetch = this | ||
this._dataConnection.on('data', function (data) { | ||
console.debug('Remote Peer Data message received (type %s): %s', | ||
typeof (data), data) | ||
// we expect data to be a response to a previously sent request message | ||
const response = data | ||
const ticket = peerFetch._nextTicketInLine | ||
console.debug(peerFetch, peerFetch._requestMap, ticket, response) | ||
// update request map entry with this response | ||
const pair = peerFetch._requestMap.get(ticket) | ||
pair.response = response | ||
}) | ||
} | ||
|
||
/** | ||
* REST API over HTTP GET | ||
*/ | ||
async get ({ url = '/', params = {} }) { | ||
console.debug('PeerFetch.get', url, params) | ||
if (params.size > 0) { | ||
var esc = encodeURIComponent | ||
var query = Object.keys(params) | ||
.map(k => esc(k) + '=' + esc(params[k])) | ||
.join('&') | ||
url += '?' + query | ||
} | ||
const request = { | ||
url, | ||
method: 'GET' | ||
} | ||
// get a ticket that matches the request | ||
// and use it to claim the corresponding | ||
// response when availably | ||
const ticket = this._pushRequest(request) | ||
const response = await this._receiveResponse(ticket) | ||
return response | ||
} | ||
|
||
_pushRequest (request) { | ||
const ticket = this._drawNewTicket() | ||
console.debug(this._requestMap) | ||
this._requestMap.set(ticket, { request }) | ||
if (this._requestMap.size === 1) { | ||
// there are no other pending requests | ||
// let's send this one on the wire | ||
this._sendNextRequest(ticket) | ||
} | ||
return ticket | ||
} | ||
|
||
/** | ||
Send next pending request to remote peer. | ||
Requests are sent one at a time. | ||
Only when a previous request response arrives | ||
is the next request sent across the wire. | ||
In the future we can look at handling multiple requests | ||
and responses in parallel over the same data connection or | ||
even a pool of connections. | ||
*/ | ||
_sendNextRequest (ticket) { | ||
const { request } = this._requestMap.get(ticket) | ||
const jsonRequest = JSON.stringify(request) | ||
const requestMap = this._requestMap | ||
console.debug('Sending request to remote peer', | ||
{ requestMap, ticket, request }) | ||
this._dataConnection.send(jsonRequest) | ||
} | ||
|
||
async _receiveResponse (ticket) { | ||
const timeout = 30 * 1000 // 30 seconds | ||
const timerStart = Date.now() | ||
let timeElapsed = timerStart | ||
let request, response | ||
do { | ||
({ request, response } = this._requestMap.get(ticket)) | ||
if (response) { | ||
// if (typeof(response) === 'string') { | ||
this._ticketProcessed(ticket) | ||
console.debug('Received response', { ticket, request, response }) | ||
return response | ||
} | ||
timeElapsed = Date.now() - timeElapsed | ||
await sleep(100) | ||
} while (!response && timeElapsed < timeout) | ||
if (!response) { | ||
throw Error('PeerFetch Timeout while waiting for response.') | ||
} | ||
} | ||
} | ||
|
||
function sleep (ms) { | ||
return new Promise(resolve => setTimeout(resolve, ms)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,3 @@ | ||
/** | ||
Manage plug and play connection to Ambianic Edge. | ||
*/ | ||
|
||
/** | ||
* Local room management for a peer | ||
*/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters