Skip to content

Commit

Permalink
implement dxb over http, goodbye messages, improve endpoint online de…
Browse files Browse the repository at this point in the history
…tection
  • Loading branch information
benStre committed Nov 11, 2023
1 parent 9f66c15 commit 2cee076
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 5 deletions.
2 changes: 1 addition & 1 deletion compiler/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class DatexResponse<T> {


export const ProtocolDataTypesMap = [
"REQUEST", "RESPONSE", "DATA", "TMP_SCOPE", "LOCAL", "HELLO", "DEBUGGER", "SOURCE_MAP", "UPDATE"
"REQUEST", "RESPONSE", "DATA", "TMP_SCOPE", "LOCAL", "HELLO", "DEBUGGER", "SOURCE_MAP", "UPDATE", "GOODBYE"
]


Expand Down
1 change: 1 addition & 0 deletions compiler/protocol_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export enum ProtocolDataType {
DEBUGGER = 6, // get a debugger for a scope
SOURCE_MAP = 7, // send a source map for a scope
UPDATE = 8, // like normal request, but don't propgate updated pointer values back to sender (prevent recursive loop)
GOODBYE = 9, // info message that endpoint is offline
}
25 changes: 25 additions & 0 deletions network/datex-http-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Send DATEX blocks to a server with a HTTPComInterface
* (listening to POST requests on /datex-http).
* No response is returned.
*
* This is not the same as datex-over-http implemented in UIX
* that send unsigned/unencrypted DATEX scripts to an HTTP server
*/
export function sendDatexViaHTTPChannel(dxb: ArrayBuffer, origin = window.location.origin) {

// fallback to sendBeacon for firefox until fetch keepalive implemented
if (navigator.userAgent.includes("Firefox/")) {
navigator.sendBeacon(origin + "/datex-http", dxb);
}
else {
fetch(origin + "/datex-http", {
method:'post',
headers:{
'Content-Type': 'application/datex',
},
body: dxb,
keepalive: true
})
}
}
7 changes: 7 additions & 0 deletions network/supranet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { endpoint_config } from "../runtime/endpoint_config.ts";
import { endpoint_name, UnresolvedEndpointProperty } from "../datex_all.ts";
import { Datex } from "../mod.ts";
import { Storage } from "../runtime/storage.ts";
import { sendDatexViaHTTPChannel } from "./datex-http-channel.ts";
const logger = new Logger("DATEX Supranet");

// entry point to connect to the datex network
Expand Down Expand Up @@ -152,6 +153,12 @@ export class Supranet {
// TODO: (does not work because response never reaches endpoint if valid endpoint already exists in network)
// Crypto.validateOwnKeysAgainstNetwork();

// send goodbye on process close
const byeDatex = <ArrayBuffer> await Datex.Compiler.compile("", [], {type:Datex.ProtocolDataType.GOODBYE, sign:true, flood:true, __routing_ttl:10})
globalThis.addEventListener("beforeunload", () => {
sendDatexViaHTTPChannel(byeDatex);
})

return connected;
}

Expand Down
34 changes: 30 additions & 4 deletions runtime/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,10 @@ export class Runtime {
console.log("Scope error occured, cannot get the original error here!");
return;
}

// assume sender endpoint is online now
if (header.sender && header.signed) header.sender.setOnline(true)

// return error to sender (if request)
if (header.type == ProtocolDataType.REQUEST) {
// is not a DatexError -> convert to DatexError
Expand Down Expand Up @@ -1839,6 +1843,9 @@ export class Runtime {

const unique_sid = header.sid+"-"+header.return_index;

// assume sender endpoint is online now
if (header.sender && header.signed) header.sender.setOnline(true)

// return global result to sender (if request)
if (header.type == ProtocolDataType.REQUEST) {
this.datexOut(["?", [return_value], {type:ProtocolDataType.RESPONSE, to:header.sender, return_index:header.return_index, encrypt:header.encrypted, sign:header.signed}], header.sender, header.sid, false);
Expand Down Expand Up @@ -1871,19 +1878,38 @@ export class Runtime {

// hello (also temp: get public keys)
else if (header.type == ProtocolDataType.HELLO) {
if (return_value) {
if (!header.sender) logger.error("Invalid HELLO message, no sender");
else if (return_value) {
try {
let keys_updated = await Crypto.bindKeys(header.sender, ...<[ArrayBuffer,ArrayBuffer]>return_value);
const keys_updated = await Crypto.bindKeys(header.sender, ...<[ArrayBuffer,ArrayBuffer]>return_value);
if (header.routing?.ttl)
header.routing.ttl--;

// set online to true (even if HELLO message not signed)
header.sender.setOnline(true)
logger.debug("HELLO ("+header.sid+"/" + header.inc+ "/" + header.return_index + ") from " + header.sender + ", keys "+(keys_updated?"":"not ")+"updated, ttl = " + header.routing?.ttl);
}
catch (e) {
logger.error("Invalid HELLO keys");
}
}
else logger.debug("HELLO from " + header.sender + ", no keys, ttl = " + header.routing?.ttl);
else {
// set online to true (even if HELLO message not signed)
header.sender.setOnline(true)
logger.debug("HELLO from " + header.sender + ", no keys, ttl = " + header.routing?.ttl);
}
}

else if (header.type == ProtocolDataType.GOODBYE) {
if (header.signed && header.sender) {
logger.debug("GOODBYE from " + header.sender)
header.sender.setOnline(false)
}
else {
logger.error("ignoring unsigned GOODBYE message")
}
}


else if (header.type == ProtocolDataType.DEBUGGER) {
logger.success("DEBUGGER ?", return_value)
Expand Down Expand Up @@ -5015,7 +5041,7 @@ export class Runtime {
catch {}
}).filter(o => o && !(o.equals(Runtime.endpoint) || o.equals(SCOPE.sender))))
];
if (origins.length > 2) {
if (origins.length > 1) {
logger.debug("pre-fetching online state for "+ origins.length + " endpoints")
await Promise.race([
Promise.all(origins.map(origin => origin.isOnline())),
Expand Down
11 changes: 11 additions & 0 deletions types/addressing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,17 @@ export class Endpoint extends Target {
this.#onlinePointer.onGargabeCollection(()=> clearInterval(interval));
return this.#onlinePointer;
}

/**
* Override online state (e.g. when retrieving a GOODBYE or other message)
* @param online
*/
public setOnline(online = true) {
this.#online = new Promise(resolve => resolve(online));
this.#current_online = online;
if (this.#onlinePointer) this.#onlinePointer.val = online;
}

// returns (cached) online status
public async isOnline(): Promise<boolean> {
if (Runtime.endpoint.equals(this) || Runtime.main_node?.equals(this) || this as Endpoint === LOCAL_ENDPOINT) return true; // is own endpoint or main node
Expand Down
1 change: 1 addition & 0 deletions utils/debug-cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function debugMode(enable = true) {
console.log("[debug mode " + (enable ? "enabled" : "disabled") + "]");
if (enable) setCookie("datex-debug", "true")
else setCookie("datex-debug", "");
window.location.reload()
}


Expand Down

0 comments on commit 2cee076

Please sign in to comment.