UNPKG

@fanoutio/serve-grip

Version:
99 lines (98 loc) 3.79 kB
import { createMiddleware } from 'hono/factory'; import { Channel, getWebSocketContextFromReq, isWsOverHttp, encodeWebSocketEvents, } from '@fanoutio/grip'; import debug from '../debug.js'; import { ServeGripBase } from '../ServeGripBase.js'; class ServeGrip extends ServeGripBase { constructor(config) { super(config); } getRequestGrip(req) { return req.grip; } setRequestGrip(req, grip) { req.grip = grip; } isRequestWsOverHttp(req) { return isWsOverHttp(req.request.raw); } getRequestWebSocketContext(req) { return getWebSocketContextFromReq(req.request.raw); } getRequestHeaderValue(req, key) { return req.request.raw.headers.get(key) ?? undefined; } setResponseGrip(res, grip) { res.grip = grip; } setResponseStatus(res, code) { res.status = code; } endResponse(res, chunk) { res.endChunk = chunk; return res; } onAfterSetup(params) { params.res.getGripInstruct = params.gripInstructGetter; } } export function serveGrip(config) { const configBuilder = typeof config === 'function' ? config : () => config; return createMiddleware(async (c, next) => { const configValue = await configBuilder(c); const serveGripInstance = new ServeGrip(configValue); const requestState = { request: c.req, grip: undefined, }; const responseState = { grip: undefined, status: undefined, endChunk: undefined, getGripInstruct: undefined, }; const result = await serveGripInstance.run(requestState, responseState); if (!result || requestState.grip == null || responseState.grip == null) { // serveGripInstance.run returns false if there was an error. c.res = new Response(responseState.endChunk ?? 'Error in serveGrip middleware.', { status: responseState.status ?? 500, }); return; } const grip = Object.assign({}, requestState.grip, responseState.grip, { getPublisher: serveGripInstance.getPublisher.bind(serveGripInstance), }); c.set('grip', grip); await next(); if (grip.wsContext != null) { if (c.res.status === 200 || c.res.status === 204) { const wsContextHeaders = grip.wsContext.toHeaders(); const events = grip.wsContext.getOutgoingEvents(); c.res = new Response(encodeWebSocketEvents(events), { status: 200, headers: wsContextHeaders, }); } } else { const gripInstruct = responseState.getGripInstruct?.(); if (gripInstruct != null) { if (c.res.status === 304) { // Code 304 only allows certain headers. // Some web servers strictly enforce this. // In that case we won't be able to use // Grip-* headers to talk to the proxy. // Switch to code 200 and use Grip-Status // to specify intended status. debug("Using gripInstruct setStatus header to handle 304"); c.status(200); gripInstruct.setStatus(304); } // Apply prefix to channel names gripInstruct.channels = gripInstruct.channels.map((ch) => new Channel(serveGripInstance.prefix + ch.name, ch.prevId)); for (const [key, value] of Object.entries(gripInstruct.toHeaders())) { c.res.headers.set(key, value); } } } }); }