Files
nym/clients/webassembly/js-example/index.js
T
2020-05-11 14:52:23 +01:00

185 lines
7.1 KiB
JavaScript

// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import * as wasm from "nym-client-wasm";
import { makeRegisterRequest, makeAuthenticateRequest, makeSendablePacket, getTopology, getInitialGatewayAddress } from "nym-client-wasm/helpers"
class GatewayConnection {
constructor(gateway_url, ownAddress, registeredCallback) {
const conn = new WebSocket(gateway_url);
// lovely bindings here we go!
this.onSocketMessage = this.onSocketMessage.bind(this);
this.handleBlobResponse = this.handleBlobResponse.bind(this);
this.handleRegisterResponse = this.handleRegisterResponse.bind(this);
this.handleAuthenticateResponse = this.handleAuthenticateResponse.bind(this);
this.handleErrorResponse = this.handleErrorResponse.bind(this);
this.handleSendConfirmation = this.handleSendConfirmation.bind(this);
conn.onclose = this.onSocketClose;
conn.onmessage = this.onSocketMessage;
conn.onerror = (ev) => console.error("Socket error: ", ev);
conn.onopen = this.onSocketOpen.bind(this);
this.ownAddress = ownAddress;
this.conn = conn;
this.registeredCallback = registeredCallback
}
closeConnection() {
this.conn.close();
}
sendMessage(topology, message, recipient) {
let gatewaySphinxPacket = makeSendablePacket(topology, message, recipient);
this.conn.send(gatewaySphinxPacket)
}
sendRegisterRequest() {
const registerReq = makeRegisterRequest(this.ownAddress);
this.conn.send(registerReq);
}
sendAuthenticateRequest(token) {
const authenticateReq = makeAuthenticateRequest(this.ownAddress, token);
this.conn.send(authenticateReq);
}
onSocketOpen() {
console.log("opened socket connection!")
// the first message you should send is either register or authenticate, otherwise your sphinx packets
// are not going to be accepted (nor you will receive any)
this.sendRegisterRequest()
}
onSocketClose(ev) {
console.log("The websocket was closed", ev);
}
handleSendConfirmation(additionalData) {
console.log("received send confirmation", additionalData);
}
handleRegisterResponse(additionalData) {
console.log("registered! - ", additionalData);
this.registeredCallback();
}
handleAuthenticateResponse(additionalData) {
console.log("authenticated! - ", additionalData);
}
handleErrorResponse(additionalData) {
console.error("received error response: ", additionalData)
}
handleBlobResponse(data) {
// note that the actual handling depends on the expected content, however,
// in this example we're just sending a text messages to ourselves and hence
// we can safely read it as text
let reader = new FileReader();
reader.onload = () => {
displayReceived(reader.result)
};
reader.readAsText(data);
}
onSocketMessage(ev) {
if (ev.data instanceof Blob) {
this.handleBlobResponse(ev.data)
} else {
const receivedData = JSON.parse(ev.data);
switch (receivedData.type) {
case "send": return this.handleSendConfirmation(receivedData);
case "register": return this.handleRegisterResponse(receivedData);
case "authenticate": return this.handleAuthenticateResponse(receivedData);
case "error": return this.handleErrorResponse(receivedData);
}
console.log("Received unknown response!");
console.log(receivedData);
}
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main() {
var directoryUrl = "http://127.0.0.1:8080/api/presence/topology";
const gatewayUrl = await getInitialGatewayAddress(directoryUrl);
if (gatewayUrl == "") {
alert("No gateways detected - cannot procede");
return;
}
// NOTE: in an actual application, you should save those keys somewhere and only generate them a single time
// then reuse them in subsequent runs
const keypair_address = JSON.parse(wasm.keygen());
console.log("our keypair and address are: ", keypair_address);
document.getElementById("recipient").value = keypair_address.address
document.querySelector('#send-button').disabled = true;
// basically to ensure connection is established before we do anything else
// and more importantly to ensure we are registered so that we could send messages to ourselves
// (i.e. we exist in the topology we're about to pull
let registered_callback = async () => {
// this function is called immediately after we receive registration response
// however, gateways are sending their presence data every 1.5s, so it might take at most 1.5s before
// our address is present in the topology, so just wait that long
// (JS to DH: this is done so that we could have multiple gateways online even for the wasm client)
await sleep(1500);
// Get the topology, then the mixnode and provider data
const topology = await getTopology(directoryUrl);
// Set up the send button
const string_topology = JSON.stringify(topology);
const sendButton = document.querySelector('#send-button');
sendButton.disabled = false;
sendButton.onclick = function () {
sendMessageToMixnet(conn, string_topology);
}
}
// Set up a websocket connection to the gateway node
let conn = new GatewayConnection(gatewayUrl, keypair_address.address, registered_callback);
}
// Create a Sphinx packet and send it to the mixnet through the Gateway node.
function sendMessageToMixnet(connection, topology) {
var recipient = document.getElementById("recipient").value;
var sendText = document.getElementById("sendtext").value;
connection.sendMessage(topology, sendText, recipient);
displaySend(sendText);
}
function displaySend(message) {
let timestamp = new Date().toISOString().substr(11, 12);
document.getElementById("output").innerHTML = document.getElementById("output").innerHTML + "<p style='color: blue; word-break: break-all;'>" + timestamp + " <b>sent</b> >>> " + message + "</p >";
}
function displayReceived(message) {
let timestamp = new Date().toISOString().substr(11, 12);
document.getElementById("output").innerHTML = document.getElementById("output").innerHTML + "<p style='color: green; word-break: break-all;'>" + timestamp + " <b>received</b> >>> " + message + "</p >";
}
// Let's get started!
main();