UNPKG

pluto-http-client

Version:

HTTP client for NodeJS. Inspired in the Java JAX-RS spec so you can expect excellence, versatility and extensibility.

362 lines (361 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpHeaderReader = exports.Event = void 0; const cookie_1 = require("../core/cookie"); var Event; (function (Event) { Event[Event["Token"] = 0] = "Token"; Event[Event["QuotedString"] = 1] = "QuotedString"; Event[Event["Comment"] = 2] = "Comment"; Event[Event["Separator"] = 3] = "Separator"; Event[Event["Control"] = 4] = "Control"; })(Event = exports.Event || (exports.Event = {})); class HttpHeaderReader { constructor(header, processComments = false) { this.header = header; this.processComments = processComments; this.index = 0; this.length = header.length; } hasNext() { return this.skipWhiteSpace(); } get value() { return this._value; } skipWhiteSpace() { for (; this.index < this.length; this.index++) { if (!this.isWhiteSpace(this.header.charAt(this.index))) { return true; } } return false; } isWhiteSpace(c) { return HttpHeaderReader.WHITE_SPACE.has(c); } isSeparator(c) { return HttpHeaderReader.SEPARATORS.has(c); } nextToken() { const e = this.next(false); if (e != Event.Token) { throw new Error("Next event is not a Token:" + this.index); } return this._value || ""; } nextSeparator(seperator) { var _a, _b; const e = this.next(false); if (e != Event.Separator) { throw new Error("Next event is not a Separator" + this.index); } if (seperator != ((_a = this._value) === null || _a === void 0 ? void 0 : _a.charAt(0))) { throw new Error(`Expected separator '${seperator}' instead of ${(_b = this._value) === null || _b === void 0 ? void 0 : _b.charAt(0)} at index ${this.index}`); } } hasNextSeparator(separator, skipWhiteSpace) { if (skipWhiteSpace) { this.skipWhiteSpace(); } if (this.index >= this.length) { return false; } let c = this.header.charAt(this.index); return this.isSeparator(c) && c == separator; } next(skipWhiteSpace = true, preserveBackslash = false) { this.event = this.process(this.getNextCharacter(skipWhiteSpace), preserveBackslash); return this.event; } getNextCharacter(skipWhiteSpace) { if (skipWhiteSpace) { this.skipWhiteSpace(); } if (this.index >= this.length) { throw new Error("HTTP_HEADER_END_OF_HEADER:" + this.index); } return this.header.charCodeAt(this.index); } getType(c) { return HttpHeaderReader.TYPE_TABLE[c]; } isToken(c) { return HttpHeaderReader.IS_TOKEN[c]; } processQuotedString(preserveBackslash) { let filter = false; for (let start = ++this.index; this.index < this.length; this.index++) { const c = this.header.charCodeAt(this.index); if (!preserveBackslash && c == '\\'.charCodeAt(0)) { this.index++; filter = true; } else if (c == '\r'.charCodeAt(0)) { filter = true; } else if (c == '"'.charCodeAt(0)) { this._value = (filter) ? this.filterToken(this.header, start, this.index, preserveBackslash) : this.header.substring(start, this.index); this.index++; return; } } throw Error("HTTP_HEADER_UNBALANCED_QUOTED" + this.index); } nextQuotedString() { const e = this.next(false); if (e != Event.QuotedString) { throw new Error(`Next event is not a Quoted String. Index: ${this.index}`); } else { return this._value; } } nextTokenOrQuotedString(preserveBackslash) { const e = this.next(false, preserveBackslash); if (e != Event.Token && e != Event.QuotedString) { throw new Error("Next event is not a Token or a Quoted String, " + this._value + this.index); } else { return this._value || ""; } } filterToken(s, start, end, preserveBackslash = false) { const buffer = []; let c; let gotEscape = false; let gotCR = false; for (let i = start; i < end; i++) { c = s.charAt(i); if (c == '\n' && gotCR) { gotCR = false; continue; } gotCR = false; if (!gotEscape) { if (!preserveBackslash && c == '\\') { gotEscape = true; } else if (c == '\r') { gotCR = true; } else { buffer.push(c); } } else { buffer.push(c); gotEscape = false; } } return buffer.join(""); } processComment() { let filter = false; let nesting; let start; for (start = ++this.index, nesting = 1; nesting > 0 && this.index < this.length; this.index++) { let c = this.header.charAt(this.index); if (c == '\\') { this.index++; filter = true; } else if (c == '\r') { filter = true; } else if (c == '(') { nesting++; } else if (c == ')') { nesting--; } } if (nesting != 0) { throw new Error("HTTP_HEADER_UNBALANCED_COMMENTS" + this.index); } this._value = (filter) ? this.filterToken(this.header, start, this.index - 1) : this.header.substring(start, this.index - 1); } process(c, preserveBackslash) { if (c > 127) { this.index++; return Event.Control; } switch (this.getType(c)) { case HttpHeaderReader.TOKEN: { const start = this.index; for (this.index++; this.index < this.length; this.index++) { if (!this.isToken(this.header.charCodeAt(this.index))) { break; } } this._value = this.header.substring(start, this.index); return Event.Token; } case HttpHeaderReader.QUOTED_STRING: this.processQuotedString(preserveBackslash); return Event.QuotedString; case HttpHeaderReader.COMMENT: if (!this.processComments) { throw new Error("HTTP_HEADER_COMMENTS_NOT_ALLOWED" + this.index); } this.processComment(); return Event.Comment; case HttpHeaderReader.SEPARATOR: this.index++; this._value = String.fromCharCode(c); return Event.Separator; case HttpHeaderReader.CONTROL: this.index++; this._value = String.fromCharCode(c); return Event.Control; default: throw new Error("HTTP_HEADER_WHITESPACE_NOT_ALLOWED:" + this.index); } } static createTableType() { const table = {}; const controlCharBound = 32; for (let i = 0; i < controlCharBound; i++) { table[i] = HttpHeaderReader.CONTROL; } table[127] = HttpHeaderReader.CONTROL; for (let i = controlCharBound; i < 127; i++) { table[i] = HttpHeaderReader.TOKEN; } for (const c of HttpHeaderReader.SEPARATORS.values()) { table[c.charCodeAt(0)] = HttpHeaderReader.SEPARATOR; } table['('.charCodeAt(0)] = HttpHeaderReader.COMMENT; table['"'.charCodeAt(0)] = HttpHeaderReader.QUOTED_STRING; for (const c of HttpHeaderReader.WHITE_SPACE.values()) { table[c.charCodeAt(0)] = -1; } return table; } static createTokenTable() { const table = {}; for (let i = 0; i <= 127; i++) { table[i] = (HttpHeaderReader.TYPE_TABLE[i] == HttpHeaderReader.TOKEN); } return table; } static readParameters(reader, fileNameFix = false) { var _a; let m = new Map(); while (reader.hasNext()) { reader.nextSeparator(';'); while (reader.hasNextSeparator(';', true)) { reader.next(); } if (!reader.hasNext()) { break; } let name = (_a = reader.nextToken()) === null || _a === void 0 ? void 0 : _a.toString().toLowerCase(); reader.nextSeparator('='); let value; if ("filename" === name && fileNameFix) { value = reader.nextTokenOrQuotedString(true).toString(); value = value.substring(value.lastIndexOf('\\') + 1); } else { value = reader.nextTokenOrQuotedString(false).toString(); } m.set(name, value); } return m; } static appendQuotedIfWhitespace(buffer, s) { if (/ /.test(s || "")) { buffer.push(`"${s}"`); } else { buffer.push(s); } } static appendQuotedIfNonToken(b, value) { if (value) { const quote = !HttpHeaderReader.isTokenString(value); if (quote) { b.push('"'); } HttpHeaderReader.appendEscapingQuotes(b, value); if (quote) { b.push('"'); } } } static isTokenString(s) { for (let idx = 0; idx < s.length; ++idx) { const c = s.charCodeAt(idx); if (!HttpHeaderReader.IS_TOKEN[c]) { return false; } } return true; } static appendEscapingQuotes(b, value) { for (let i = 0; i < value.length; ++i) { const c = value.charAt(i); if (c == '"') { b.push('\\'); } b.push(c); } } static parseCookie(header) { if (!header) { return; } let cookie = undefined; const bites = header.split(/;/); for (let bite of bites) { const crumbs = bite.split("=", 2); const name = crumbs.length > 0 ? crumbs[0].trim() : ""; let value = crumbs.length > 1 ? crumbs[1].trim() : ""; if (value.startsWith("\"") && value.endsWith("\"") && value.length > 1) { value = value.substring(1, value.length - 1); } if (!cookie) { cookie = new cookie_1.Cookie(name, value); } else { let param = name.toLowerCase(); if (param.startsWith("comment")) { cookie.comment = value; } else if (param.startsWith("domain")) { cookie.domain = value; } else if (param.startsWith("max-age")) { cookie.maxAge = Number.parseInt(value); } else if (param.startsWith("path")) { cookie.path = value; } else if (param.startsWith("secure")) { cookie.secure = true; } else if (param.startsWith("version")) { cookie.version = Number.parseInt(value); } else if (param.startsWith("httponly")) { cookie.httpOnly = true; } else if (param.startsWith("expires")) { cookie.expiry = new Date(value); } } } return cookie; } } exports.HttpHeaderReader = HttpHeaderReader; HttpHeaderReader.TOKEN = 0; HttpHeaderReader.QUOTED_STRING = 1; HttpHeaderReader.COMMENT = 2; HttpHeaderReader.SEPARATOR = 3; HttpHeaderReader.CONTROL = 4; HttpHeaderReader.WHITE_SPACE = new Set(['\t', '\r', '\n', ' ']); HttpHeaderReader.SEPARATORS = new Set(['(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t']); HttpHeaderReader.TYPE_TABLE = HttpHeaderReader.createTableType(); HttpHeaderReader.IS_TOKEN = HttpHeaderReader.createTokenTable();