UNPKG

node-web-mvc

Version:
110 lines (109 loc) 4.51 kB
"use strict"; /** * @moduel HttpRequestValidation * @description http请求校验 */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const RequestUtil_1 = __importDefault(require("../util/RequestUtil")); const HttpHeaders_1 = __importDefault(require("./HttpHeaders")); const HttpMethod_1 = __importDefault(require("./HttpMethod")); const HttpStatus_1 = __importDefault(require("./HttpStatus")); const etagRegex = new RegExp('\\*|\\s*((W\\/)?("[^"]*"))\\s*,?'); class HttpRequestValidation { constructor(request, response) { this.request = request; this.response = response; this.notModified = false; } normalizeTimestamp(lastModifiedTimestamp) { // 时间戳精度设置在 1s误差 return parseInt((lastModifiedTimestamp / 1000).toString()) * 1000; } checkNotModified(etag, lastModifiedTimestamp) { const method = this.request.method; const isSafeMethod = method === HttpMethod_1.default.GET || method === HttpMethod_1.default.HEAD; // 1. 校验 if-unmodified-since if (this.validateIfUnmodifiedSince(lastModifiedTimestamp)) { if (this.notModified) { // 如果此时文件被修改了,这里需要设置返回412状态 this.response.setStatus(HttpStatus_1.default.PRECONDITION_FAILED); } return this.notModified; } // 2. 校验: if-none-match if (!this.validateIfNoneMatch(etag)) { // 3. 校验: if-modified-since this.validateIfModifiedSince(lastModifiedTimestamp); } // 4. 进行304判定 if (this.notModified) { const status = isSafeMethod ? HttpStatus_1.default.NOT_MODIFIED : HttpStatus_1.default.PRECONDITION_FAILED; this.response.setStatus(status); } // 5. 写出 last-modifed 以及 etag if (isSafeMethod) { if (lastModifiedTimestamp > 0 && !this.response.getHeader(HttpHeaders_1.default.LAST_MODIFIED)) { this.response.setDateHeader(HttpHeaders_1.default.LAST_MODIFIED, lastModifiedTimestamp); } if (etag && !this.response.getHeader(HttpHeaders_1.default.ETAG)) { etag = RequestUtil_1.default.padEtagIfNecessary(etag); this.response.setHeader(HttpHeaders_1.default.ETAG, etag); } } if (this.notModified) { this.response.end(); } return this.notModified; } /** * 校验当前请求头中带 if-unmodified-since * @param lastModifiedTimestamp 资源最后修改时间 */ validateIfUnmodifiedSince(lastModifiedTimestamp) { const timestamp = this.normalizeTimestamp(lastModifiedTimestamp); // 获取if-unmodified-since值 const ifUnmodifiedSince = RequestUtil_1.default.parseDataHeader(this.request, HttpHeaders_1.default.IF_UNMODIFIED_SINCE); if (ifUnmodifiedSince === -1 || timestamp < 0) { return false; } this.notModified = ifUnmodifiedSince < timestamp; return true; } /** * 校验if-none-match * @returns 当前etag是否匹配成功 */ validateIfNoneMatch(etag) { const ifNoneMatch = this.request.getHeader(HttpHeaders_1.default.IF_NONE_MATCH); if (!etag || !ifNoneMatch) { return false; } etag = RequestUtil_1.default.padEtagIfNecessary(etag); if (etag.startsWith('W/')) { etag = etag.substring(2); } const match = (value) => { const r = value.match(etagRegex); return r && r[3] === etag; }; const elements = ifNoneMatch instanceof Array ? ifNoneMatch : [ifNoneMatch]; this.notModified = !!elements.find(match); return true; } /** * 校验if-modified-since */ validateIfModifiedSince(lastModifiedTimestamp) { const ifModifiedSince = RequestUtil_1.default.parseDataHeader(this.request, HttpHeaders_1.default.IF_MODIFIED_SINCE); if (!ifModifiedSince || lastModifiedTimestamp < 0) { return false; } const timestamp = this.normalizeTimestamp(lastModifiedTimestamp); this.notModified = ifModifiedSince >= timestamp; return true; } } exports.default = HttpRequestValidation;