1import { parse } from 'url'; 2 3import { ServerNext, ServerRequest, ServerResponse } from './server.types'; 4import * as Log from '../../../log'; 5 6/** Base middleware creator for Expo dev servers. */ 7export abstract class ExpoMiddleware { 8 constructor( 9 protected projectRoot: string, 10 protected supportedPaths: string[] 11 ) {} 12 13 /** 14 * Returns true when the middleware should handle the incoming server request. 15 * Exposed for testing. 16 */ 17 _shouldHandleRequest(req: ServerRequest): boolean { 18 if (!req.url) { 19 return false; 20 } 21 const parsed = parse(req.url); 22 // Strip the query params 23 if (!parsed.pathname) { 24 return false; 25 } 26 27 return this.supportedPaths.includes(parsed.pathname); 28 } 29 30 abstract handleRequestAsync( 31 req: ServerRequest, 32 res: ServerResponse, 33 next: ServerNext 34 ): Promise<void>; 35 36 /** Create a server middleware handler. */ 37 public getHandler() { 38 const internalMiddleware = async ( 39 req: ServerRequest, 40 res: ServerResponse, 41 next: ServerNext 42 ) => { 43 try { 44 return await this.handleRequestAsync(req, res, next); 45 } catch (error: any) { 46 Log.exception(error); 47 // 5xx = Server Error HTTP code 48 res.statusCode = 500; 49 if (typeof error === 'object' && error !== null) { 50 res.end( 51 JSON.stringify({ 52 error: error.toString(), 53 }) 54 ); 55 } else { 56 res.end(`Unexpected error: ${error}`); 57 } 58 } 59 }; 60 const middleware = async (req: ServerRequest, res: ServerResponse, next: ServerNext) => { 61 if (!this._shouldHandleRequest(req)) { 62 return next(); 63 } 64 return internalMiddleware(req, res, next); 65 }; 66 67 middleware.internal = internalMiddleware; 68 69 return middleware; 70 } 71} 72 73export function disableResponseCache(res: ServerResponse): ServerResponse { 74 res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate'); 75 res.setHeader('Expires', '-1'); 76 res.setHeader('Pragma', 'no-cache'); 77 return res; 78} 79