diff --git a/application/controller/socket_verify.go b/application/controller/socket_verify.go index a1b36de..f8a6c01 100644 --- a/application/controller/socket_verify.go +++ b/application/controller/socket_verify.go @@ -156,3 +156,8 @@ func (s socketVerification) Get( return nil } + +func (s socketVerification) Options( + w http.ResponseWriter, r *http.Request, l log.Logger) error { + return nil +} diff --git a/ui/app.js b/ui/app.js index d5b444a..538e641 100644 --- a/ui/app.js +++ b/ui/app.js @@ -200,25 +200,29 @@ function startApp(rootEl) { await cipher.hmac512(enc.encode(finalKey), enc.encode(rTime)) ).slice(0, 32); }, - buildBackendSocketURL() { - let r = ""; + buildBackendSocketURLs() { + let r = { + webSocket: "", + keepAlive: "", + }; switch (location.protocol) { case "https:": - r = "wss://"; + r.webSocket = "wss://"; break; default: - r = "ws://"; + r.webSocket = "ws://"; } - r += location.host + socksInterface; + r.webSocket += location.host + socksInterface; + r.keepAlive = location.protocol + "//" + location.host + socksInterface; return r; }, buildSocket(key, dialTimeout, heartbeatInterval) { return new Socket( - this.buildBackendSocketURL(), + this.buildBackendSocketURLs(), key, dialTimeout * 1000, heartbeatInterval * 1000 diff --git a/ui/socket.js b/ui/socket.js index 0c28582..ee50641 100644 --- a/ui/socket.js +++ b/ui/socket.js @@ -19,6 +19,7 @@ import * as crypt from "./crypto.js"; import * as reader from "./stream/reader.js"; import * as sender from "./stream/sender.js"; import * as streams from "./stream/streams.js"; +import * as xhr from "./xhr.js"; export const ECHO_FAILED = streams.ECHO_FAILED; @@ -39,19 +40,22 @@ class Dial { this.address = address; this.timeout = timeout; this.privateKey = privateKey; + this.keepAliveTicker = null; } /** * Connect to the remote server * + * @param {string} address Target URL address * @param {number} timeout Connect timeout * * @returns {Promise} When connection is established * */ - connect(timeout) { + connect(address, timeout) { + const self = this; return new Promise((resolve, reject) => { - let ws = new WebSocket(this.address), + let ws = new WebSocket(address.webSocket), promised = false, timeoutTimer = setTimeout(() => { ws.close(); @@ -77,6 +81,12 @@ class Dial { return reject(e); }; + if (!self.keepAliveTicker) { + self.keepAliveTicker = setInterval(() => { + xhr.options(address.keepAlive, {}); + }, self.timeout); + } + ws.addEventListener("open", (_event) => { myRes(ws); }); @@ -87,10 +97,14 @@ class Dial { }; myRej(event); + clearInterval(self.keepAliveTicker); + self.keepAliveTicker = null; }); ws.addEventListener("error", (_event) => { ws.close(); + clearInterval(self.keepAliveTicker); + self.keepAliveTicker = null; }); }); } @@ -123,7 +137,7 @@ class Dial { * */ async dial(callbacks) { - let ws = await this.connect(this.timeout); + let ws = await this.connect(this.address, this.timeout); try { let rd = new reader.Reader(new reader.Multiple(() => {}), (data) => { diff --git a/ui/xhr.js b/ui/xhr.js index 553adcd..476ef0a 100644 --- a/ui/xhr.js +++ b/ui/xhr.js @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -export function get(url, headers) { +function send(method, url, headers) { return new Promise((res, rej) => { let authReq = new XMLHttpRequest(); @@ -35,7 +35,7 @@ export function get(url, headers) { rej(e); }); - authReq.open("GET", url, true); + authReq.open(method, url, true); for (let h in headers) { authReq.setRequestHeader(h, headers[h]); @@ -44,3 +44,11 @@ export function get(url, headers) { authReq.send(); }); } + +export function get(url, headers) { + return send("GET", url, headers); +} + +export function options(url, headers) { + return send("OPTIONS", url, headers); +}