UNPKG

@jsonjoy.com/reactive-rpc

Version:

Reactive-RPC is a library for building reactive APIs over WebSocket, HTTP, and other RPCs.

170 lines 6.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConnectionContext = void 0; const NullObject_1 = require("@jsonjoy.com/util/lib/NullObject"); const copy_1 = require("@jsonjoy.com/util/lib/buffers/copy"); const concat_1 = require("@jsonjoy.com/util/lib/buffers/concat"); const REGEX_AUTH_TOKEN_SPECIFIER = /tkn\.([a-zA-Z0-9\-_]+)(?:[^a-zA-Z0-9\-_]|$)/; const REGEX_CODECS_SPECIFIER = /rpc\.(\w{0,32})\.(\w{0,32})\.(\w{0,32})(?:\-(\w{0,32}))?/; class ConnectionContext { static findIp(req, res) { return (req.getHeader('x-forwarded-for') || req.getHeader('x-real-ip') || Buffer.from(res.getRemoteAddressAsText()).toString()); } static findTokenInText(text) { const match = REGEX_AUTH_TOKEN_SPECIFIER.exec(text); if (!match) return ''; return match[1] || ''; } static findToken(req) { let token = ''; let text = ''; text = req.getHeader('authorization'); if (text) token = ConnectionContext.findTokenInText(text); if (token) return token; text = req.getQuery(); if (text) token = ConnectionContext.findTokenInText(text); if (token) return token; text = req.getHeader('cookie'); if (text) token = ConnectionContext.findTokenInText(text); if (token) return token; text = req.getHeader('sec-websocket-protocol'); if (text) token = ConnectionContext.findTokenInText(text); return token; } static fromReqRes(req, res, params, app) { const ip = ConnectionContext.findIp(req, res); const token = ConnectionContext.findToken(req); const codecs = app.codecs; const valueCodecs = codecs.value; const ctx = new ConnectionContext(ip, token, params, new NullObject_1.NullObject(), valueCodecs.json, valueCodecs.json, codecs.messages.compact, res); const contentType = req.getHeader('content-type'); if (contentType) ctx.setCodecs(contentType, codecs); return ctx; } static fromWs(req, res, secWebSocketProtocol, params, app) { const ip = ConnectionContext.findIp(req, res); const token = ConnectionContext.findToken(req); const codecs = app.codecs; const valueCodecs = codecs.value; const ctx = new ConnectionContext(ip, token, params, new NullObject_1.NullObject(), valueCodecs.json, valueCodecs.json, codecs.messages.compact, res); const contentType = req.getHeader('content-type'); if (contentType) ctx.setCodecs(contentType, codecs); else if (secWebSocketProtocol) ctx.setCodecs(secWebSocketProtocol, codecs); return ctx; } constructor(ip, token, params, meta, reqCodec, resCodec, msgCodec, res = undefined) { this.ip = ip; this.token = token; this.params = params; this.meta = meta; this.reqCodec = reqCodec; this.resCodec = resCodec; this.msgCodec = msgCodec; this.res = res; } setCodecs(specifier, codecs) { const match = REGEX_CODECS_SPECIFIER.exec(specifier); if (!match) return; const [, protocol, messageFormat, request, response] = match; switch (protocol) { case 'rx': { switch (messageFormat) { case 'compact': { this.msgCodec = codecs.messages.compact; break; } case 'binary': { this.msgCodec = codecs.messages.binary; break; } } break; } case 'json2': { this.msgCodec = codecs.messages.jsonRpc2; break; } } switch (request) { case 'cbor': { this.resCodec = this.reqCodec = codecs.value.cbor; break; } case 'json': { this.resCodec = this.reqCodec = codecs.value.json; break; } case 'msgpack': { this.resCodec = this.reqCodec = codecs.value.msgpack; break; } } switch (response) { case 'cbor': { this.resCodec = codecs.value.cbor; break; } case 'json': { this.resCodec = codecs.value.json; break; } case 'msgpack': { this.resCodec = codecs.value.msgpack; break; } } } requestBodyParts(max) { const res = this.res; return new Promise((resolve) => { const list = []; if (!res) return resolve(list); let running = 0; res.onData((ab, isLast) => { running += ab.byteLength; if (running > max) { res.aborted = true; res.end('too large'); } if (isLast) list.push(new Uint8Array(ab)), resolve(list); else list.push((0, copy_1.copy)(new Uint8Array(ab))); }); }); } async requestBody(max) { const parts = await this.requestBodyParts(max); return (0, concat_1.concatList)(parts); } async requestBodyJson(max) { const parts = await this.requestBodyParts(max); const bodyUint8 = (0, concat_1.concatList)(parts); return this.reqCodec.decoder.read(bodyUint8); } sendResponse(response) { const res = this.res; if (!res) return; if (res.aborted) return; res.end(response); } } exports.ConnectionContext = ConnectionContext; //# sourceMappingURL=context.js.map