UNPKG

@curveball/kernel

Version:

Curveball is a framework writting in Typescript for Node.js

130 lines (105 loc) 3.16 kB
import RequestInterface from './request.js'; /** * This method will check the following request headers: * * 1. If-Match * 2. If-None-Match * 3. If-Modified-Since * 4. If-Unmodified-Since * * To check these headers,v alues for lastModified and etag should be passed. * * The result of this function will return a suggested HTTP status. It will * return * * '200' if all the preconditions passed. * * '304' is a 'Not Modified' status should be returned, and * * '412' if a 'Precondition failed' error should be returned. */ export function conditionalCheck(request: RequestInterface, lastModified: Date|null, etag: string|null): 200 | 304 | 412 { validateETag(etag); if (request.headers.has('If-Match')) { return ifMatchCheck( request.headers.get('If-Match')!, etag ); } if (request.headers.has('If-None-Match')) { return ifNoneMatchCheck( request.method, request.headers.get('If-None-Match')!, etag ); } if (request.headers.has('If-Modified-Since')) { return ifModifiedSinceCheck( request.method, request.headers.get('If-Modified-Since')!, lastModified ); } if (request.headers.has('If-Unmodified-Since')) { return ifUnmodifiedSinceCheck( request.headers.get('If-Unmodified-Since')!, lastModified ); } return 200; } function ifMatchCheck(header: string, serverState: string | null): 200 | 412 { if (header==='*') { return serverState !== null ? 200 : 412; } const headerTags = header.split(',').map( foo => foo.trim() ); for(const tag of headerTags) { if (tag===serverState) return 200; } return 412; } function ifNoneMatchCheck(method: string, header: string, serverState: string | null): 200 | 304 | 412 { let pass; if (header==='*') { pass = serverState === null; } else { const headerTags = header.split(',').map( foo => foo.trim() ); pass = true; for(const tag of headerTags) { if (tag===serverState) { pass = false; } } } if (!pass) { if (method === 'GET' || method === 'HEAD') { return 304; } else { return 412; } } return 200; } function ifModifiedSinceCheck(method: string, header: string, serverState: Date|null): 200 | 304 { if (method !== 'GET' && method !== 'HEAD') { // Only GET and HEAD are supported, everything else is ignored return 200; } if (serverState === null) { return 200; } const headerDate = new Date(header); return (serverState.getTime() > headerDate.getTime()) ? 200 : 304; } function ifUnmodifiedSinceCheck(header: string, serverState: Date|null): 200 | 412 { // This is not in any spec, but I believe that we should reject requests // if we don't know that the Last-Modified value is. if (serverState === null) { return 412; } const headerDate = new Date(header); return (serverState.getTime() <= headerDate.getTime()) ? 200 : 412; } function validateETag(etag: null|string) { if (etag===null) return; if (!/^(W\/)?"[\x21\x23-\x7e]+"$/.test(etag)) { throw new Error('Etags must be valid. Did you forget the double-quotes?'); } }