UNPKG

hap-controller

Version:

Library to implement a HAP (HomeKit) controller

120 lines 5.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /** * Basic parser for HTTP-based HAP event messages. */ const events_1 = require("events"); var State; (function (State) { State[State["EMPTY"] = 0] = "EMPTY"; State[State["REQUEST_LINE_COMPLETE"] = 1] = "REQUEST_LINE_COMPLETE"; State[State["HEADERS_COMPLETE"] = 2] = "HEADERS_COMPLETE"; })(State || (State = {})); const HEADER_SUFFIX = '\r\n\r\n'; class HttpEventParser extends events_1.EventEmitter { /** * Initialize the HttpEventParser object. */ constructor() { super(); this._pending = Buffer.alloc(0); this._state = State.EMPTY; this.headers = {}; this.protocol = null; this.version = null; this.statusCode = null; this.statusMessage = null; this.body = Buffer.alloc(0); this._reset(); } /** * Execute parser on a chunk of data. * * @param {Buffer} data - Chunk of data */ execute(data) { this._pending = Buffer.concat([this._pending, data]); while (this._pending.length > 0) { switch (this._state) { case State.EMPTY: { const crlf = this._pending.indexOf('\r\n'); if (crlf < 0) { return; } const requestLine = this._pending.slice(0, crlf).toString(); this._pending = this._pending.slice(crlf + 2, this._pending.length); let parts = requestLine.split(' '); this.statusCode = parseInt(parts[1], 10); this.statusMessage = parts.slice(2).join(' '); parts = parts[0].split('/', 2); this.protocol = parts[0]; this.version = parts[1]; this._state = State.REQUEST_LINE_COMPLETE; break; } case State.REQUEST_LINE_COMPLETE: { const end = this._pending.indexOf(HEADER_SUFFIX); if (end < 0) { return; } const headers = this._pending.slice(0, end).toString(); this._pending = this._pending.slice(end + 4, this._pending.length); const lines = headers.split('\r\n'); for (const line of lines) { const idx = line.indexOf(':'); if (idx > 0) { const name = line.substring(0, idx).trim(); const value = line.substring(idx + 1, line.length).trim(); this.headers[name.toLowerCase()] = value; } } if (parseInt(this.headers['content-length'], 10) === 0) { this._reset(); } else { this._state = State.HEADERS_COMPLETE; } break; } case State.HEADERS_COMPLETE: { if (typeof this.headers['content-length'] !== 'undefined') { const contentLength = parseInt(this.headers['content-length'], 10); const toCopy = Math.min(contentLength - this.body.length, this._pending.length); this.body = Buffer.concat([this.body, this._pending.slice(0, toCopy)]); this._pending = this._pending.slice(toCopy, this._pending.length); if (this.body.length === contentLength && this.protocol === 'EVENT') { this.emit('event', this.body); this._reset(); } } else if (typeof this.headers['content-type'] !== 'undefined') { this.body = Buffer.from(this._pending); const firstPosition = this.body.indexOf('{'); const lastPosition = this.body.lastIndexOf('}'); this.body = this.body.slice(firstPosition, lastPosition + 1); this._pending = this._pending.slice(this.body.length, this._pending.length); if (this.body.length > 0 && this.protocol === 'EVENT') { this.emit('event', this.body); this._reset(); } } break; } } } } /** * Reset the internal parser state. */ _reset() { this.protocol = null; this.version = null; this.statusCode = null; this.statusMessage = null; this.headers = {}; this.body = Buffer.alloc(0); this._state = State.EMPTY; } } exports.default = HttpEventParser; //# sourceMappingURL=http-event-parser.js.map