@fanoutio/serve-grip
Version:
Connect-style Middleware for GRIP
99 lines (98 loc) • 3.79 kB
JavaScript
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);
}
}
}
});
}