diff --git a/notes.md b/notes.md index 10b25b4d..40bffa56 100644 --- a/notes.md +++ b/notes.md @@ -5,3 +5,4 @@ The packet based approach in the ASK for the new handshake is the first step but Client Hello Server Hello Server Connection ID Packet Sent to Client Client responds with new server connection ID - connection is established (if fails at this step just re-transmit next packets) +Allows for Head and Data like a full request in the event a certificate is requested if not then it can terminate early on in the packet process diff --git a/scripts/certificates.js b/scripts/certificates.js index 003ea31a..d74eeb40 100644 --- a/scripts/certificates.js +++ b/scripts/certificates.js @@ -35,7 +35,7 @@ const domainProfile = await createProfile({ // Encrypt public key sent in the packet // encryptClientKey: 'sealedbox', // encryptServerKey: 'sealedbox', - encryptKeypair: true + // encryptKeypair: true }, // Max connection id size in bytes usually randomly generated and checked but used to calculate max packet payload size connectionIdSize: 8, @@ -47,11 +47,18 @@ const domainProfile = await createProfile({ // maxHeadSize: 1200, // heartbeat is an interval check for when a client must send something to the server to remain connected heartbeat: 30000, + url: 'universal.web', + domain: 'universal', + extension: 'web', ip: '::1', port: 8888, - domain: 'universal.web', - hostname: 'universal', - extension: 'web', + // Used when a custom Domain name server is used to resolve the domain name locations still provides valid certificates else will be warned of invalid certificate + // domainInfoServer: { + // url: 'dis.universal.web', + // subdomain: 'dis', + // domain: 'universal', + // extension: 'web', + // }, // Shows the browser how to display the domain name for humans correctly encoding: 'utf8', entity: { @@ -73,12 +80,16 @@ const domainProfile = await createProfile({ publicKey: true }, // This allows a browser or connection to use the realtime mode for the UDSP connection + // Allowing it to act like a webtransport, websocket, and or traditional HTTP like connection realtime: true, // blocked methods mean methods the server doesn't permit // blockedMethods: ['open'], allowedMethods: ['get', 'connect', 'open', 'file', 'stream', 'close'], + // deafult compression of payload data only compression: true, - headerCompression: true, + // packetCompression: true, + // headerCompression: true, + // footerCompression: true, autoLogin: true, }, master: {}, diff --git a/todo.md b/todo.md index 308980df..04ed6036 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,5 @@ # TODO -Swap to simple binary IDs instead of UTF8 strings -Swap to simple binary Codes +Swap to simple numerical Codes Minify property names for packets Minify property names for certificates diff --git a/udsp/client/index.js b/udsp/client/index.js index 847bd40a..7e5a2c8d 100644 --- a/udsp/client/index.js +++ b/udsp/client/index.js @@ -43,7 +43,7 @@ import { onPacket } from './onPacket.js'; import { intro } from './intro.js'; import { onListening } from './listening.js'; import { keychainGet } from '#keychain'; -import { Ask } from '../ask.js'; +import { Ask } from '../request/ask.js'; import { fetchRequest } from '../fetch.js'; import { UDSP } from '#udsp/base'; // UNIVERSAL WEB Client Class diff --git a/udsp/client/send.js b/udsp/client/send.js index 46c176bb..10c34771 100644 --- a/udsp/client/send.js +++ b/udsp/client/send.js @@ -5,11 +5,7 @@ import { promise } from '@universalweb/acid'; import { encodePacket } from '#udsp/encodePacket'; import { sendPacket } from '#udsp/sendPacket'; imported('Client Send'); -export async function send(packet) { - const packetConfig = { - source: this, - packet, - }; +export async function send(packet, options) { console.log(`client.send to Server`, this.destination.port, this.destination.ip); - return sendPacket(packetConfig); + return sendPacket(packet, this, this.socket); } diff --git a/udsp/encodePacket.js b/udsp/encodePacket.js index 6d4dc03a..136811ba 100644 --- a/udsp/encodePacket.js +++ b/udsp/encodePacket.js @@ -16,13 +16,13 @@ import { objectSize } from '@universalweb/acid'; import { toBase64 } from '#crypto'; -export async function encodePacket(config) { +export async function encodePacket(packet, source, destination) { success(`PROCESSING ENCODE PACKET`); const { - source, - options, - packet: message - } = config; + message, + header: headers, + footer + } = packet; const { state, isClient, @@ -30,64 +30,56 @@ export async function encodePacket(config) { isServerEnd, isServerClient } = source; - const header = options?.header || {}; - const footer = options?.footer; - const destination = source.destination || config.destination; let id = destination.id || source.id; - const { cryptography } = source; - let encryptConnectionId; - if (isServerEnd) { - encryptConnectionId = cryptography.config.encryptClientConnectionId; - } else { - encryptConnectionId = cryptography.config.encryptServerConnectionId; - } if (!id) { return console.error(`ID IS'T ASSIGNED`); } + const { cryptography } = source; + let encryptConnectionId = cryptography.config.encryptConnectionId; + if (!encryptConnectionId) { + if (isServerEnd) { + encryptConnectionId = cryptography.config.encryptClientConnectionId; + } else { + encryptConnectionId = cryptography.config.encryptServerConnectionId; + } + } if (encryptConnectionId) { // console.log(destination); - if (encryptConnectionId === 'sealedbox') { - if (isServerEnd) { - id = cryptography.encryptClientConnectionId(id, destination.connectionIdKeypair); - } else { - id = cryptography.encryptServerConnectionId(id, destination.connectionIdKeypair); - } + if (isServerEnd) { + id = cryptography.encryptClientConnectionId(id, destination.connectionIdKeypair); + } else { + id = cryptography.encryptServerConnectionId(id, destination.connectionIdKeypair); } if (!id) { return console.error(`Connection ID Encrypt failed method given ${encryptConnectionId}`); } } - header.id = id; + let header; + if (headers && objectSize(headers)) { + header = headers; + header.id = id; + } else { + header = id; + } // console.log(config); message.t = Date.now(); - if (isClient) { - if (state === 0) { - console.log('DESTINATION ENCRYPT PUBLIC KEY', toBase64(destination.encryptKeypair.publicKey)); - if (!header.key) { - header.key = source.encryptKeypair.publicKey; - } - } - } if (header.key) { const { encryptClientKey, encryptServerKey } = cryptography.config; if (isClient) { - if (encryptClientKey === 'sealedbox') { + if (encryptClientKey) { header.key = cryptography.encryptClientKey(header.key, destination.encryptKeypair); } } if (isServerEnd) { - if (encryptServerKey === 'sealedbox') { + if (encryptServerKey) { header.key = cryptography.encryptServerKey(header.key, destination.encryptKeypair); } } } - if (options) { - console.log('Packet Options', options); - } - const headerEncoded = (objectSize(header) === 1 && header.id) ? encode(id) : encode(header); + const headerEncoded = encode(header); const messageEncoded = encode(message); const ad = (footer) ? Buffer.concat([headerEncoded, footer]) : headerEncoded; const encryptedMessage = cryptography.encrypt(messageEncoded, source.sessionKeys, ad); @@ -98,16 +90,16 @@ export async function encodePacket(config) { if (footer) { packetStructure[2] = encode(footer); } - const packet = encode(packetStructure); + const packetEncoded = encode(packetStructure); info(`clientId: ${toBase64(header.id)}`); info(`Transmit Key ${toBase64(source.sessionKeys.transmitKey)}`); - const packetSize = packet.length; + const packetSize = packetEncoded.length; console.log('Size Unencrypted', encode([headerEncoded, messageEncoded]).length); info(`encoded Packet Size ${packetSize}`); if (packetSize >= 1328) { - console.log(packet); + console.log(packetEncoded); failed(`WARNING: Packet size is larger than max allowed size 1328 -> ${packetSize} over by ${packetSize - 1328}`); } success(`PROCESSED ENCODE PACKET`); - return packet; + return packetEncoded; } diff --git a/udsp/ask.js b/udsp/request/ask.js similarity index 86% rename from udsp/ask.js rename to udsp/request/ask.js index 116c07ae..a2296b92 100644 --- a/udsp/ask.js +++ b/udsp/request/ask.js @@ -11,7 +11,7 @@ import { decode, encode } from 'msgpackr'; import { failed, info, msgReceived, msgSent } from '#logs'; -import { Base } from './request/base.js'; +import { Base } from './base.js'; import { request } from '#udsp/request'; export class Ask extends Base { constructor(requestObject, options = {}, source) { @@ -24,7 +24,7 @@ export class Ask extends Base { data, method = 'get' } = requestObject; - const head = requestObject.head || requestObject.headers || options.head || options.headers || {}; + const head = requestObject.head || options.head; console.log('Ask', requestObject); const streamId = packetIdGenerator.get(); this.request.sid = streamId; @@ -48,9 +48,7 @@ export class Ask extends Base { } isAsk = true; type = 'ask'; - request = { - head: {} - }; + request = {}; } export async function ask(source) { return construct(Ask, omit); diff --git a/udsp/request/base.js b/udsp/request/base.js index bd2490a9..3cf36b72 100644 --- a/udsp/request/base.js +++ b/udsp/request/base.js @@ -12,6 +12,7 @@ import { import { encode, decode } from 'msgpackr'; import { request } from '#udsp/request'; import { assembleData } from './assembleData.js'; +import { toBase64 } from '#crypto'; export class Base { constructor(options = {}, source) { const { events, } = options; @@ -131,19 +132,19 @@ export class Base { this.state = 1; } const packet = this.getPacketTemplate(); - packet.setup = true; - packet.headerSize = this.outgoingHeadSize; + packet.message.setup = true; + packet.message.headerSize = this.outgoingHeadSize; this.sendPacket(packet); } sendIntro() { const packet = this.getPacketTemplate(); - packet.intro = true; + packet.message.intro = true; this.sendPacket(packet); } sendIntroReply() { const packet = this.getPacketTemplate(); - packet.introReply = true; - packet.scid = this.client().id; + packet.message.introReply = true; + packet.message.scid = this.client().id; this.sendPacket(packet); } sendFinished() { @@ -151,7 +152,7 @@ export class Base { this.state = 1; } const packet = this.getPacketTemplate(); - packet.intro = true; + packet.message.intro = true; this.sendPacket(packet); } sendHeadReady() { @@ -159,7 +160,7 @@ export class Base { this.state = 2; } const packet = this.getPacketTemplate(); - packet.headReady = true; + packet.message.headReady = true; this.sendPacket(packet); } sendDataReady() { @@ -167,12 +168,12 @@ export class Base { this.state = 3; } const packet = this.getPacketTemplate(); - packet.dataReady = true; + packet.message.dataReady = true; this.sendPacket(packet); } async sendEnd() { const packet = this.getPacketTemplate(); - packet.end = true; + packet.message.end = true; this.sendPacket(packet); } get headers() { @@ -203,12 +204,12 @@ export class Base { const headSize = this.outgoingHeadSize; while (currentBytePosition < this.outgoingHeadSize) { const packet = this.getPacketTemplate(); - packet.sid = sid; - packet.pid = packetId; + packet.message.sid = sid; + packet.message.pid = packetId; const endIndex = currentBytePosition + maxHeadSize; const safeEndIndex = endIndex > headSize ? headSize : endIndex; - packet.head = this.outgoingHead.subarray(currentBytePosition, safeEndIndex); - packet.headSize = packet.head.length; + packet.message.head = this.outgoingHead.subarray(currentBytePosition, safeEndIndex); + packet.message.headSize = packet.head.length; outgoingHeadPackets[packetId] = packet; if (safeEndIndex === headSize) { packet.last = true; @@ -313,13 +314,29 @@ export class Base { getPacketTemplate() { const { id, } = this; const packet = { - sid: id + message: { + sid: id + } }; return packet; } destroy = destroy; onPacket = onPacket; - sendPacket = sendPacket; + sendPacket(packet, options) { + this.source().send(packet, options); + } + addUDSPHeader(packet, headerName, headerValue) { + if (!packet.header) { + packet.header = {}; + } + packet.header[headerName] = headerValue; + } + addPublicKeyHeader(packet) { + const source = this.source(); + const key = source.encryptKeypair.publicKey; + console.log('DESTINATION ENCRYPT PUBLIC KEY', toBase64(key)); + this.addUDSPHeader(packet, 'key', key); + } on = on; outgoingHead; outgoingData; diff --git a/udsp/request/dataPacketization.js b/udsp/request/dataPacketization.js index 7cba9336..3444c672 100644 --- a/udsp/request/dataPacketization.js +++ b/udsp/request/dataPacketization.js @@ -19,15 +19,18 @@ export async function dataPacketization(source) { const safeEndIndex = endIndex > dataSize ? dataSize : endIndex; const chunk = outgoingData.subarray(currentBytePosition, safeEndIndex); console.log('chunksize', chunk.length, currentBytePosition, endIndex); - const packet = { + const message = { pid: packetId, endIndex: safeEndIndex, sid }; - packet.data = chunk; - outgoingDataPackets[packetId] = outgoingDataPackets; + message.data = chunk; + const packet = { + message + }; + outgoingDataPackets[packetId] = packet; if (safeEndIndex === dataSize) { - packet.last = true; + message.last = true; break; } currentBytePosition += maxDataSize; @@ -35,9 +38,11 @@ export async function dataPacketization(source) { } } else { const packet = { - pid: 0, - end: true, - data: outgoingData + message: { + pid: 0, + end: true, + data: outgoingData + } }; console.log(source); outgoingDataPackets[0] = packet; diff --git a/udsp/reply.js b/udsp/request/reply.js similarity index 74% rename from udsp/reply.js rename to udsp/request/reply.js index e5afe5b5..84f70cc3 100644 --- a/udsp/reply.js +++ b/udsp/request/reply.js @@ -6,9 +6,9 @@ import { success, failed, info, msgReceived, msgSent } from '#logs'; import { processEvent } from '#udsp/processEvent'; -import { Base } from './request/base.js'; +import { Base } from './base.js'; /** - * @todo Add promise to send use the method that Ask uses assign the accept, return it, and when completed execute. + * @todo */ export class Reply extends Base { constructor(request, source) { @@ -22,10 +22,6 @@ export class Reply extends Base { queue, packetIdGenerator } = source; - const server = source.server(); - this.server = function() { - return server; - }; this.sid = sid; this.id = sid; this.response.sid = sid; @@ -37,11 +33,9 @@ export class Reply extends Base { this.state = 1; await processEvent(this); } - response = { - head: {} - }; + response = {}; } export function reply(packet, client) { - console.log(client); + // console.log(client); return construct(Reply, [packet, client]); } diff --git a/udsp/request/sendPacket.js b/udsp/request/sendPacket.js index 7c90461e..7c11b4ce 100644 --- a/udsp/request/sendPacket.js +++ b/udsp/request/sendPacket.js @@ -1,26 +1,4 @@ import { info } from '#logs'; export async function sendPacket(packet) { - const { - message, - options, - header, - footer - } = packet; - const source = this.source(); - if (options) { - info(`Sending Packet with options`); - } - if (header) { - info(`Sending Packet with header`); - } - if (message) { - info(`Sending Packet with message`); - if (message.method) { - info(`Sending Packet with act ${message.method}`); - } - } - if (footer) { - info(`Sending Packet with footer`); - } await source.send(packet); } diff --git a/udsp/request/sendPacketsById.js b/udsp/request/sendPacketsById.js index 127603fd..bc072e43 100644 --- a/udsp/request/sendPacketsById.js +++ b/udsp/request/sendPacketsById.js @@ -3,15 +3,11 @@ export async function sendPacketsById(packetArray, indexes) { const thisReply = this; if (isArray(indexes)) { eachArray(indexes, (id) => { - const message = packetArray[id]; - thisReply.sendPacket({ - message - }); + const packet = packetArray[id]; + thisReply.sendPacket(packet); }); } else { - const message = packetArray[indexes]; - thisReply.sendPacket({ - message - }); + const packet = packetArray[indexes]; + thisReply.sendPacket(packet); } } diff --git a/udsp/sendPacket.js b/udsp/sendPacket.js index d90d4c6e..af66d460 100644 --- a/udsp/sendPacket.js +++ b/udsp/sendPacket.js @@ -3,40 +3,46 @@ import { } from '#logs'; import { promise, isFunction } from '@universalweb/acid'; import { encodePacket } from '#udsp/encodePacket'; -// clientId, nonce, encrypted message size, flags, packet size. -export async function sendPacket(packetConfig) { +export async function sendPacket(packet, source, socket, destination = source.destination) { success(`SENDING MESSAGE`); // console.log(packetConfig); const { - source, - source: { - server, - isClient, - isServer, - isServerClient - } - } = packetConfig; - const destination = packetConfig.destination || packetConfig.source.destination; + server, + isClient, + isServer, + isServerClient + } = source; + const { + message, + header, + footer + } = packet; const { ip, port, } = destination; - console.log(packetConfig.packet); - const packet = await encodePacket(packetConfig); - console.log(`Packet Encoded Size ${packet.length} Sending to ip: ${ip} Port: ${port}`); - let rawSocket; - if (isServerClient) { - rawSocket = source.server().socket; - } else { - rawSocket = source.socket; + // console.log(packetConfig.packet); + if (header) { + info(`Sending Packet with header`); + } + if (message) { + info(`Sending Packet with message`); + if (message.method) { + info(`Sending Packet with act ${message.method}`); + } + } + if (footer) { + info(`Sending Packet with footer`); } + const packetEncoded = await encodePacket(packet, source, destination); + console.log(`Packet Encoded Size ${packetEncoded.length} Sending to ip: ${ip} Port: ${port}`); return promise((accept, reject) => { - rawSocket.send(packet, port, ip, (error) => { + socket.send(packetEncoded, port, ip, (error) => { if (error) { reject(error); return failed(error); } - success('Packet Sent Out', packet.length); + success('Packet Sent Out', packetEncoded.length); accept(); }); }); diff --git a/udsp/server/actions/file.js b/udsp/server/actions/file.js index edd82eb8..ffbbd70b 100644 --- a/udsp/server/actions/file.js +++ b/udsp/server/actions/file.js @@ -5,11 +5,9 @@ import { read } from '#utilities/file'; import path from 'path'; const dots = /\./g; /** - * - * @todo Include file size & other metadata as mandatory single packet data. In the head section first so that it can be built and prepared before the data begins sending. After head completes it can then proceed with the data. + * @todo */ const cache = {}; -// This is for small static resource files only which could be loaded into memory or could be loaded in on the fly and then discarded or cached. export async function file(reply) { const { resourceDirectory, diff --git a/udsp/server/clients/index.js b/udsp/server/clients/index.js index 8530922e..b3b7680a 100644 --- a/udsp/server/clients/index.js +++ b/udsp/server/clients/index.js @@ -21,6 +21,9 @@ export class Client { this.server = function() { return server; }; + this.socket = function() { + return server.socket; + }; return initialize(config, client); } async created() { @@ -49,11 +52,7 @@ export class Client { } async send(packet) { msgSent(`socket Sent -> ID: ${this.id}`); - const config = { - source: this, - packet - }; - return sendPacket(config); + return sendPacket(packet, this, this.socket()); } async received(message, frameHeaders) { const server = this.server(); diff --git a/udsp/server/index.js b/udsp/server/index.js index 4fa6da67..3453c267 100644 --- a/udsp/server/index.js +++ b/udsp/server/index.js @@ -149,12 +149,7 @@ export class Server extends UDSP { return this; } async send(packet, destination) { - const config = { - source: this, - destination, - packet - }; - return sendPacket(config); + return sendPacket(packet, this, this.socket, destination); } bindMethods(methods) { const thisServer = this; diff --git a/udsp/server/onPacket.js b/udsp/server/onPacket.js index 58a3f210..6247108b 100644 --- a/udsp/server/onPacket.js +++ b/udsp/server/onPacket.js @@ -5,7 +5,7 @@ import { toBase64 } from '#crypto'; import { isEmpty } from '@universalweb/acid'; import { decodePacket, decodePacketHeaders } from '#udsp/decodePacket'; import { createClient } from './clients/index.js'; -import { reply } from '#udsp/reply'; +import { reply } from '#udsp/request/reply'; const isServer = true; export async function onPacket(packet, connection) { const thisServer = this;