Browse Source

started work on TCPProxy. Wrapper around TCPServerSocket and TCPSocket from directSocketsApi. refresh of cx files, removed old bad implementation of directSocketsApi spec

oscar 2 months ago
parent
commit
290d9a010e
10 changed files with 877 additions and 712 deletions
  1. 155 143
      assets/cx_esm.js
  2. 155 143
      cx.js
  3. 313 0
      src/lib/TCPProxy.js
  4. 172 0
      src/lib/TCPSocketClient.js
  5. 54 1
      src/lib/WebVM.svelte
  6. 5 5
      src/lib/network.js
  7. 0 183
      src/lib/socket-connection.ts
  8. 0 69
      src/lib/socket-log.ts
  9. 0 141
      src/lib/socket-server.ts
  10. 23 27
      src/lib/streams.js

File diff suppressed because it is too large
+ 155 - 143
assets/cx_esm.js


File diff suppressed because it is too large
+ 155 - 143
cx.js


+ 313 - 0
src/lib/TCPProxy.js

@@ -0,0 +1,313 @@
+import { TCPSocketClient  } from "./TCPSocketClient";
+
+export class TCPProxy {
+  constructor() {
+    this.server = null;
+    this.inboundConnections = new Map();  // clientID -> TCPSocketClient
+    this.outboundConnections = new Map(); // clientID -> TCPSocketClient
+    this.forwardingPairs = new Map();     // inboundID -> outboundID
+    this.reverseForwardingPairs = new Map(); // outboundID -> inboundID
+    this._reader = null;
+
+    this.nextConnectionID = 1;
+
+    // event callbacks
+    this.onServerStart = null;
+    this.onInboundConnect = null;
+    this.onOutboundConnect = null;
+    this.onInboundData = null;
+    this.onOutboundData = null;
+    this.onInboundClose = null;
+    this.onOutboundClose = null;
+    this.onError = null;
+  }
+
+  _generateConnectionID() {
+    return this.nextConnectionID++;
+  }
+
+  async start(localAddress, options = {}) {
+    try {
+      this.server = new TCPServerSocket(localAddress, options); // localAddress instead of ::1 but testing for now
+      console.log(`Server created: `, this.server);
+      // console.log(`opened properties: `, Object.getOwnPropertyDescriptor(this.server, 'opened'));
+      // console.log(`server properties: `, Object.getOwnPropertyDescriptor(this.server));
+      const openInfo = await this.server.opened;
+      const { readable, localAddress: boundAddress, localPort } = openInfo;
+
+      console.log(`TCPProxy started on ${boundAddress}:${localPort}`);
+      if (this.onServerStart) {
+        this.onServerStart(openInfo);
+      }
+
+      this._reader = readable.getReader();
+
+      // accept connections in a loop
+      (async () => {
+        try {
+          while (true) {
+            const {value: incomingSocket, done} = await this._reader.read();
+            if (done) {
+              console.log(`Server stopped accepting connections`);
+              break;
+            }
+            this._handleIncomingConnection(incomingSocket);
+          }
+        } catch (e) {
+          console.error(`Error accepting connections: `, e);
+          if (this.onError) {
+            this.onError(e);
+          }
+        } finally {
+          this._reader.releaseLock();
+        }
+      })();
+      return openInfo;
+    } catch (e) {
+      console.error(`Failed to start Proxy server: `, e);
+      if (this.onError) {
+        this.onError(e);
+      }
+      throw e;
+    }
+  }
+
+  async _handleIncomingConnection(incomingSocket) {
+    try {
+      const connectionID = this._generateConnectionID();
+      console.log(`Assigned id: ${connectionID}`);
+      const inboundClient = new TCPSocketClient();
+      inboundClient.socket = incomingSocket;
+
+      const openInfo = await incomingSocket.opened;
+      inboundClient._readable = openInfo.readable;
+      inboundClient._writable = openInfo.writable;
+
+      inboundClient.onData = (data) => {
+        this._handleInboundData(connectionID, data);
+      };
+      inboundClient.onClose = () => {
+        this._handleInboundClose(connectionID);
+      };
+      inboundClient.onError = (error) => {
+        if (this.onError) {
+          this.onError('inbound', connectionID, e);
+        }
+      };
+
+      inboundClient._startReading();
+      this.inboundConnections.set(connectionID, inboundClient);
+      console.log(`Accepted inbound connection ${connectionID} from ${openInfo.remoteAddress}:${openInfo.remotePort}`);
+      if (this.onInboundConnect) {
+        this.onInboundConnect(connectionID, openInfo);
+      }
+
+      return connectionID;
+    } catch (e) {
+      console.error(`Error handling incoming connection: `, e);
+      if (this.onError) {
+        this.onError(`inbound`, `unknown`, e);
+      }
+    }
+  }
+
+  _handleInboundData(inboundID, data) {
+    if (!data || data.byteLength === 0) {
+      return;
+    }
+    if (this.onInboundData) {
+      this.onInboundData(inboundID, data);
+    }
+
+    const outboundID = this.forwardingPairs.get(inboundID);
+    if (outboundID) {
+      const outboundClient = this.outboundConnections.get(outboundID);
+      if (outboundClient) {
+        outboundClient.send(data).catch((e) => {
+          console.error(`Error forwarding data to outbound connection ${outboundID}`);
+          if (this.onError) {
+            this.onError(`outbound`, outboundID, e);
+          }
+        });
+      }
+    }
+  }
+
+  _handleOutboundData(outboundID, data) {
+    if (!data || data.byteLength === 0) {
+      return;
+    }
+    if (this.onOutboundData) {
+      this.onOutboundData(outboundID, data);
+    }
+
+    const inboundID = this.reverseForwardingPairs.get(outboundID);
+    if (inboundID) {
+      const inboundClient = this.inboundConnections.get(inboundID);
+      if (inboundClient) {
+        inboundClient.send(data).catch((e) => {
+          console.error(`Error forward data to inbound connection ${inboundID}`);
+          if (this.onError) {
+            this.onError(`inbound`, inboundID, e);
+          }
+        });
+      }
+    }
+  }
+
+  _handleInboundClose(inboundID) {
+    const inboundClient = this.inboundConnections.get(inboundID);
+    this.inboundConnections.delete(inboundID);
+    console.log(`Inbond connection ${inboundID} closed`);
+
+    if (this.onInboundClose) {
+      this.onInboundClose(inboundID);
+    }
+
+    // close pair
+    const outboundID = this.forwardingPairs.get(inboundID);
+    if (outboundID) {
+      const outboundClient = this.outboundConnections.get(outboundID);
+      if (outboundClient) {
+        outboundClient.close().catch((e) => {
+          console.error(`Error closing outbound connection ${outboundID}`);
+        });
+      }
+
+      this.forwardingPairs.delete(inboundID);
+      this.reverseForwardingPairs.delete(outboundID);
+    }
+  }
+
+  _handleOutboundClose(outboundID) {
+    const outboundClient = this.outboundConnections.get(outboundID);
+    this.outboundConnections.delete(outboundID);
+    console.log(`Outbound connection ${outboundID} closed`);
+
+    if (this.onOutboundClose) {
+      this.onOutboundClose(outboundID);
+    }
+
+    // close pair
+    const inboundID = this.reverseForwardingPairs.get(outboundID);
+    if (inboundID) {
+      const inboundClient = this.inboundConnections.get(inboundID);
+      if (inboundClient) {
+        inboundClient.close().catch((e) => {
+          console.error(`Error closing inbound connection ${inboundID}`, e);
+        });
+      }
+
+      this.reverseForwardingPairs.delete(outboundID);
+      this.forwardingPairs.delete(inboundID);
+    }
+  }
+
+  async connect(remoteAddress, remotePort, options = {}) {
+    try {
+      const connectionID = this._generateConnectionID();
+      const outboundClient = new TCPSocketClient(remoteAddress, remotePort, options);
+
+      outboundClient.onOpen = (openInfo) => {
+        console.log(`Established outbound connection ${connectionID} to ${remoteAddress}:${remotePort}`);
+
+        if (this.onOutboundConnect) {
+          this.onOutboundConnect(connectionID, openInfo);
+        }
+      };
+      outboundClient.onData = (data) => {
+        this._handleOutboundData(connectionID, data);
+      };
+      outboundClient.onClose = () => {
+        this._handleOutboundClose(connectionID);
+      };
+      outboundClient.onError = (e) => {
+        if (this.onError) {
+          this.onError(`outbound`, connectionID, e);
+        }
+      };
+      await outboundClient.connect();
+      this.outboundConnections.set(connectionID, outboundClient);
+      return connectionID;
+    } catch (e) {
+      console.error(`Error connecting to ${remoteAddress}:${remotePort}: `, e);
+      if (this.onError) {
+        this.onError(`outbound`, `unknown`, e);
+      }
+      throw e;
+    }
+  }
+
+  link(inboundID, remoteAddr, remotePort, options = {}) {
+    return new Promise(async (resolve, reject) => {
+      try {
+        if (!this.inboundConnections.has(inboundID)) {
+          throw new Error(`Inbound connection ${inboundID} not found`);
+        }
+
+        const outboundID = await this.connect(remoteAddr, remotePort, options);
+
+        this.forwardingPairs.set(inboundID, outboundID);
+        this.reverseForwardingPairs.set(outboundID, inboundID);
+
+        console.log(`Linked ${inboundID} <-> ${outboundID} (${remoteAddr}:${remotePort})`);
+        resolve({inboundID, outboundID});
+      } catch (e) {
+        console.error(`Error creating tunnel for ${inboundID} to ${remoteAddr}:${remotePort}: `, e);
+        reject(e);
+      }
+    });
+  }
+
+  closeInbound(inboundID) {
+    const client = this.inboundConnections.get(inboundID);
+    if (client) {
+      return client.close();
+    }
+    return Promise.resolve();
+  }
+
+  closeOutbound(outboundID) {
+    const client = this.outboundConnections.get(outboundID);
+    if (client) {
+      return client.close();
+    }
+    return Promise.resolve();
+  }
+
+  // need to rework so that we releaseLock() before closing() server reader. Current structure sucks for that
+  async close() {
+    console.log(`Closing TCP proxy`);
+    for (const [id, client] of this.inboundConnections.entries()) {
+      try {
+        await client.close();
+      } catch (e) {
+        console.error(`Error closing inbound connection ${id}: `, e);
+      }
+    }
+    for (const [id, client] of this.outboundConnections.entries()) {
+      try {
+        await client.close();
+      } catch (e) {
+        console.error(`Error closing outbound connection ${id}: `, e);
+      }
+    }
+
+    this.inboundConnections.clear();
+    this.outboundConnections.clear();
+    this.forwardingPairs.clear();
+    this.reverseForwardingPairs.clear();
+
+    if (this.server) {
+      try {
+        await this.server.close();
+      } catch (e) {
+        console.error(`Error closing server: `, e);
+      }
+      this.server = null;
+    }
+
+    console.log("TCP Proxy closed");
+  }
+  
+}

+ 172 - 0
src/lib/TCPSocketClient.js

@@ -0,0 +1,172 @@
+export class TCPSocketClient {
+  constructor(remoteAddress, remotePort, options = {}) {
+    this.socket = null;
+    this.remoteAddress = remoteAddress;
+    this.remotePort = remotePort;
+    this.options = options;
+
+    // internals
+    this._readable = null;
+    this._writable = null;
+    this._reader = null;
+    this._writer;
+
+    this._isOpenedSettled = false;
+    this._isClosedSettled = false;
+    this._openedPromise = new Promise((resolve, reject) => {
+      this._resolveOpened = (value) => {
+        this._isOpenedSettled = true;
+        resolve(value);
+      };
+
+      this._rejectOpened = (reason) => {
+        this._isOpenedSettled = true;
+        reject(reason);
+      };
+    });
+    this._closedPromise = new Promise((resolve, reject) => {
+      this._resolveClosed = (value) => {
+        this._isClosedSettled = true;
+        resolve(value);
+      };
+      this._rejectClosed = (reason) => {
+        this._isClosedSettled = true;
+        reject(reason);
+      };
+    });
+
+    // event callbacks
+    this.onOpen = null;
+    this.onData = null;
+    this.onClose = null;
+    this.onError = null;
+  }
+
+  get opened() {
+    return this._openedPromise;
+  }
+
+  get closed() {
+    return this._closedPromise;
+  }
+
+  async connect(timeoutMs = 5000) {
+    try {
+      let timeoutID;
+      const timeoutPromise = new Promise((_, reject) => {
+        timeoutID = setTimeout(() => {
+          console.log(`Connection to ${this.remoteAddress}:${this.remotePort} timed out after ${timeoutMs}ms`);
+          reject(new Error("Connection timeout"));
+        }, timeoutMs);
+      });
+
+      this.socket = new TCPSocket(this.remoteAddress, this.remotePort, this.options);
+      const openInfo = await Promise.race([this.socket.opened, timeoutPromise]);
+      clearTimeout(timeoutID);
+      // const openInfo = await this.socket.opened;
+
+      this._readable = openInfo.readable;
+      this._writable = openInfo.writable;
+
+      if (this.onData) {
+        this._startReading();
+      }
+      if (this.onOpen) {
+        this.onOpen(openInfo);
+      }
+
+      this._resolveOpened(openInfo);
+      return openInfo;
+    } catch (e) {
+      this._rejectOpened(e);
+      this._rejectClosed(e);
+
+      if (this.onError) {
+        this.onError(e);
+      }
+
+      throw e;
+    }
+  }
+
+  async _startReading() {
+    try {
+      this._reader = this._readable.getReader();
+      while (true) {
+        const {value, done} = await this._reader.read();
+
+        if (done) {
+          break;
+        }
+        if (value && value.byteLength > 0) {
+          if (this.onData) {
+            this.onData(value);
+          }
+        }
+      }
+    } catch (e) {
+      if (this._reader) {
+        this._reader.releaseLock();
+        this._reader = null;
+      }
+    }
+  }
+
+  async send(data) {
+    if (!this._writable) {
+      throw new Error(`Socket is not connected`);
+    }
+
+    try {
+      this._writer = this._writable.getWriter();
+      let buffer = data;
+      if (typeof data === `string`) {
+        const encoder = new TextEncoder();
+        buffer = encoder.encode(data);
+      }
+
+      await this._writer.write(buffer);
+
+      await this._writer.releaseLock();
+      this._writer = null;
+      return true;
+    } catch (e) {
+      if (this.onError) {
+        this.onError(e);
+      }
+      throw error;
+    }
+  }
+
+  async close() {
+    try {
+      if (!this.socket) {
+        throw new Error(`Socket is not connected`);
+      }
+
+      if (this._isClosedSettled) {
+        return this._closedPromise;
+      }
+
+      // if streams are locked err
+      if ((this._readable && this._readable.locked) || (this._writable && this._writable.locked)) {
+        throw new Error(`Cannot close socket while streams are locked`);
+      }
+
+      await this.socket.close();
+      if (this.onClose) {
+        this.onClose();
+      }
+
+      this._resolveClosed();
+      return this._closedPromise;
+    } catch (e) {
+      this._rejectClosed(e);
+      if (this.onError) {
+        this.onError(e);
+      }
+      
+      throw error;
+    }
+  }
+}

+ 54 - 1
src/lib/WebVM.svelte

@@ -11,6 +11,7 @@
 	import { introMessage, errorMessage, unexpectedErrorMessage } from '$lib/messages.js'
 	import { displayConfig, handleToolImpl } from '$lib/anthropic.js'
 	import { tryPlausible } from '$lib/plausible.js'
+	import { TCPProxy } from '$lib/TCPProxy.js'
 
 	export let configObj = null;
 	export let processCallback = null;
@@ -19,6 +20,11 @@
 	export let diskLatencies = [];
 	export let activityEventsInterval = 0;
 
+	// TCP
+	var server = null;
+	var serverBytes = 0;
+	var serverConnections = 0;
+	
 
 	var term = null;
 	var cx = null;
@@ -185,8 +191,54 @@
 		if(display)
 			setScreenSize(display);
 	}
+	async function initProxy() {
+		const proxy = new TCPProxy();
+		proxy.onServerStart = (openInfo) => {
+			console.log(`Server started on ${openInfo.localAddress}:${openInfo.localPort}`);
+		};
+		proxy.onInboundConnect = (id, openInfo) => {
+			console.log(`New inbound connection ${id} from ${openInfo.remoteAddress}:${openInfo.remotePort}`);
+		};
+		proxy.onOutboundConnect = (id, openInfo) => {
+			console.log(`New Outbound connection ${id} to ${openInfo.remoteAddress}:${openInfo.remotePort}`);
+		};
+		proxy.onInboundData = (id, data) => {
+			console.log(`Received data from ${id}: `, data);
+		};
+		proxy.onInboundClose = (id) => {
+			console.log(`Closed inbound connection ${id}`);
+		};
+		proxy.onOutboundClose = (id) => {
+			console.log(`Closed outbound connection ${id}`);
+		};
+		proxy.onError = (type, id, e) => {
+			console.error(`Error in ${type} ${id}: `, e);
+		};
+		await proxy.start('0.0.0.0', {localPort: 33000});
+		return proxy;
+	}
 	async function initTerminal()
 	{
+		const proxy = await initProxy();
+		console.log(`We have proxy`);
+		const outboundID = await proxy.connect('google.com', 80);
+		console.log(`Created direct outbound connection ${outboundID}`);
+		const outboundClient = proxy.outboundConnections.get(outboundID);
+		if (outboundClient) {
+		 console.log(`We have a client`);
+		 const httpRequest =
+		 				"GET / HTTP/1.1\r\n" +
+		 				"Host: google.com\r\n" +
+		 				"Connection: close\r\n\r\n";
+		 await outboundClient.send(httpRequest);
+		 console.log(`Sent data to ${outboundID}`);
+		}
+		outboundClient._reader.releaseLock();
+		console.log(`Released Lock`);
+		await outboundClient.close();
+		// await proxy.close();
+
+
 		const { Terminal } = await import('@xterm/xterm');
 		const { FitAddon } = await import('@xterm/addon-fit');
 		const { WebLinksAddon } = await import('@xterm/addon-web-links');
@@ -241,6 +293,7 @@
 	function handleProcessCreated()
 	{
 		processCount++;
+		console.log("in handleProcessCreated, callback ", processCallback.body);
 		if(processCallback)
 			processCallback(processCount);
 	}
@@ -307,7 +360,7 @@
 		];
 		try
 		{
-			cx = await CheerpX.Linux.create({mounts: mountPoints, networkInterface: directSocketsInterface});
+			cx = await CheerpX.Linux.create({mounts: mountPoints, networkInterface: networkInterface, directSocketsInterface: directSocketsInterface});
 		}
 		catch(e)
 		{

+ 5 - 5
src/lib/network.js

@@ -148,15 +148,15 @@ export function updateButtonData(state, handleConnect) {
 	}
 }
 
-export const networkInterface = { authKey: authKey, controlUrl: controlUrl, loginUrlCb: loginUrlCb, stateUpdateCb: stateUpdateCb, netmapUpdateCb: netmapUpdateCb };
 
+export const networkInterface = { authKey: authKey, controlUrl: controlUrl, loginUrlCb: loginUrlCb, stateUpdateCb: stateUpdateCb, netmapUpdateCb: netmapUpdateCb };
 export const networkData = { currentIp: null, connectionState: connectionState, exitNode: exitNode, loginUrl: null, dashboardUrl: dashboardUrl }
 
+
 //
 // IWA test
 // 
-// let localhost = "localhost";
-let host = "127.0.0.1";
+let host = "127.0.0.1"; // localhost
 let port = 4321;
-let kind = "TCP";
-export const directSocketsInterface = { host, port, kind};
+export const directSocketsInterface = { host: host, port: port };
+

+ 0 - 183
src/lib/socket-connection.ts

@@ -1,183 +0,0 @@
-/**
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Socket connection web component
- */
-class SocketConnection extends HTMLElement {
-  socket: TCPSocket | undefined;
-  reader: ReadableStreamDefaultReader | undefined;
-  address: string | undefined;
-  port: number | undefined;
-  incomingContent: HTMLElement | undefined;
-  logElement: HTMLElement | undefined;
-
-  constructor() {
-    super();
-  }
-
-  static get observedAttributes() {
-    return ['address', 'port', 'log'];
-  }
-
-  attributeChangedCallback(
-    property: string,
-    oldValue: string,
-    newValue: string,
-  ) {
-    if (oldValue === newValue) return;
-    if (property === 'address') {
-      this.address = newValue;
-    }
-
-    if (property === 'port') {
-      this.port = parseInt(newValue);
-    }
-
-    if (property === 'log' && this.logElement) {
-      console.log(`incoming content ${newValue}`);
-      this.logElement?.setAttribute('input', newValue);
-    }
-  }
-  /**
-   * Setup when socket connection component is appended to DOM.
-   */
-  async connectedCallback() {
-    const shadow = this.attachShadow({ mode: 'open' });
-
-    const template = document
-      .getElementById('socket-connection')
-      ?.content.cloneNode(true);
-
-    const logOutput = template.querySelector('#connection');
-
-    const messageInput = template.querySelector('#messageInput');
-    const sendButton = template.querySelector('#sendButton');
-    const disconnectButton = template.querySelector('#disconnectButton');
-
-    this.logElement = template.querySelector('#log');
-
-    // automatically connect to server
-    logOutput.textContent = 'Trying to connect...';
-    disconnectButton.disabled = true;
-    const connected = await this.connectToServer();
-    if (connected) {
-      disconnectButton.textContent = 'Disconnect';
-      disconnectButton.disabled = false;
-      logOutput.textContent = `Successfully connected to ${this.address} at port ${this.port}`;
-    } else {
-      disconnectButton.textContent = 'Remove socket';
-      disconnectButton.disabled = false;
-      logOutput.textContent = `Failed to connect to ${this.address} at port ${this.port}`;
-    }
-
-    sendButton.addEventListener('click', async (e) => {
-      e.preventDefault();
-      disconnectButton.disabled = true;
-
-      const sendEvent = new CustomEvent('send', {
-        bubbles: true,
-        cancelable: false,
-        detail: {
-          message: messageInput.value,
-        },
-      });
-      this.dispatchEvent(sendEvent);
-
-      disconnectButton.disabled = false;
-      messageInput.value = '';
-    });
-
-    disconnectButton.addEventListener('click', async () => {
-      sendButton.disabled = true;
-      messageInput.disabled = true;
-      await this.disconnectButtonCallback(disconnectButton, logOutput);
-    });
-
-    shadow.append(template);
-  }
-
-  /**
-   * Callback to handle disconnect button click events.
-   * Updates UI and calls methods for disconnecting from server.
-   */
-  async disconnectButtonCallback(
-    disconnectButton: HTMLButtonElement,
-    logOutput: HTMLParagraphElement,
-  ) {
-    if (this.socket) {
-      logOutput.textContent = 'Trying to disconnect...';
-      disconnectButton.disabled = true;
-      // disconnect from server
-      const disconnected = await this.disconnectFromServer();
-      if (disconnected) {
-        logOutput.textContent = `Disconnected from ${this.address} at port ${this.port}`;
-        const closeEvent = new CustomEvent('close', {
-          bubbles: true,
-          cancelable: false,
-        });
-        this.dispatchEvent(closeEvent);
-      } else {
-        disconnectButton.disabled = false;
-        logOutput.textContent = `Failed to disconnect from ${this.address} at port ${this.port}`;
-      }
-    }
-  }
-
-  /**
-   * Disconnect from server.
-   */
-  async disconnectFromServer(): Promise<boolean> {
-    try {
-      if (this.socket) {
-        if (this.reader) {
-          this.reader.releaseLock();
-        }
-        await this.socket.close();
-      }
-    } catch (e) {
-      console.log(e);
-      return Promise.resolve(false);
-    }
-    this.socket = undefined;
-    return Promise.resolve(true);
-  }
-
-  /**
-   * Connect to server.
-   */
-  async connectToServer(): Promise<boolean> {
-    try {
-      // Create socket
-      if (this.address && this.port) {
-        this.socket = new TCPSocket(this.address, this.port);
-      }
-
-      if (!this.socket) {
-        return Promise.resolve(false);
-      }
-    } catch (e) {
-      console.log(e);
-      await this.disconnectFromServer();
-      return Promise.resolve(false);
-    }
-
-    return Promise.resolve(true);
-  }
-}
-
-// customElements.define('socket-connection', SocketConnection);
-export default SocketConnection;

+ 0 - 69
src/lib/socket-log.ts

@@ -1,69 +0,0 @@
-/**
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-class SocketLog extends HTMLElement {
-  input: string | undefined;
-  header: HTMLElement | undefined;
-  log: HTMLElement | undefined;
-
-  constructor() {
-    super();
-  }
-
-  static get observedAttributes() {
-    return ['input'];
-  }
-
-  attributeChangedCallback(
-    property: string,
-    oldValue: string,
-    newValue: string,
-  ) {
-    if (oldValue === newValue) {
-      return;
-    }
-
-    console.log(
-      `Attribute: ${property} changed from ${oldValue} to ${newValue}`,
-    );
-
-    if (property === 'input') {
-      this.input = newValue;
-      if (this.header) {
-        this.header.textContent = 'Latest transmission';
-      }
-      if (this.log) {
-        this.log.textContent = newValue;
-      }
-    }
-  }
-
-  async connectedCallback() {
-    const shadow = this.attachShadow({ mode: 'open' });
-    const template = document
-      .getElementById('socket-log')
-      ?.content.cloneNode(true);
-
-    // Add values to template
-    this.header = template.querySelector('#log-header');
-    this.log = template.querySelector('#log');
-
-    shadow.append(template);
-  }
-}
-
-// customElements.define('socket-log', SocketLog);
-export default SocketLog;

+ 0 - 141
src/lib/socket-server.ts

@@ -1,141 +0,0 @@
-/**
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-class SocketServer extends HTMLElement {
-  server: TCPServerSocket | undefined;
-  connections: number | undefined;
-  bytes: number | undefined;
-  address: string | undefined;
-  port: number | undefined;
-  log: string | undefined;
-  portElement: HTMLElement | undefined;
-  addressElement: HTMLElement | undefined;
-  connectionsElement: HTMLElement | undefined;
-  bytesElement: HTMLElement | undefined;
-  logElement: HTMLElement | undefined;
-
-  constructor() {
-    super();
-  }
-
-  static get observedAttributes() {
-    return ['address', 'port', 'connections', 'bytes', 'log'];
-  }
-
-  update() {
-    if (this.portElement) {
-      this.portElement.textContent = this.port?.toString() || '';
-    }
-
-    if (this.addressElement) {
-      this.addressElement.textContent = this.address || '';
-    }
-
-    if (this.connectionsElement) {
-      this.connectionsElement.textContent = this.connections?.toString() || '0';
-    }
-
-    if (this.bytesElement) {
-      this.bytesElement.textContent = this.bytes?.toString() || '0';
-    }
-
-    if (this.logElement) {
-      if (this.log) {
-        this.logElement.setAttribute('input', this.log || '');
-      }
-    }
-  }
-
-  attributeChangedCallback(
-    property: string,
-    oldValue: string,
-    newValue: string,
-  ) {
-    if (oldValue === newValue) {
-      return;
-    }
-
-    console.log(
-      `Attribute ${property} changed from ${oldValue} to ${newValue}`,
-    );
-
-    switch (property) {
-      case 'address':
-        this.address = newValue;
-        break;
-      case 'port':
-        this.port = parseInt(newValue);
-        break;
-      case 'connections':
-        this.connections = parseInt(newValue);
-        break;
-      case 'bytes':
-        this.bytes = parseInt(newValue);
-        break;
-      case 'log':
-        this.log = newValue;
-        break;
-    }
-
-    this.update();
-  }
-
-  async connectedCallback() {
-    const shadow = this.attachShadow({ mode: 'open' });
-    const template = document
-      .getElementById('socket-server')
-      ?.content.cloneNode(true);
-
-    console.log('IN');
-
-    console.log(this.server);
-
-    // Add values to template
-    this.portElement = template.querySelector('#port') as HTMLElement;
-
-    this.addressElement = template.querySelector('#address') as HTMLElement;
-
-    this.connectionsElement = template.querySelector(
-      '#connections',
-    ) as HTMLElement;
-
-    this.bytesElement = template.querySelector('#bytes') as HTMLElement;
-
-    this.logElement = template.querySelector('#log') as HTMLElement;
-
-    const messageInput = template.querySelector('#messageInput');
-    const sendButton = template.querySelector('#sendButton');
-
-    sendButton.addEventListener('click', async (e) => {
-      e.preventDefault();
-      const sendEvent = new CustomEvent('send', {
-        bubbles: true,
-        cancelable: false,
-        detail: {
-          message: messageInput.value,
-        },
-      });
-      this.dispatchEvent(sendEvent);
-      messageInput.value = '';
-    });
-
-    shadow.append(template);
-    this.update();
-  }
-}
-
-// customElements.define('socket-server', SocketServer);
-export default SocketServer;

+ 23 - 27
src/lib/streams.ts → src/lib/streams.js

@@ -14,21 +14,21 @@
  * limitations under the License.
  */
 
+ //
+ // -- MODIFIED TO JS FOR WEBVM --
+ // 
+
 export async function readStream(
-  reader: ReadableStreamDefaultReader,
-  cb: (value: Uint8Array) => void,
-): Promise<void> {
-  // Read from the socket until it's closed
+  reader,
+  cb
+) {
   while (reader) {
-    // Wait for the next chunk
     const { value, done } = await reader.read();
 
-    // Send the chunk to the callback
     if (value) {
       cb(value);
     }
 
-    // Release the reader if we're done
     if (done) {
       reader.releaseLock();
       break;
@@ -37,49 +37,45 @@ export async function readStream(
 }
 
 export async function writeStream(
-  socket: TCPSocket,
-  message: string,
-): Promise<void> {
-  // Wait for the socket to be opened
+    socket,
+    message
+) {
   const connection = await socket.opened;
-  // Get a writer to write to the socket
-  const writer = connection?.writable.getWriter();
+
+  if (!connection || !connection.writable) {
+    console.error("Socket connection or writable is null");
+  }
+
+  const writer = connection.writable.getWriter();
   const encoder = new TextEncoder();
-  writer.write(encoder.encode(message));
+  await writer.write(encoder.encode(message));
   writer.releaseLock();
 }
+
 export async function collectConnections(
-  server: TCPServerSocket,
-  infoCB: (address: string, port: number) => void,
-  connectionCB: (value: TCPSocket) => Promise<void>,
-): Promise<void> {
-  // Wait for the server to be opened
+  server,
+  infoCB,
+  connectionCB
+) {
   const { readable, localAddress, localPort } = await server.opened;
 
-  // Get a reader to read from the server
-  // These will be connections to the server
   const connections = readable.getReader();
 
-  // Send the server info to the callback
+  // send server info to the callback
   infoCB(localAddress, localPort);
 
-  // Read connections from the server until all connections are closed
   while (connections) {
     const { value: connection, done } = await connections.read();
 
-    // Send the connection to the callback
     if (connection) {
       connectionCB(connection);
     }
 
-    // Release the connection if we're done
     if (done) {
       connections.releaseLock();
       break;
     }
   }
-
-  // Wait for the server to be closed
   await server.closed;
   console.log('Closed');
 }

Some files were not shown because too many files changed in this diff