changed to async

This commit is contained in:
2021-05-24 13:33:46 +02:00
parent 0601f52661
commit eea681d435
3 changed files with 130 additions and 73 deletions

View File

@ -40,7 +40,6 @@ export class App {
} }
handleHash(hash) { handleHash(hash) {
console.log(hash);
if (hash[0] === '#') hash = hash.substr(1); if (hash[0] === '#') hash = hash.substr(1);
let defaultCase = () => { let defaultCase = () => {
@ -81,19 +80,30 @@ export class App {
//this.setHash(hash); //this.setHash(hash);
} }
login(accountName, domainName, password) { async login(accountName, domainName, password) {
return USIMP.Domain.fromName(domainName).then(domain => { let domain;
try {
domain = await USIMP.Domain.fromName(domainName);
} catch (error) {
if (error instanceof DOMException) {
throw Error("Connection timed out");
} else {
throw Error("Invalid USIMP domain");
}
}
let session = new USIMP.Session(domain); let session = new USIMP.Session(domain);
session.authenticate(accountName, password) let rtt = await session.chooseDomainServer();
.catch((event) => { console.log(rtt);
console.error(`Unable to reach domain server: ${event}`);
}) let response = await session.authenticate(accountName, password);
.then(() => { if (response.status === "success") {
this.session = session; this.session = session;
this.defaultLocation = "/"; this.defaultLocation = "/";
this.setHash("/"); this.setHash("/");
}); } else {
}); throw Error(response.message);
}
} }
hideMain() { hideMain() {
@ -167,15 +177,19 @@ export class App {
form.appendChild(div); form.appendChild(div);
} }
this.login(form.account.value, form.domain.value, form.password.value) try {
.catch(reason => { await this.login(form.account.value, form.domain.value, form.password.value);
} catch (error) {
if (error.toString() === "Error: unable to authenticate") {
document.getElementsByName("account")[0].setAttribute("invalid", "invalid");
} else {
document.getElementsByName("domain")[0].setAttribute("invalid", "invalid"); document.getElementsByName("domain")[0].setAttribute("invalid", "invalid");
formError("Invalid USIMP domain"); }
console.error(reason); formError(error.toString());
}) console.error(error);
.finally(() => { } finally {
for (let e of form) e.disabled = false; for (let e of form) e.disabled = false;
}); }
}); });
this.windows.appendChild(win); this.windows.appendChild(win);
@ -184,6 +198,10 @@ export class App {
event.target.removeAttribute("invalid"); event.target.removeAttribute("invalid");
}); });
document.getElementsByName("account")[0].addEventListener("input", (event) => {
event.target.removeAttribute("invalid");
});
document.getElementsByName("account")[0].focus(); document.getElementsByName("account")[0].focus();
} }
@ -192,7 +210,7 @@ export class App {
<div class="chat"> <div class="chat">
<div class="chat-history"></div> <div class="chat-history"></div>
<div class="chat-input-wrapper"> <div class="chat-input-wrapper">
<input name="message" type="text"/> <input name="message" type="text" autocomplete="off"/>
</div> </div>
</div>`; </div>`;
@ -208,7 +226,7 @@ export class App {
}); });
this.session.subscribe(response => { this.session.subscribe(response => {
this.addMessage(response.event.data.message); this.addMessage(response.data.event.data.message);
}); });
} }
} }

View File

@ -15,47 +15,51 @@ export class Domain {
this.name = jsonData.name; this.name = jsonData.name;
this.id = jsonData.id; this.id = jsonData.id;
this.servers = []; this.servers = [];
for (let domainServer of domainServers) { for (const domainServer of domainServers) {
this.servers.push(new DomainServer(domainServer)); this.servers.push(new DomainServer(domainServer));
} }
this.invalidServers = []; this.invalidServers = [];
} }
static async fromName(domainName) { static async fromName(domainName) {
return fetch(`https://${domainName}/.well-known/usimp.json`, { const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 2000);
let response = await fetch(`https://${domainName}/.well-known/usimp.json`, {
redirect: "manual", redirect: "manual",
}).then(response => { signal: controller.signal,
if (response.ok) {
return response.json();
} else {
throw "Invalid response";
}
}).then(data => {
return new Domain(data.domain, data.domain_servers);
}); });
clearTimeout(timer);
if (!response.ok) {
throw Error("Invalid response");
}
const data = await response.json();
return new Domain(data.domain, data.domain_servers);
} }
chooseDomainServer() { chooseDomainServer() {
if (this.servers.length === 0) throw Error("No domain servers specified"); if (this.servers.length === 0) throw Error("No domain servers specified");
this.servers.filter(srv => this.invalidServers.map(srv => srv.id).includes(srv.id)); const servers = this.servers.filter(srv => !this.invalidServers.map(srv => srv.id).includes(srv.id));
if (this.servers.length === 0) throw Error("No domain servers reachable"); if (servers.length === 0) throw Error("No domain servers reachable");
if (this.servers.length === 1) return this.servers[0]; if (servers.length === 1) return servers[0];
let priority = this.servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity); const priority = servers.reduce((min, srv) => Math.min(min, srv.priority), Infinity);
let domainServers = this.servers.filter(srv => srv.priority === priority); const domainServers = servers.filter(srv => srv.priority === priority);
if (domainServers.length === 1) return this.servers[0]; if (domainServers.length === 1) return servers[0];
let totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0); const totalWeight = domainServers.reduce((total, srv) => total + srv.weight, 0);
let w = Math.floor(Math.random() * totalWeight); const w = Math.floor(Math.random() * totalWeight);
let accumulator = 0; let accumulator = 0;
for (let srv of domainServers) { for (const srv of domainServers) {
accumulator += srv.weight; accumulator += srv.weight;
if (w < accumulator) return srv; if (w < accumulator) return srv;
} }
throw "Domain server selection did not work correctly"; throw Error("Domain server selection did not work correctly");
} }
} }
@ -107,8 +111,12 @@ export class Session {
constructor(domain) { constructor(domain) {
this.domain = domain; this.domain = domain;
this.server = domain.chooseDomainServer();
this.numRequests = 0; this.numRequests = 0;
}
async chooseDomainServer() {
while (!this.server) {
this.server = this.domain.chooseDomainServer();
const host = this.server.host; const host = this.server.host;
const protocols = this.server.protocols; const protocols = this.server.protocols;
@ -116,28 +124,38 @@ export class Session {
/* /*
if ("wss" in protocols) { if ("wss" in protocols) {
this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]); this.wsConnection = new WebSocket(`wss://${host}:${protocols.wss}/_usimp/websocket`, ["usimp"]);
} else if ("ws" in protocols) {
this.wsConnection = new WebSocket(`ws://${host}:${protocols.ws}/_usimp/websocket`, ["usimp"])
} }
*/ */
if ("https" in protocols) { // TODO http -> https
this.httpBaseUrl = `https://${host}:${protocols.https}/_usimp`; if ("http" in protocols) {
} else if ("http" in protocols) {
this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`; this.httpBaseUrl = `http://${host}:${protocols.http}/_usimp`;
try {
return await this.ping();
} catch {
this.domain.invalidServers.push(this.server);
this.server = null;
}
} else { } else {
throw "Domain server provides no supported transport protocols"; console.warn(`Domain server ${this.server} does not support 'https' transport protocol`);
this.domain.invalidServers.push(this.server);
this.server = null;
}
} }
} }
send(endpoint, data) { async send(endpoint, data, timeout = 2000, forceHttp = false) {
this.numRequests++; this.numRequests++;
if (this.wsConnection) {
if (!forceHttp && this.wsConnection) {
this.wsConnection.send(JSON.stringify({ this.wsConnection.send(JSON.stringify({
'request_num': this.numRequests, 'request_num': this.numRequests,
'data': data 'data': data
})); }));
} else { } else {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
let headers = { let headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'To-Domain': this.domain.id, 'To-Domain': this.domain.id,
@ -147,28 +165,45 @@ export class Session {
headers['Authorization'] = `usimp ${this.token}`; headers['Authorization'] = `usimp ${this.token}`;
} }
return fetch(`${this.httpBaseUrl}/${endpoint}`, { const startTime = new Date();
let response = await fetch(`${this.httpBaseUrl}/${endpoint}`, {
method: "POST", method: "POST",
headers: headers, headers: headers,
body: JSON.stringify(data), body: JSON.stringify(data),
}).then(response => { signal: controller.signal,
return response.json();
}); });
const endTime = new Date();
clearTimeout(timer);
let responseData = await response.json();
responseData.duration = endTime - startTime;
return responseData;
} }
} }
authenticate(accountName, password) { async ping() {
return this.send("authenticate", { let result = {"http": null, "ws": null};
const res = await this.send("ping", {}, undefined, true);
result.http = res.duration;
if (this.wsConnection) {
await this.wsConnection.ping();
}
return result;
}
async authenticate(accountName, password) {
let response = await this.send("authenticate", {
type: "password", type: "password",
name: accountName, name: accountName,
password: password, password: password,
}).then(response => {
this.token = response.token;
return response;
}); });
if (response.status === "success") {
this.token = response.data.token;
}
return response;
} }
sendEvent(roomId, data) { async sendEvent(roomId, data) {
return this.send("send_event", { return this.send("send_event", {
room_id: roomId, room_id: roomId,
data: data, data: data,

View File

@ -54,7 +54,6 @@ div#windows {
div#windows > *, div#windows > *,
main { main {
backdrop-filter: blur(32px);
border: 1px solid var(--bg-win); border: 1px solid var(--bg-win);
background: var(--bg-win); background: var(--bg-win);
border-radius: 4px; border-radius: 4px;
@ -68,6 +67,7 @@ main {
} }
div#windows > * { div#windows > * {
backdrop-filter: blur(32px);
padding: 0.5em 1em; padding: 0.5em 1em;
} }
@ -199,6 +199,7 @@ main .chat-history {
height: calc(100% - 3em); height: calc(100% - 3em);
overflow-y: scroll; overflow-y: scroll;
box-sizing: border-box; box-sizing: border-box;
backdrop-filter: blur(4px);
} }
main .chat-input-wrapper { main .chat-input-wrapper {
@ -207,6 +208,7 @@ main .chat-input-wrapper {
border-top: 1px solid var(--bg-win); border-top: 1px solid var(--bg-win);
padding: 0.5em 1em; padding: 0.5em 1em;
box-sizing: border-box; box-sizing: border-box;
backdrop-filter: blur(32px);
} }
main .chat-input-wrapper input { main .chat-input-wrapper input {
@ -218,6 +220,7 @@ main .chat-input-wrapper input {
box-sizing: border-box; box-sizing: border-box;
outline: none; outline: none;
padding: 0.25em 1em; padding: 0.25em 1em;
font-size: 0.875rem;
} }
main .chat .message { main .chat .message {
@ -228,5 +231,6 @@ main .chat .message {
display: block; display: block;
width: fit-content; width: fit-content;
width: -moz-fit-content; width: -moz-fit-content;
background-color: var(--bg); background-color: rgba(255, 255, 255, 0.875);
font-size: 0.875rem;
} }