@browsery/http-parser
Version:
Browser compatible http-parser-js module
421 lines (417 loc) • 11.9 kB
JavaScript
function assertOk(a){
if (!a)
throw new TypeError('AssertionError [ERR_ASSERTION]: ' + JSON.stringify(a) + ' == true');
};
function assertEqual(a, b){
if (a !== b)
throw new TypeError('AssertionError [ERR_ASSERTION]: ' + JSON.stringify(a) + ' == ' + JSON.stringify(b));
};
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// ../../node_modules/http-parser-js/http-parser.js
exports.HTTPParser = HTTPParser;
function HTTPParser(type) {
if (type !== void 0 && type !== HTTPParser.REQUEST && type !== HTTPParser.RESPONSE) {
throw new Error("type must be REQUEST or RESPONSE");
}
if (type === void 0) {
} else {
this.initialize(type);
}
this.maxHeaderSize = HTTPParser.maxHeaderSize;
}
__name(HTTPParser, "HTTPParser");
HTTPParser.prototype.initialize = function(type, async_resource) {
if (type !== HTTPParser.REQUEST && type !== HTTPParser.RESPONSE) {
throw new Error("type must be REQUEST or RESPONSE");
}
this.type = type;
this.state = type + "_LINE";
this.info = {
headers: [],
upgrade: false
};
this.trailers = [];
this.line = "";
this.isChunked = false;
this.connection = "";
this.headerSize = 0;
this.body_bytes = null;
this.isUserCall = false;
this.hadError = false;
};
HTTPParser.encoding = "ascii";
HTTPParser.maxHeaderSize = 80 * 1024;
HTTPParser.REQUEST = "REQUEST";
HTTPParser.RESPONSE = "RESPONSE";
var kOnHeaders = HTTPParser.kOnHeaders = 1;
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete = 2;
var kOnBody = HTTPParser.kOnBody = 3;
var kOnMessageComplete = HTTPParser.kOnMessageComplete = 4;
HTTPParser.prototype[kOnHeaders] = HTTPParser.prototype[kOnHeadersComplete] = HTTPParser.prototype[kOnBody] = HTTPParser.prototype[kOnMessageComplete] = function() {
};
var compatMode0_12 = true;
Object.defineProperty(HTTPParser, "kOnExecute", {
get: /* @__PURE__ */ __name(function() {
compatMode0_12 = false;
return 99;
}, "get")
});
var methods = exports.methods = HTTPParser.methods = [
"DELETE",
"GET",
"HEAD",
"POST",
"PUT",
"CONNECT",
"OPTIONS",
"TRACE",
"COPY",
"LOCK",
"MKCOL",
"MOVE",
"PROPFIND",
"PROPPATCH",
"SEARCH",
"UNLOCK",
"BIND",
"REBIND",
"UNBIND",
"ACL",
"REPORT",
"MKACTIVITY",
"CHECKOUT",
"MERGE",
"M-SEARCH",
"NOTIFY",
"SUBSCRIBE",
"UNSUBSCRIBE",
"PATCH",
"PURGE",
"MKCALENDAR",
"LINK",
"UNLINK",
"SOURCE"
];
var method_connect = methods.indexOf("CONNECT");
HTTPParser.prototype.reinitialize = HTTPParser;
HTTPParser.prototype.close = HTTPParser.prototype.pause = HTTPParser.prototype.resume = HTTPParser.prototype.remove = HTTPParser.prototype.free = function() {
};
HTTPParser.prototype._compatMode0_11 = false;
HTTPParser.prototype.getAsyncId = function() {
return 0;
};
var headerState = {
REQUEST_LINE: true,
RESPONSE_LINE: true,
HEADER: true
};
HTTPParser.prototype.execute = function(chunk, start, length) {
if (!(this instanceof HTTPParser)) {
throw new TypeError("not a HTTPParser");
}
start = start || 0;
length = typeof length === "number" ? length : chunk.length;
this.chunk = chunk;
this.offset = start;
var end = this.end = start + length;
try {
while (this.offset < end) {
if (this[this.state]()) {
break;
}
}
} catch (err) {
if (this.isUserCall) {
throw err;
}
this.hadError = true;
return err;
}
this.chunk = null;
length = this.offset - start;
if (headerState[this.state]) {
this.headerSize += length;
if (this.headerSize > (this.maxHeaderSize || HTTPParser.maxHeaderSize)) {
return new Error("max header size exceeded");
}
}
return length;
};
var stateFinishAllowed = {
REQUEST_LINE: true,
RESPONSE_LINE: true,
BODY_RAW: true
};
HTTPParser.prototype.finish = function() {
if (this.hadError) {
return;
}
if (!stateFinishAllowed[this.state]) {
return new Error("invalid state for EOF");
}
if (this.state === "BODY_RAW") {
this.userCall()(this[kOnMessageComplete]());
}
};
HTTPParser.prototype.consume = HTTPParser.prototype.unconsume = HTTPParser.prototype.getCurrentBuffer = function() {
};
HTTPParser.prototype.userCall = function() {
this.isUserCall = true;
var self = this;
return function(ret) {
self.isUserCall = false;
return ret;
};
};
HTTPParser.prototype.nextRequest = function() {
this.userCall()(this[kOnMessageComplete]());
this.reinitialize(this.type);
};
HTTPParser.prototype.consumeLine = function() {
var end = this.end, chunk = this.chunk;
for (var i = this.offset; i < end; i++) {
if (chunk[i] === 10) {
var line = this.line + chunk.toString(HTTPParser.encoding, this.offset, i);
if (line.charAt(line.length - 1) === "\r") {
line = line.substr(0, line.length - 1);
}
this.line = "";
this.offset = i + 1;
return line;
}
}
this.line += chunk.toString(HTTPParser.encoding, this.offset, this.end);
this.offset = this.end;
};
var headerExp = /^([^: \t]+):[ \t]*((?:.*[^ \t])|)/;
var headerContinueExp = /^[ \t]+(.*[^ \t])/;
HTTPParser.prototype.parseHeader = function(line, headers) {
if (line.indexOf("\r") !== -1) {
throw parseErrorCode("HPE_LF_EXPECTED");
}
var match = headerExp.exec(line);
var k = match && match[1];
if (k) {
headers.push(k);
headers.push(match[2]);
} else {
var matchContinue = headerContinueExp.exec(line);
if (matchContinue && headers.length) {
if (headers[headers.length - 1]) {
headers[headers.length - 1] += " ";
}
headers[headers.length - 1] += matchContinue[1];
}
}
};
var requestExp = /^([A-Z-]+) ([^ ]+) HTTP\/(\d)\.(\d)$/;
HTTPParser.prototype.REQUEST_LINE = function() {
var line = this.consumeLine();
if (!line) {
return;
}
var match = requestExp.exec(line);
if (match === null) {
throw parseErrorCode("HPE_INVALID_CONSTANT");
}
this.info.method = this._compatMode0_11 ? match[1] : methods.indexOf(match[1]);
if (this.info.method === -1) {
throw new Error("invalid request method");
}
this.info.url = match[2];
this.info.versionMajor = +match[3];
this.info.versionMinor = +match[4];
this.body_bytes = 0;
this.state = "HEADER";
};
var responseExp = /^HTTP\/(\d)\.(\d) (\d{3}) ?(.*)$/;
HTTPParser.prototype.RESPONSE_LINE = function() {
var line = this.consumeLine();
if (!line) {
return;
}
var match = responseExp.exec(line);
if (match === null) {
throw parseErrorCode("HPE_INVALID_CONSTANT");
}
this.info.versionMajor = +match[1];
this.info.versionMinor = +match[2];
var statusCode = this.info.statusCode = +match[3];
this.info.statusMessage = match[4];
if ((statusCode / 100 | 0) === 1 || statusCode === 204 || statusCode === 304) {
this.body_bytes = 0;
}
this.state = "HEADER";
};
HTTPParser.prototype.shouldKeepAlive = function() {
if (this.info.versionMajor > 0 && this.info.versionMinor > 0) {
if (this.connection.indexOf("close") !== -1) {
return false;
}
} else if (this.connection.indexOf("keep-alive") === -1) {
return false;
}
if (this.body_bytes !== null || this.isChunked) {
return true;
}
return false;
};
HTTPParser.prototype.HEADER = function() {
var line = this.consumeLine();
if (line === void 0) {
return;
}
var info = this.info;
if (line) {
this.parseHeader(line, info.headers);
} else {
var headers = info.headers;
var hasContentLength = false;
var currentContentLengthValue;
var hasUpgradeHeader = false;
for (var i = 0; i < headers.length; i += 2) {
switch (headers[i].toLowerCase()) {
case "transfer-encoding":
this.isChunked = headers[i + 1].toLowerCase() === "chunked";
break;
case "content-length":
currentContentLengthValue = +headers[i + 1];
if (hasContentLength) {
if (currentContentLengthValue !== this.body_bytes) {
throw parseErrorCode("HPE_UNEXPECTED_CONTENT_LENGTH");
}
} else {
hasContentLength = true;
this.body_bytes = currentContentLengthValue;
}
break;
case "connection":
this.connection += headers[i + 1].toLowerCase();
break;
case "upgrade":
hasUpgradeHeader = true;
break;
}
}
if (this.isChunked && hasContentLength) {
hasContentLength = false;
this.body_bytes = null;
}
if (hasUpgradeHeader && this.connection.indexOf("upgrade") != -1) {
info.upgrade = this.type === HTTPParser.REQUEST || info.statusCode === 101;
} else {
info.upgrade = info.method === method_connect;
}
if (this.isChunked && info.upgrade) {
this.isChunked = false;
}
info.shouldKeepAlive = this.shouldKeepAlive();
var skipBody;
if (compatMode0_12) {
skipBody = this.userCall()(this[kOnHeadersComplete](info));
} else {
skipBody = this.userCall()(this[kOnHeadersComplete](
info.versionMajor,
info.versionMinor,
info.headers,
info.method,
info.url,
info.statusCode,
info.statusMessage,
info.upgrade,
info.shouldKeepAlive
));
}
if (skipBody === 2) {
this.nextRequest();
return true;
} else if (this.isChunked && !skipBody) {
this.state = "BODY_CHUNKHEAD";
} else if (skipBody || this.body_bytes === 0) {
this.nextRequest();
return info.upgrade;
} else if (this.body_bytes === null) {
this.state = "BODY_RAW";
} else {
this.state = "BODY_SIZED";
}
}
};
HTTPParser.prototype.BODY_CHUNKHEAD = function() {
var line = this.consumeLine();
if (line === void 0) {
return;
}
this.body_bytes = parseInt(line, 16);
if (!this.body_bytes) {
this.state = "BODY_CHUNKTRAILERS";
} else {
this.state = "BODY_CHUNK";
}
};
HTTPParser.prototype.BODY_CHUNK = function() {
var length = Math.min(this.end - this.offset, this.body_bytes);
this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.offset + length), 0, length));
this.offset += length;
this.body_bytes -= length;
if (!this.body_bytes) {
this.state = "BODY_CHUNKEMPTYLINE";
}
};
HTTPParser.prototype.BODY_CHUNKEMPTYLINE = function() {
var line = this.consumeLine();
if (line === void 0) {
return;
}
if (line !== "") {
throw new Error("Expected empty line");
}
this.state = "BODY_CHUNKHEAD";
};
HTTPParser.prototype.BODY_CHUNKTRAILERS = function() {
var line = this.consumeLine();
if (line === void 0) {
return;
}
if (line) {
this.parseHeader(line, this.trailers);
} else {
if (this.trailers.length) {
this.userCall()(this[kOnHeaders](this.trailers, ""));
}
this.nextRequest();
}
};
HTTPParser.prototype.BODY_RAW = function() {
this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.end), 0, this.end - this.offset));
this.offset = this.end;
};
HTTPParser.prototype.BODY_SIZED = function() {
var length = Math.min(this.end - this.offset, this.body_bytes);
this.userCall()(this[kOnBody](this.chunk.slice(this.offset, this.offset + length), 0, length));
this.offset += length;
this.body_bytes -= length;
if (!this.body_bytes) {
this.nextRequest();
}
};
["Headers", "HeadersComplete", "Body", "MessageComplete"].forEach(function(name) {
var k = HTTPParser["kOn" + name];
Object.defineProperty(HTTPParser.prototype, "on" + name, {
get: /* @__PURE__ */ __name(function() {
return this[k];
}, "get"),
set: /* @__PURE__ */ __name(function(to) {
this._compatMode0_11 = true;
method_connect = "CONNECT";
return this[k] = to;
}, "set")
});
});
function parseErrorCode(code) {
var err = new Error("Parse Error");
err.code = code;
return err;
}
__name(parseErrorCode, "parseErrorCode");