MCPcopy
hub / github.com/garrytan/gstack / makeFetchHandler

Function makeFetchHandler

browse/src/server.ts:1669–2849  ·  view source on GitHub ↗
(surface: Surface)

Source from the content-addressed store, hash-verified

1667
1668
1669 const makeFetchHandler = (surface: Surface) => async (req: Request): Promise<Response> => {
1670 const url = new URL(req.url);
1671
1672 // ─── Tunnel surface filter (runs before any route dispatch) ──
1673 if (surface === 'tunnel') {
1674 const isGetConnect = req.method === 'GET' && url.pathname === '/connect';
1675 const allowed = TUNNEL_PATHS.has(url.pathname);
1676 if (!allowed && !isGetConnect) {
1677 logTunnelDenial(req, url, 'path_not_on_tunnel');
1678 return new Response(JSON.stringify({ error: 'Not found' }), {
1679 status: 404, headers: { 'Content-Type': 'application/json' },
1680 });
1681 }
1682 if (isRootRequest(req)) {
1683 logTunnelDenial(req, url, 'root_token_on_tunnel');
1684 return new Response(JSON.stringify({
1685 error: 'Root token rejected on tunnel surface',
1686 hint: 'Remote agents must pair via /connect to receive a scoped token.',
1687 }), { status: 403, headers: { 'Content-Type': 'application/json' } });
1688 }
1689 if (url.pathname !== '/connect' && !getTokenInfo(req)) {
1690 logTunnelDenial(req, url, 'missing_scoped_token');
1691 return new Response(JSON.stringify({ error: 'Unauthorized' }), {
1692 status: 401, headers: { 'Content-Type': 'application/json' },
1693 });
1694 }
1695 }
1696
1697 // beforeRoute overlay hook (v1.35.0.0). Runs AFTER the tunnel surface
1698 // filter and BEFORE per-route dispatch. Pre-resolves bearer auth once
1699 // so the hook receives TokenInfo | null. Note: getTokenInfo returns null
1700 // for both missing AND invalid bearer — see the ServerConfig.beforeRoute
1701 // JSDoc for the security implications.
1702 if (beforeRoute) {
1703 const auth = getTokenInfo(req);
1704 const overlayResp = await beforeRoute(req, surface, auth);
1705 if (overlayResp) return overlayResp;
1706 }
1707
1708 // GET /connect — alive probe. Unauth on both surfaces. Used by /pair
1709 // and /tunnel/start to detect dead ngrok tunnels via the tunnel URL,
1710 // since /health is not tunnel-reachable under the dual-listener design.
1711 //
1712 // Shares the same rate limit as POST /connect — otherwise a tunnel
1713 // caller can probe unlimited GETs and lock out nothing, which makes
1714 // the endpoint a free daemon-enumeration surface.
1715 if (url.pathname === '/connect' && req.method === 'GET') {
1716 if (!checkConnectRateLimit()) {
1717 return new Response(JSON.stringify({ error: 'Rate limited' }), {
1718 status: 429, headers: { 'Content-Type': 'application/json' },
1719 });
1720 }
1721 return new Response(JSON.stringify({ alive: true }), {
1722 status: 200, headers: { 'Content-Type': 'application/json' },
1723 });
1724 }
1725
1726 // Cookie picker routes — HTML page unauthenticated, data/action routes require auth

Callers 1

buildFetchHandlerFunction · 0.85

Calls 15

logTunnelDenialFunction · 0.90
checkConnectRateLimitFunction · 0.90
handleCookiePickerRouteFunction · 0.90
mintLeaseFunction · 0.90
mintPtySessionTokenFunction · 0.90
revokePtySessionTokenFunction · 0.90
revokeLeaseFunction · 0.90
buildPtySetCookieFunction · 0.90
validateLeaseFunction · 0.90
refreshLeaseFunction · 0.90
isSidecarAvailableFunction · 0.90
scanWithSidecarFunction · 0.90

Tested by

no test coverage detected