1import { parsePlatformHeader } from './resolvePlatform';
2import { ServerNext, ServerRequest, ServerResponse } from './server.types';
3
4const debug = require('debug')('expo:start:server:metro:historyFallback') as typeof console.log;
5
6const WS_DEVICE_URL = '/inspector/device';
7const WS_DEBUGGER_URL = '/inspector/debug';
8const PAGES_LIST_JSON_URL = '/json';
9const PAGES_LIST_JSON_URL_2 = '/json/list';
10const PAGES_LIST_JSON_VERSION_URL = '/json/version';
11
12export function isInspectorProxyRequest(req: ServerRequest) {
13  const ua = req.headers['user-agent'];
14  const url = req.url;
15
16  // This check is very fragile but it enables websites to use any of the
17  // endpoints below without triggering the inspector proxy.
18  if (!url || (ua && !ua.includes('node-fetch'))) {
19    // This optimizes for the inspector working over the endpoint being available on web.
20    // Web is less fragile.
21    return false;
22  }
23
24  return [
25    WS_DEVICE_URL,
26    WS_DEBUGGER_URL,
27    PAGES_LIST_JSON_URL,
28    PAGES_LIST_JSON_URL_2,
29    PAGES_LIST_JSON_VERSION_URL,
30  ].includes(url);
31}
32
33/**
34 * Create a web-only middleware which redirects to the index middleware without losing the path component.
35 * This is useful for things like React Navigation which need to render the index.html and then direct the user in-memory.
36 */
37export class HistoryFallbackMiddleware {
38  constructor(
39    private indexMiddleware: (
40      req: ServerRequest,
41      res: ServerResponse,
42      next: ServerNext
43    ) => Promise<void>
44  ) {}
45  getHandler() {
46    return (req: ServerRequest, res: ServerResponse, next: any) => {
47      const platform = parsePlatformHeader(req);
48
49      if (!platform || platform === 'web') {
50        if (isInspectorProxyRequest(req)) {
51          debug('Inspector proxy request:', req.url, 'UA:', req.headers['user-agent']);
52          return next();
53        }
54        // Redirect unknown to the manifest handler while preserving the path.
55        // This implements the HTML5 history fallback API.
56        return this.indexMiddleware(req, res, next);
57      }
58
59      return next();
60    };
61  }
62}
63