UNPKG

@fanoutio/grip

Version:
120 lines (119 loc) 5.73 kB
import { Item, HttpResponseFormat, HttpStreamFormat, } from '../data/index.js'; import { PublisherClient } from './PublisherClient.js'; import { parseGripUri, validateSig } from '../utilities/index.js'; // The Publisher class allows consumers to easily publish HTTP response // and HTTP stream format messages to GRIP proxies. Publisher can be configured // using IGripConfig objects. export class Publisher { clients = []; prefix; constructor(config, publisherOptions) { this.applyConfigs(config ?? [], publisherOptions); this.prefix = publisherOptions?.prefix; } // Apply the specified GRIP configurations to this PublisherBase instance. applyConfigs(config, publisherClientOptions) { const configsAsArray = Array.isArray(config) ? config : [config]; for (const configEntry of configsAsArray) { this.applyConfig(configEntry, publisherClientOptions); } } // Apply the specified GRIP configuration to this PublisherBase instance. // The parameter corresponds to a single PublisherClient instance. Each object // will be parsed and a PublisherClient will be created either using just // a URI or a URI and authentication information (Basic, JWT, or Bearer Token). applyConfig(config, publisherClientOptions) { const gripConfig = typeof config === 'string' ? parseGripUri(config) : config; const client = new PublisherClient(gripConfig, { fetch: publisherClientOptions?.fetch, }); this.addClient(client); } // Add the specified PublisherClient instance. addClient(client) { this.clients.push(client); } // The publish method for publishing the specified item to the specified // channel on the configured endpoint. // This function returns a promise which is resolved when the publish is complete, // or rejected with an exception describing the failure if the publish fails. async publish(channel, item) { await Promise.all(this.clients.map((client) => client.publish((this.prefix ?? '') + channel, item))); } // A utility method for publishing an item to the specified channel on the configured endpoint // by building it from a single Format object or array of Format objects. // This function returns a promise which is resolved when the publish is complete, // or rejected with an exception describing the failure if the publish fails. async publishFormats(channel, formats, id, prevId) { await this.publish(channel, new Item(formats, id, prevId)); } // Publish an HTTP response format message to all of the configured // PubControlClients with a specified channel, message, and optional ID, and // previous ID. The 'data' parameter may be provided as either an HttpResponseFormat // instance or a string (in which case an HttpResponseFormat instance will // be created and have the 'body' field set to the specified string). // This function returns a promise which is resolved when the publish is complete, // or rejected with an exception describing the failure if the publish fails. async publishHttpResponse(channel, data, id, prevId) { const httpResponse = data instanceof HttpResponseFormat ? data : new HttpResponseFormat({ body: data }); return this.publishFormats(channel, httpResponse, id, prevId); } // Publish an HTTP stream format message to all of the configured // PubControlClients with a specified channel, message, and optional ID, and // previous ID. The 'data' parameter may be provided as either an HttpStreamFormat // instance or a string (in which case an HttpStreamFormat instance will // be created and have the 'content' field set to the specified string). // This function returns a promise which is resolved when the publish is complete, // or rejected with an exception describing the failure if the publish fails. async publishHttpStream(channel, data, id, prevId) { const httpStream = data instanceof HttpStreamFormat ? data : new HttpStreamFormat(data); return this.publishFormats(channel, httpStream, id, prevId); } async validateGripSig(gripSigHeaderValue) { let isProxied = false; let needsSigned = false; let isSigned = false; if (gripSigHeaderValue == null || this.clients.length === 0) { return { isProxied, needsSigned, isSigned, }; } let signatureValidated = false; // The value needs to be appropriately signed if all the publisher clients // have a "verify key". needsSigned = true; for (const client of this.clients) { const verifyKey = client.getVerifyKey?.(); if (verifyKey == null) { needsSigned = false; break; } // We only need to validate the signature for one client if (!signatureValidated) { const verifyIss = client.getVerifyIss?.(); if (verifyIss == null) { signatureValidated = await validateSig(gripSigHeaderValue, verifyKey); } else { signatureValidated = await validateSig(gripSigHeaderValue, verifyKey, verifyIss); } } } if (needsSigned) { if (signatureValidated) { isProxied = true; isSigned = true; } } else { isProxied = true; } return { isProxied, needsSigned, isSigned, }; } }