first implementation
This commit is contained in:
26
chat-signaling-server/templates/_layout.html
Normal file
26
chat-signaling-server/templates/_layout.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
{% block head %}
|
||||
<title>{% block fulltitle %}{% block title %}{% endblock %} - {% block sitename %}Demo{% endblock %}{% endblock %}</title>
|
||||
{% block stylesheets %}
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
{% block pagestylesheet %}{% endblock %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content">{% block content %}{% endblock %}</div>
|
||||
<div id="footer">
|
||||
{% block footer %}
|
||||
© Copyright 2008 by <a href="http://domain.invalid/">you</a>.
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block scripts %}
|
||||
{% block pagescripts %}{% endblock %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
|
||||
</html>
|
267
chat-signaling-server/templates/index.html
Normal file
267
chat-signaling-server/templates/index.html
Normal file
@ -0,0 +1,267 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block title %}ChatBox{% endblock %}
|
||||
{% block pagestylesheet %}
|
||||
<link rel="stylesheet" href="/static/index.css" />
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="app">
|
||||
<div id="message-list">
|
||||
|
||||
</div>
|
||||
<div id="inputs">
|
||||
<select id="peers">
|
||||
|
||||
</select>
|
||||
<input id="message-input" />
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block pagescripts %}
|
||||
<script>
|
||||
(() => {
|
||||
let message_list = document.getElementById("message-list")
|
||||
let peers = document.getElementById("peers")
|
||||
let inputbox = document.getElementById("message-input")
|
||||
|
||||
let search = new URLSearchParams(location.search);
|
||||
let sender = search.get("name") || "";
|
||||
|
||||
let room = search.get("room") || "public";
|
||||
|
||||
let peerMap = new Map();
|
||||
let channelMap = new Map();
|
||||
|
||||
let ws;
|
||||
let wsReconnectInterval = 5000;
|
||||
|
||||
const MessageBootstrap = "Bootstrap";
|
||||
const MessageDiscoverRequest = "DiscoverRequest";
|
||||
const MessageDiscoverResponse = "DiscoverResponse";
|
||||
const MessageSessionOffer = "SessionOffer";
|
||||
const MessageSessionAnswer = "SessionAnswer";
|
||||
const MessageICECandidate = "IceCandidate";
|
||||
|
||||
const display = (message) => {
|
||||
let newNode = document.createElement("div")
|
||||
newNode.innerHTML = `${new Date().toTimeString()}: ${message}`;
|
||||
message_list.appendChild(newNode)
|
||||
}
|
||||
|
||||
inputbox.addEventListener("keyup", (e) => {
|
||||
if (!(e.key === 'Enter' || e.keyCode === 13)) {
|
||||
return
|
||||
}
|
||||
if (!inputbox.value || inputbox.value.trim().length == 0) {
|
||||
return
|
||||
}
|
||||
let channel = channelMap.get(peers.value)
|
||||
if (!channel) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log(`You -> ${peers.value}: ${inputbox.value}`)
|
||||
display(`You -> ${peers.value}: ${inputbox.value}`)
|
||||
channel.send(inputbox.value)
|
||||
inputbox.value = ""
|
||||
})
|
||||
|
||||
const addPeer = (peerName) => {
|
||||
let newNode = document.createElement("option")
|
||||
newNode.id = peerName
|
||||
newNode.setAttribute("value", peerName)
|
||||
newNode.innerText = peerName;
|
||||
newNode.innerHTML = peerName;
|
||||
peers.appendChild(newNode)
|
||||
}
|
||||
const removePeer = (peerName) => {
|
||||
let el = document.getElementById(peerName);
|
||||
if(el) el.remove()
|
||||
}
|
||||
|
||||
let timeout;
|
||||
|
||||
let reconnect = () => {
|
||||
if(ws && (ws.readyState == WebSocket.CONNECTING || ws.readyState == WebSocket.OPEN)) {
|
||||
console.log("ws ok", ws)
|
||||
return;
|
||||
}
|
||||
|
||||
let protocol = "ws://";
|
||||
if (location.protocol === "https") {
|
||||
protocol = "wss://"
|
||||
}
|
||||
|
||||
ws = new WebSocket(protocol+location.host+"/ws");
|
||||
ws.addEventListener("error", reconnect)
|
||||
ws.addEventListener("close", reconnect)
|
||||
|
||||
ws.addEventListener("message", ({data}) => {
|
||||
handle_ws_message(JSON.parse(data))
|
||||
})
|
||||
ws.addEventListener("open", () => {
|
||||
display("server connected. waiting for a name to be assigned for you...")
|
||||
ws.send(JSON.stringify({
|
||||
message: {
|
||||
type: MessageBootstrap,
|
||||
},
|
||||
room,
|
||||
sender,
|
||||
}))
|
||||
})
|
||||
console.log("connecting...")
|
||||
}
|
||||
|
||||
let handle_ws_message = (wsMessage) => {
|
||||
let recreateAndSetupPeer = async (peerName) => {
|
||||
if (!peerMap.has(peerName)) {
|
||||
|
||||
let peer = new RTCPeerConnection({
|
||||
iceServers: [{ urls: ["stun:nhz.jeffthecoder.xyz:3478", "stun:nhz.jeffthecoder.xyz:3479", "stun:nhz.jeffthecoder.xyz:13478"] }]
|
||||
})
|
||||
peer.addEventListener("signalingstatechange", (ev) => {
|
||||
console.log("signaling state changed: ", peer.signalingState)
|
||||
})
|
||||
peer.addEventListener("connectionstatechange", (ev) => {
|
||||
console.log("peer connection state changed: ", peer.connectionState)
|
||||
switch (peer.connectionState) {
|
||||
case "closed":
|
||||
break;
|
||||
case "disconnected":
|
||||
case "failed":
|
||||
peer.restartIce()
|
||||
break
|
||||
}
|
||||
})
|
||||
peer.addEventListener("icecandidate", (ev) => {
|
||||
if (!ev.candidate) {
|
||||
console.log("gather end")
|
||||
return
|
||||
}
|
||||
let candidate = ev.candidate.toJSON()
|
||||
ws.send(JSON.stringify({
|
||||
message: {
|
||||
type: MessageICECandidate,
|
||||
candidate: JSON.stringify(candidate),
|
||||
sender,
|
||||
kind: 3,
|
||||
},
|
||||
room,
|
||||
sender,
|
||||
receiver: wsMessage.sender,
|
||||
}))
|
||||
})
|
||||
peer.addEventListener("icegatheringstatechange", ev => {
|
||||
console.log("gather", peer.iceGatheringState)
|
||||
})
|
||||
peer.addEventListener("datachannel", ({channel}) => {
|
||||
channelMap.set(peerName, channel);
|
||||
channel.addEventListener("open", (ev) => {
|
||||
display("connected in event")
|
||||
addPeer(peerName)
|
||||
})
|
||||
channel.addEventListener("message", (ev) => {
|
||||
display(`${peerName} -> You: ${ev.data}`)
|
||||
})
|
||||
channel.addEventListener("close", () => {
|
||||
removePeer(peerName)
|
||||
})
|
||||
})
|
||||
|
||||
peerMap.set(peerName, peer);
|
||||
}
|
||||
let peer = peerMap.get(peerName);
|
||||
|
||||
let resultSdp = null, resultMessageType;
|
||||
if(wsMessage.message.type === MessageDiscoverResponse) {
|
||||
let channel = peer.createDataChannel("chat");
|
||||
channel.addEventListener("open", (ev) => {
|
||||
display("connected in event")
|
||||
addPeer(peerName)
|
||||
})
|
||||
channel.addEventListener("message", (ev) => {
|
||||
display(`${peerName} -> You: ${ev.data}`)
|
||||
})
|
||||
channel.addEventListener("close", () => {
|
||||
removePeer(peerName)
|
||||
})
|
||||
channelMap.set(peerName, channel);
|
||||
|
||||
|
||||
resultSdp = await peer.createOffer();
|
||||
resultMessageType = MessageSessionOffer;
|
||||
peer.setLocalDescription(resultSdp)
|
||||
console.log("set local offer")
|
||||
} else {
|
||||
peer.setRemoteDescription(JSON.parse(wsMessage.message.sdp))
|
||||
console.log("set remote offer")
|
||||
resultSdp = await peer.createAnswer();
|
||||
resultMessageType = MessageSessionAnswer;
|
||||
peer.setLocalDescription(resultSdp)
|
||||
console.log("set local answer")
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
message: {
|
||||
type: resultMessageType,
|
||||
sdp: JSON.stringify(resultSdp),
|
||||
sender,
|
||||
kind: 3,
|
||||
},
|
||||
room,
|
||||
sender,
|
||||
receiver: wsMessage.sender,
|
||||
}))
|
||||
}
|
||||
|
||||
let peer = peerMap.get(wsMessage.sender)
|
||||
|
||||
switch (wsMessage.message.type) {
|
||||
case MessageBootstrap:
|
||||
sender = wsMessage.sender;
|
||||
ws.send(JSON.stringify({
|
||||
message: {
|
||||
type: MessageDiscoverRequest,
|
||||
},
|
||||
room,
|
||||
sender: wsMessage.sender,
|
||||
}))
|
||||
display(`You are ${sender}. Searching for peers...`)
|
||||
break;
|
||||
case MessageDiscoverRequest:
|
||||
display("connecting to peer " + wsMessage.sender)
|
||||
ws.send(JSON.stringify({
|
||||
message: {
|
||||
type: MessageDiscoverResponse,
|
||||
},
|
||||
room,
|
||||
sender,
|
||||
receiver: wsMessage.sender,
|
||||
}))
|
||||
break
|
||||
case MessageDiscoverResponse:
|
||||
recreateAndSetupPeer(wsMessage.sender)
|
||||
break;
|
||||
case MessageSessionOffer:
|
||||
recreateAndSetupPeer(wsMessage.sender)
|
||||
break
|
||||
case MessageSessionAnswer:
|
||||
display("receiving connection to peer: " + wsMessage.sender)
|
||||
console.log("set remote answer")
|
||||
peer.setRemoteDescription(JSON.parse(wsMessage.message.sdp))
|
||||
peer.restartIce()
|
||||
break
|
||||
case MessageICECandidate:
|
||||
let candidate = JSON.parse(wsMessage.message.candidate);
|
||||
if(!peer) {
|
||||
console.warn("candidate dropped", candidate)
|
||||
return
|
||||
}
|
||||
peer.addIceCandidate(candidate)
|
||||
}
|
||||
}
|
||||
|
||||
reconnect()
|
||||
console.log("document loaded")
|
||||
})()
|
||||
</script>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user