Skip to content

Instantly share code, notes, and snippets.

@flotwig
Last active May 8, 2019 21:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flotwig/c44b1cc8dfd825326fddef4d58e403e3 to your computer and use it in GitHub Desktop.
Save flotwig/c44b1cc8dfd825326fddef4d58e403e3 to your computer and use it in GitHub Desktop.
  • Bugs:
    • #76 - Node has a constant header size limit of 80kb
  • Serve dynamic HTTP/1.1 content on any http[s]://host:port/path combination
    • can serve pre-configured static responses to regex or glob urls
type GlobPattern = string
type StringMatcher = GlobPattern | RegEx
type StringDictMatcher = { [key: string]: StringMatcher }
type NumberMatcher = number | number[] // list of acceptable numbers

type HttpMethodMatcher = StringMatcher

// all HTTP requests have some basic information
// users would use a `RouteMatcher` to "subscribe" to a certain subset of requests

// no callbacks are supported in matchers - helps cut down on un-needed back-and-forth
// websocket requests to match requests - they are all static

// then, the route is handled by a `RouteHandler`, which will either return a static response
// or send a callback to the driver, where it will be handled

/** Types for Route Matching **/

type RouteMatcher = StringMatcher | RouteMatcherOptions

interface RouteMatcherOptions {
    auth?: { username: StringMatcher, password: StringMatcher }
    headers?: StringDictMatcher
    hostname?: StringMatcher
    https?: boolean // serve this over HTTPS
    method?: HttpMethodMatcher // defaults to GET
    path?: StringMatcher // includes query params
    pathname?: StringMatcher // does not include query params
    port?: NumberMatcher
    query?: StringDictMatcher // match querystring values
    url?: StringMatcher
}

/** Types for Route Responses **/

interface CyIncomingResponse {
    // an interface like the one brian proposed to make changes to the response
}

interface CyIncomingRequest {
    // as much stuff from `incomingmessage` as makes sense to serialize and send
}

type CyResponse = string | object
type CyInterceptor = (res: CyIncomingResponse, send?: () => void) => void

interface CyIncomingHTTPRequest extends CyIncomingRequest {
    // if `responseOrInterceptor` is undefined, just forward the modified request to the destination
    reply: (responseOrInterceptor?: CyResponse | CyInterceptor) => void

    // todo: is this needed, when they could just do `setTimeout(()=>req.reply(), delayMs)`?
    // delay: (delayMs: number) => void
}

type HTTPController = (req: CyRequest, next: () => void) => void

interface WebSocketController {
    onConnect?: (req: CyIncomingRequest, socket: CyWebSocket) => void
    onMessage?: (socket: CyWebSocket, message: CyWebSocketMessage) => void
    onDisconnect?: (socket: CyWebSocket) => void
    
    transport: 'socket.io'  // socket.io client over websockets
               | 'socket.io-longpolling' // socket.io client via longpolling
               | 'websockets' // vanilla websockets server
}

type RouteHandlerController = HTTPController | WebSocketController

interface RouteHandler = string | object | RouteHandlerController

/** Driver Commands **/

type CyRoute = (matcher: RouteMatcher, handler: RouteHandler) => void

What if multiple routes match?

No reply will be sent until a req.reply is called. req.reply can only be called once, to call it multiple times is an error.

If next() is called inside of a HTTPController, the next matching HTTPController will be called with a potentially-modified req.

next() cannot be called inside of a req.reply, as at that point, the response is being sent.

If there is not a "next matching HTTPController" and next() is called, then the default middleware will be called, which simply does req.reply() to passthru the request and send the response.

If there is a req.reply callback that accepts the send parameter, Cypress will dispatch the modified res only when send is called. If it does not accept send, Cypress will dispatch the modified res synchronously at the end of the req.reply callback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment