edge cases
This commit is contained in:
@@ -15,6 +15,6 @@
|
|||||||
* [x] QRCode
|
* [x] QRCode
|
||||||
* [x] Fix turn server settings to work on all servers
|
* [x] Fix turn server settings to work on all servers
|
||||||
* [x] More cleaning
|
* [x] More cleaning
|
||||||
* [ ] Fix edge cases
|
* [x] Fix edge cases
|
||||||
* [ ] Cancel download
|
* [ ] Cancel download
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -48,7 +48,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-2 w-full">
|
<div class="mt-2 w-full">
|
||||||
<div class="inline-grid *:[grid-area:1/1] align-middle mr-1">
|
<div class="inline-grid *:[grid-area:1/1] align-middle mr-1">
|
||||||
<div :class="`status status-${statusColor(status)} status-lg animate-ping`"></div>
|
<div v-if="statusBlinking(status)" :class="`status status-${statusColor(status)} status-lg animate-ping`">
|
||||||
|
</div>
|
||||||
<div :class="`status status-${statusColor(status)} status-lg`"></div>
|
<div :class="`status status-${statusColor(status)} status-lg`"></div>
|
||||||
</div> {{ status }}
|
</div> {{ status }}
|
||||||
</div>
|
</div>
|
||||||
@@ -67,14 +68,18 @@
|
|||||||
<div v-if="serverIsReady" @click="onShare" class="mt-2 w-full btn btn-primary">
|
<div v-if="serverIsReady" @click="onShare" class="mt-2 w-full btn btn-primary">
|
||||||
<i icon="share-2"></i> {{ serverShareText }}
|
<i icon="share-2"></i> {{ serverShareText }}
|
||||||
</div>
|
</div>
|
||||||
<ul v-if="serverIsReady && server.clients.length"
|
<ul v-if="serverIsReady && server.clients.length" class="mt-2 -mx-10 -mb-10 list rounded-box text-left">
|
||||||
class="mt-2 list bg-base-100 rounded-box shadow-md text-left">
|
|
||||||
<li class="p-4 pb-2 text-xs opacity-60 tracking-wide">Connected peers</li>
|
<li class="p-4 pb-2 text-xs opacity-60 tracking-wide">Connected peers</li>
|
||||||
<li class="list-row" v-for="client in server.clients" v-bind:key="client.id">
|
<li class="list-row" v-for="client in server.clients" v-bind:key="client.id">
|
||||||
<div class="list-col-grow">
|
<div class="list-col-grow">
|
||||||
<div>{{ client.userAgent ?? client.id }}</div>
|
<div>{{ client.userAgent ?? client.id }}</div>
|
||||||
<div>
|
<div>
|
||||||
<div :class="`status status-${statusColor(client.status)}`"></div> {{ client.status }}
|
<div class="inline-grid *:[grid-area:1/1] align-middle mr-1">
|
||||||
|
<div v-if="statusBlinking(client.status)"
|
||||||
|
:class="`status status-${statusColor(client.status)} animate-ping`">
|
||||||
|
</div>
|
||||||
|
<div :class="`status status-${statusColor(client.status)}`"></div>
|
||||||
|
</div> {{ client.status }}
|
||||||
</div>
|
</div>
|
||||||
<progress :value="client.sent" :max="fileSize" aria-busy="!client.connected" class="progress"
|
<progress :value="client.sent" :max="fileSize" aria-busy="!client.connected" class="progress"
|
||||||
:class="client.sent >= fileSize ? 'progress-success' : 'progress-primary'"></progress>
|
:class="client.sent >= fileSize ? 'progress-success' : 'progress-primary'"></progress>
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ const MESSAGE_TYPE = {
|
|||||||
ClientInfo: "client-info",
|
ClientInfo: "client-info",
|
||||||
ClientSeek: "client-seek",
|
ClientSeek: "client-seek",
|
||||||
ClientDone: "client-done",
|
ClientDone: "client-done",
|
||||||
|
Ping: "ping",
|
||||||
};
|
};
|
||||||
|
|
||||||
const STATUS = {
|
const STATUS = {
|
||||||
@@ -108,12 +109,79 @@ const STATUS_COLOR = {
|
|||||||
[STATUS.ClientConnecting]: "info",
|
[STATUS.ClientConnecting]: "info",
|
||||||
[STATUS.ClientWaiting]: "info",
|
[STATUS.ClientWaiting]: "info",
|
||||||
[STATUS.ClientReady]: "success",
|
[STATUS.ClientReady]: "success",
|
||||||
[STATUS.ClientDownloading]: "info",
|
[STATUS.ClientDownloading]: "primary",
|
||||||
[STATUS.ClientDownloaded]: "success",
|
[STATUS.ClientDownloaded]: "success",
|
||||||
[STATUS.ClientDisconnected]: "neutral",
|
[STATUS.ClientDisconnected]: "neutral",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CONNECTED_STATUSES = [
|
||||||
|
STATUS.Connecting,
|
||||||
|
STATUS.ServerNoFile,
|
||||||
|
STATUS.ServerReady,
|
||||||
|
STATUS.ClientConnecting,
|
||||||
|
STATUS.ClientWaiting,
|
||||||
|
STATUS.ClientReady,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PEER_ERROR = {
|
||||||
|
/**
|
||||||
|
* The client's browser does not support some or all WebRTC features that you are trying to use.
|
||||||
|
*/
|
||||||
|
BrowserIncompatible: "browser-incompatible",
|
||||||
|
/**
|
||||||
|
* You've already disconnected this peer from the server and can no longer make any new connections on it.
|
||||||
|
*/
|
||||||
|
Disconnected: "disconnected",
|
||||||
|
/**
|
||||||
|
* The ID passed into the Peer constructor contains illegal characters.
|
||||||
|
*/
|
||||||
|
InvalidID: "invalid-id",
|
||||||
|
/**
|
||||||
|
* The API key passed into the Peer constructor contains illegal characters or is not in the system (cloud server only).
|
||||||
|
*/
|
||||||
|
InvalidKey: "invalid-key",
|
||||||
|
/**
|
||||||
|
* Lost or cannot establish a connection to the signalling server.
|
||||||
|
*/
|
||||||
|
Network: "network",
|
||||||
|
/**
|
||||||
|
* The peer you're trying to connect to does not exist.
|
||||||
|
*/
|
||||||
|
PeerUnavailable: "peer-unavailable",
|
||||||
|
/**
|
||||||
|
* PeerJS is being used securely, but the cloud server does not support SSL. Use a custom PeerServer.
|
||||||
|
*/
|
||||||
|
SslUnavailable: "ssl-unavailable",
|
||||||
|
/**
|
||||||
|
* Unable to reach the server.
|
||||||
|
*/
|
||||||
|
ServerError: "server-error",
|
||||||
|
/**
|
||||||
|
* An error from the underlying socket.
|
||||||
|
*/
|
||||||
|
SocketError: "socket-error",
|
||||||
|
/**
|
||||||
|
* The underlying socket closed unexpectedly.
|
||||||
|
*/
|
||||||
|
SocketClosed: "socket-closed",
|
||||||
|
/**
|
||||||
|
* The ID passed into the Peer constructor is already taken.
|
||||||
|
*
|
||||||
|
* :::caution
|
||||||
|
* This error is not fatal if your peer has open peer-to-peer connections.
|
||||||
|
* This can happen if you attempt to {@apilink Peer.reconnect} a peer that has been disconnected from the server,
|
||||||
|
* but its old ID has now been taken.
|
||||||
|
* :::
|
||||||
|
*/
|
||||||
|
UnavailableID: "unavailable-id",
|
||||||
|
/**
|
||||||
|
* Native WebRTC errors.
|
||||||
|
*/
|
||||||
|
WebRTC: "webrtc",
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_CHUNK_SIZE = 12 * 1024;
|
const MAX_CHUNK_SIZE = 12 * 1024;
|
||||||
|
const MAX_DELAY_PING = 5000;
|
||||||
|
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
data() {
|
data() {
|
||||||
@@ -139,6 +207,7 @@ const app = createApp({
|
|||||||
downloadEnd: null,
|
downloadEnd: null,
|
||||||
received: [],
|
received: [],
|
||||||
buffer: null,
|
buffer: null,
|
||||||
|
lastMessage: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -148,7 +217,7 @@ const app = createApp({
|
|||||||
},
|
},
|
||||||
serverIsReady() {
|
serverIsReady() {
|
||||||
return (
|
return (
|
||||||
this.error !== null && this.canConnect && this.server.data !== null
|
this.error === null && this.canConnect && this.server.data !== null
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
serverShareText() {
|
serverShareText() {
|
||||||
@@ -162,7 +231,7 @@ const app = createApp({
|
|||||||
},
|
},
|
||||||
clientIsReady() {
|
clientIsReady() {
|
||||||
return (
|
return (
|
||||||
this.error !== null &&
|
this.error === null &&
|
||||||
this.canConnect &&
|
this.canConnect &&
|
||||||
this.client.connection !== null &&
|
this.client.connection !== null &&
|
||||||
this.client.buffer !== null &&
|
this.client.buffer !== null &&
|
||||||
@@ -216,6 +285,9 @@ const app = createApp({
|
|||||||
const time =
|
const time =
|
||||||
(this.client.downloadEnd ?? new Date()) - this.client.downloadStart;
|
(this.client.downloadEnd ?? new Date()) - this.client.downloadStart;
|
||||||
const speed = this.clientDownloadProgress / time;
|
const speed = this.clientDownloadProgress / time;
|
||||||
|
if (speed <= 0) {
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
const remainingBytes = this.fileSize - this.clientDownloadProgress;
|
const remainingBytes = this.fileSize - this.clientDownloadProgress;
|
||||||
const remainingTime = remainingBytes / speed;
|
const remainingTime = remainingBytes / speed;
|
||||||
return `${utils.prettyTime(remainingTime)}`;
|
return `${utils.prettyTime(remainingTime)}`;
|
||||||
@@ -247,6 +319,9 @@ const app = createApp({
|
|||||||
if (uuidRegex.test(remoteId)) {
|
if (uuidRegex.test(remoteId)) {
|
||||||
this.isServer = false;
|
this.isServer = false;
|
||||||
this.client.remoteId = remoteId;
|
this.client.remoteId = remoteId;
|
||||||
|
} else if (remoteId !== "") {
|
||||||
|
this.isServer = false;
|
||||||
|
this.error = "Invalid link";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initPeer() {
|
initPeer() {
|
||||||
@@ -280,6 +355,14 @@ const app = createApp({
|
|||||||
this.peer.on("error", this.onPeerError);
|
this.peer.on("error", this.onPeerError);
|
||||||
},
|
},
|
||||||
initServerConnection(conn) {
|
initServerConnection(conn) {
|
||||||
|
const index = this.initServerClient(conn);
|
||||||
|
conn.on("open", () => this.onServerConnectionOpen(index));
|
||||||
|
conn.on("close", () => this.onServerConnectionClose(index));
|
||||||
|
conn.on("data", (data) => this.onServerConnectionData(index, data));
|
||||||
|
conn.on("error", (err) => this.onServerConnectionError(index, err));
|
||||||
|
this.initServerWatch(index);
|
||||||
|
},
|
||||||
|
initServerClient(conn) {
|
||||||
let index = this.server.clients.findIndex(
|
let index = this.server.clients.findIndex(
|
||||||
(client) => client.id === conn.peer
|
(client) => client.id === conn.peer
|
||||||
);
|
);
|
||||||
@@ -291,6 +374,7 @@ const app = createApp({
|
|||||||
connected: false,
|
connected: false,
|
||||||
status: STATUS.ClientConnecting,
|
status: STATUS.ClientConnecting,
|
||||||
userAgent: null,
|
userAgent: null,
|
||||||
|
lastMessage: new Date(),
|
||||||
};
|
};
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
index = this.server.clients.length;
|
index = this.server.clients.length;
|
||||||
@@ -298,10 +382,20 @@ const app = createApp({
|
|||||||
} else {
|
} else {
|
||||||
this.server.clients[index] = clientData;
|
this.server.clients[index] = clientData;
|
||||||
}
|
}
|
||||||
conn.on("open", () => this.onServerConnectionOpen(index));
|
return index;
|
||||||
conn.on("close", () => this.onServerConnectionClose(index));
|
},
|
||||||
conn.on("data", (data) => this.onServerConnectionData(index, data));
|
initServerWatch(index) {
|
||||||
conn.on("error", (err) => this.onServerConnectionError(index, err));
|
setInterval(() => {
|
||||||
|
if (!this.error && this.server.clients[index].connected) {
|
||||||
|
this.sendServerPing(index);
|
||||||
|
if (
|
||||||
|
new Date() - this.server.clients[index].lastMessage >
|
||||||
|
MAX_DELAY_PING
|
||||||
|
) {
|
||||||
|
this.onServerConnectionClose(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
},
|
},
|
||||||
initClientConnection(conn) {
|
initClientConnection(conn) {
|
||||||
this.client.connection = conn;
|
this.client.connection = conn;
|
||||||
@@ -309,6 +403,17 @@ const app = createApp({
|
|||||||
conn.on("close", this.onClientConnectionClose);
|
conn.on("close", this.onClientConnectionClose);
|
||||||
conn.on("data", this.onClientConnectionData);
|
conn.on("data", this.onClientConnectionData);
|
||||||
conn.on("error", this.onClientConnectionError);
|
conn.on("error", this.onClientConnectionError);
|
||||||
|
this.initClientWatch();
|
||||||
|
},
|
||||||
|
initClientWatch() {
|
||||||
|
setInterval(() => {
|
||||||
|
if (!this.error && this.client.connected) {
|
||||||
|
this.sendClientPing();
|
||||||
|
if (new Date() - this.client.lastMessage > MAX_DELAY_PING) {
|
||||||
|
this.onClientConnectionClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
},
|
},
|
||||||
clientCreateStream() {
|
clientCreateStream() {
|
||||||
try {
|
try {
|
||||||
@@ -334,14 +439,16 @@ const app = createApp({
|
|||||||
statusColor(status) {
|
statusColor(status) {
|
||||||
return STATUS_COLOR[status];
|
return STATUS_COLOR[status];
|
||||||
},
|
},
|
||||||
|
statusBlinking(status) {
|
||||||
|
return CONNECTED_STATUSES.includes(status);
|
||||||
|
},
|
||||||
// PEER EVENTS
|
// PEER EVENTS
|
||||||
onPeerOpen(id) {
|
onPeerOpen(id) {
|
||||||
this.localId = id;
|
this.localId = id;
|
||||||
this.error = null;
|
|
||||||
if (this.isServer) {
|
if (this.isServer) {
|
||||||
this.server.url = `${window.location.href}?s=${id}`;
|
this.server.url = `${window.location.href}?s=${id}`;
|
||||||
utils.makeQrCode(this.$refs.qrcode, this.server.url);
|
utils.makeQrCode(this.$refs.qrcode, this.server.url);
|
||||||
} else {
|
} else if (this.error === null) {
|
||||||
this.clientOpenConnection();
|
this.clientOpenConnection();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -351,15 +458,34 @@ const app = createApp({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPeerClose() {
|
onPeerClose() {
|
||||||
|
// Window shutting down
|
||||||
this.peer = null;
|
this.peer = null;
|
||||||
},
|
},
|
||||||
onPeerDisconnected() {
|
onPeerDisconnected() {
|
||||||
this.peer.reconnect();
|
if (this.peer) {
|
||||||
|
this.error = `Disconnected.<br>Reconnecting...`;
|
||||||
|
this.peer.reconnect();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onPeerError(err) {
|
onPeerError(err) {
|
||||||
this.error = `${err.type}.<br>Reconnecting...`;
|
switch (err.type) {
|
||||||
this.peer = null;
|
case PEER_ERROR.PeerUnavailable:
|
||||||
setTimeout(this.initPeer, 1000);
|
if (!this.isServer) {
|
||||||
|
this.error = `This link is no longer available`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PEER_ERROR.SocketClosed:
|
||||||
|
if (!this.isServer) {
|
||||||
|
this.error = `The remote peer closed the page`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (this.peer) {
|
||||||
|
this.error = `${err.type}.<br>Reconnecting...`;
|
||||||
|
this.peer.reconnect();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// SERVER CONNECTION EVENTS
|
// SERVER CONNECTION EVENTS
|
||||||
onServerConnectionOpen(index) {
|
onServerConnectionOpen(index) {
|
||||||
@@ -368,7 +494,10 @@ const app = createApp({
|
|||||||
this.sendServerInfo(index);
|
this.sendServerInfo(index);
|
||||||
},
|
},
|
||||||
onServerConnectionData(index, data) {
|
onServerConnectionData(index, data) {
|
||||||
|
this.server.clients[index].lastMessage = new Date();
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
case MESSAGE_TYPE.Ping:
|
||||||
|
break;
|
||||||
case MESSAGE_TYPE.ClientInfo:
|
case MESSAGE_TYPE.ClientInfo:
|
||||||
this.handleClientInfo(index, data);
|
this.handleClientInfo(index, data);
|
||||||
break;
|
break;
|
||||||
@@ -385,9 +514,11 @@ const app = createApp({
|
|||||||
},
|
},
|
||||||
onServerConnectionClose(index) {
|
onServerConnectionClose(index) {
|
||||||
this.server.clients[index].status = STATUS.ClientDisconnected;
|
this.server.clients[index].status = STATUS.ClientDisconnected;
|
||||||
|
this.server.clients[index].connected = false;
|
||||||
},
|
},
|
||||||
onServerConnectionError(index) {
|
onServerConnectionError(index) {
|
||||||
this.server.clients[index].status = STATUS.Error;
|
this.server.clients[index].status = STATUS.Error;
|
||||||
|
this.server.clients[index].connected = false;
|
||||||
},
|
},
|
||||||
// CLIENT CONNECTION EVENTS
|
// CLIENT CONNECTION EVENTS
|
||||||
onClientConnectionOpen() {
|
onClientConnectionOpen() {
|
||||||
@@ -395,7 +526,10 @@ const app = createApp({
|
|||||||
this.sendClientInfo();
|
this.sendClientInfo();
|
||||||
},
|
},
|
||||||
onClientConnectionData(data) {
|
onClientConnectionData(data) {
|
||||||
|
this.client.lastMessage = new Date();
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
|
case MESSAGE_TYPE.Ping:
|
||||||
|
break;
|
||||||
case MESSAGE_TYPE.ServerInfo:
|
case MESSAGE_TYPE.ServerInfo:
|
||||||
this.handleServerInfo(data);
|
this.handleServerInfo(data);
|
||||||
break;
|
break;
|
||||||
@@ -412,12 +546,30 @@ const app = createApp({
|
|||||||
},
|
},
|
||||||
onClientConnectionClose() {
|
onClientConnectionClose() {
|
||||||
this.client.connection = null;
|
this.client.connection = null;
|
||||||
|
if (!this.clientFinished) {
|
||||||
|
this.error = `The remote peer closed the page`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onClientConnectionError(err) {
|
onClientConnectionError(err) {
|
||||||
this.error = `${err.type}.<br>Reconnecting...`;
|
switch (err.type) {
|
||||||
setTimeout(this.clientOpenConnection, 1000);
|
case PEER_ERROR.PeerUnavailable:
|
||||||
|
this.error = `This link is no longer available`;
|
||||||
|
break;
|
||||||
|
case PEER_ERROR.SocketClosed:
|
||||||
|
this.error = `The remote peer closed the page`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.error = `${err.type}.<br>Reconnecting...`;
|
||||||
|
setTimeout(this.clientOpenConnection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// EXCHANGES
|
// EXCHANGES
|
||||||
|
sendServerPing(index) {
|
||||||
|
this.server.clients[index].connection.send({
|
||||||
|
type: MESSAGE_TYPE.Ping,
|
||||||
|
});
|
||||||
|
},
|
||||||
sendServerInfo(index) {
|
sendServerInfo(index) {
|
||||||
this.server.clients[index].connection.send({
|
this.server.clients[index].connection.send({
|
||||||
type: MESSAGE_TYPE.ServerInfo,
|
type: MESSAGE_TYPE.ServerInfo,
|
||||||
@@ -465,6 +617,11 @@ const app = createApp({
|
|||||||
this.clientDownloadFile();
|
this.clientDownloadFile();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
sendClientPing() {
|
||||||
|
this.client.connection.send({
|
||||||
|
type: MESSAGE_TYPE.Ping,
|
||||||
|
});
|
||||||
|
},
|
||||||
sendClientInfo() {
|
sendClientInfo() {
|
||||||
this.client.connection.send({
|
this.client.connection.send({
|
||||||
type: MESSAGE_TYPE.ClientInfo,
|
type: MESSAGE_TYPE.ClientInfo,
|
||||||
|
|||||||
Reference in New Issue
Block a user