diff --git a/src/puter-js/src/index.js b/src/puter-js/src/index.js index 937b177a2..09d4a3f38 100644 --- a/src/puter-js/src/index.js +++ b/src/puter-js/src/index.js @@ -20,6 +20,7 @@ import { XDIncomingService } from './services/XDIncoming.js'; import { NoPuterYetService } from './services/NoPuterYet.js'; import { Debug } from './modules/Debug.js'; import { PSocket, wispInfo } from './modules/networking/PSocket.js'; +import { PTLSSocket } from "./modules/networking/PTLS.js" import { PWispHandler } from './modules/networking/PWispHandler.js'; // TODO: This is for a safe-guard below; we should check if we can @@ -330,7 +331,10 @@ window.puter = (function() { })).json())["token"]; wispInfo.handler = new PWispHandler(wispInfo.server, wispToken); this.net = { - Socket: PSocket + Socket: PSocket, + tls: { + TLSSocket: PTLSSocket + } } })(); diff --git a/src/puter-js/src/modules/networking/PSocket.js b/src/puter-js/src/modules/networking/PSocket.js index 1c0d66c87..bfd413ed1 100644 --- a/src/puter-js/src/modules/networking/PSocket.js +++ b/src/puter-js/src/modules/networking/PSocket.js @@ -1,6 +1,6 @@ import EventListener from "../../lib/EventListener.js"; -import {PWispHandler} from "./PWispHandler.js" - +import { errors } from "./parsers.js"; +const texten = new TextEncoder(); export let wispInfo = { server: "wss://puter.cafe/", @@ -11,34 +11,40 @@ export class PSocket extends EventListener { _events = new Map(); _streamID; constructor(host, port) { - super(["data", "drain", "open", "close"]); + super(["data", "drain", "open", "error", "close", "tlsdata", "tlsopen"]); const callbacks = { dataCallBack: (data) => { this.emit("data", data); }, closeCallBack: (reason) => { - this.emit("close", false); // TODO, report errors - }, - openCallBack: () => { - this.emit("open"); + if (reason !== 0x02) { + this.emit("error", new Error(errors[reason])); + this.emit("close", true); + return; + } + this.emit("close", false); } } this._streamID = wispInfo.handler.register(host, port, callbacks); + setTimeout(() => {this.emit("open", undefined)}, 0); } addListener(...args) { this.on(...args); } write(data, callback) { - if (data.buffer) { // typedArray + if (data.buffer) { // TypedArray wispInfo.handler.write(this._streamID, data); if (callback) callback(); - } else if (data.resize) { + } else if (data.resize) { // ArrayBuffer data.write(this._streamID, new Uint8Array(data)); if (callback) callback(); - } else if (data.arrayBuffer) { // Oh No, a blob, I need to handle this later, maybe with https://gist.github.com/jimmywarting/65c358f878cac8e7f39cfb7d43931f62? - + } else if (typeof(data) === "string") { + wispInfo.handler.write(this._streamID, texten.encode(data)) + if (callback) callback(); + } else { + throw new Error("Invalid data type (not TypedArray, ArrayBuffer or String!!)"); } } close() { diff --git a/src/puter-js/src/modules/networking/PTLS.js b/src/puter-js/src/modules/networking/PTLS.js new file mode 100644 index 000000000..6591d7666 --- /dev/null +++ b/src/puter-js/src/modules/networking/PTLS.js @@ -0,0 +1,98 @@ +/** + * This file uses https://github.com/MercuryWorkshop/rustls-wasm authored by GitHub:@r58Playz under the MIT License + */ + +import { PSocket } from "./PSocket"; + +let rustls = undefined; + +export class PTLSSocket extends PSocket { + constructor(...args) { + super(...args); + (async() => { + if (!rustls) { + rustls = (await import( /* webpackIgnore: true */ "https://puter-net.b-cdn.net/rustls.js")) + await rustls.default("https://puter-net.b-cdn.net/rustls.wasm") + } + + let cancelled = false; + const readable = new ReadableStream({ + /** + * + * @param {ReadableStreamDefaultController} controller + */ + start: (controller) => { + super.on("data", (data) => { + controller.enqueue(data.buffer) + }) + super.on("close", () => { + if (!cancelled) + controller.close() + }) + + }, + pull: (controller) => { + + }, + cancel: () => { + cancelled = true; + } + + }) + + const writable = new WritableStream({ + write: (chunk) => { super.write(chunk); }, + abort: () => { console.log("hello"); super.close(); }, + close: () => { super.close(); }, + }) + + let read, write; + try { + const TLSConnnection = await rustls.connect_tls(readable, writable, args[0]) + read = TLSConnnection.read; + write = TLSConnnection.write; + } catch (e) { + this.emit("error", new Error("TLS Handshake failed: " + e)); + return; + } + + + this.writer = write.getWriter(); + // writer.write("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n"); + let reader = read.getReader(); + let done = false; + this.emit("tlsopen", undefined); + + while (!done) { + try { + const {done: readerDone, value} = await reader.read(); + done = readerDone; + if (!done) { + this.emit("tlsdata", value); + } + } catch (e) { + this.emit("error", e) + } + } + })(); + } + on(event, callback) { + if (event === "data" || event === "open") { + return super.on("tls" + event, callback) + } else { + return super.on(event, callback); + } + } + write(data, callback) { + if (data.buffer) { // TypedArray + this.writer.write(data.slice(0).buffer).then(callback); + } else if (data.resize) { // ArrayBuffer + this.writer.write(data).then(callback); + } else if (typeof(data) === "string"){ + this.writer.write(data).then(callback); + } else { + throw new Error("Invalid data type (not TypedArray, ArrayBuffer or String!!)"); + } + } + +} \ No newline at end of file diff --git a/src/puter-js/src/modules/networking/PWispHandler.js b/src/puter-js/src/modules/networking/PWispHandler.js index f5e3f9d54..1fbd23ae2 100644 --- a/src/puter-js/src/modules/networking/PWispHandler.js +++ b/src/puter-js/src/modules/networking/PWispHandler.js @@ -43,7 +43,7 @@ export class PWispHandler { } register(host, port, callbacks) { const streamID = this._nextStreamID++; - this.streamMap.set(streamID, {queue: [], streamID, buffer: this._bufferMax, dataCallBack: callbacks.dataCallBack, closeCallBack: callbacks.closeCallBack, openCallBack: callbacks.openCallBack}); + this.streamMap.set(streamID, {queue: [], streamID, buffer: this._bufferMax, dataCallBack: callbacks.dataCallBack, closeCallBack: callbacks.closeCallBack}); this._ws.send(createWispPacket({ packetType: CONNECT, streamType: TCP, @@ -51,7 +51,6 @@ export class PWispHandler { hostname: host, port: port })) - return streamID; } diff --git a/src/puter-js/src/modules/networking/parsers.js b/src/puter-js/src/modules/networking/parsers.js index 80808b32b..dcbbef2fd 100644 --- a/src/puter-js/src/modules/networking/parsers.js +++ b/src/puter-js/src/modules/networking/parsers.js @@ -14,6 +14,15 @@ export const UDP = 0x02; // Frequently used objects export const textde = new TextDecoder(); const texten = new TextEncoder(); +export const errors = { + 0x41: "Stream creation failed due to invalid information. This could be sent if the destination was a reserved address or the port is invalid." + ,0x42: "Stream creation failed due to an unreachable destination host. This could be sent if the destination is an domain which does not resolve to anything." + ,0x43: "Stream creation timed out due to the destination server not responding." + ,0x44: "Stream creation failed due to the destination server refusing the connection." + ,0x47: "TCP data transfer timed out." + ,0x48: "Stream destination address/domain is intentionally blocked by the proxy server." + ,0x49: "Connection throttled by the server." +} /** * @typedef {{packetType: number, streamID: number, streamType?: number, port?: number, hostname?: string, payload?: Uint8Array, reason?: number, remainingBuffer?: number}} ParsedWispPacket