UNPKG

@sap/odata-v4

Version:

OData V4.0 server library

142 lines (118 loc) 4.21 kB
'use strict'; const LineReader = require('./LineReader'); const Reader = require('./Reader'); const headerLineParser = require('../http/HttpHeader').parseHeaderLine; /** * States */ const STATES = { READ_NEXT_LINE: 'READ_NEXT_LINE', READ_NEXT_LINE_RETURN: 'READ_NEXT_LINE_RETURN', FINISHED: 'FINISHED' }; /** * Events */ const EVENTS = { DATA: 'header.data', END: 'header.end' }; /** * Reads a header block from the cache. Uses the LineReader to read a header line and parses the line until * a empty line is read. * Each read header (name, value) is emitted. The list of read headers is stored in a list * and can be accessed afterwards. * * @extends Reader */ class HeaderReader extends Reader { /** * Constructor */ constructor() { super(); this._headers = {}; this._headerInfos = {}; this._headerInfosRaw = {}; this._state = STATES.READ_NEXT_LINE; } /** * Returns the headers read from the cache * * @returns {Object} - Same structure as {@link http.IncomingMessage} */ getHeaderInfos() { return this._headerInfos; } /** * Returns the raw headers read from the cache (used to build a plain http request) * * @returns {Object} - Same structure as {@link http.IncomingMessage} */ getHeaderInfosRaw() { return this._headerInfosRaw; } /** * Reads header lines from the cache util a empty line is read. If the cache is empty and more data is required, * this is signaled and readCache is called again. The state is preserved. * * @param {ContentDeserializer} reader - Current reader * @returns {boolean} * this: this reader needs more data and caller should call this method again with more data in cache * false: this reader is finished caller should pop this reader from stack * null: new sub reader is on stack, call this method again after the sub reader is finished */ readCache(reader) { let needMoreData = false; let headerLineBuffer; while (needMoreData === false && this._state !== STATES.FINISHED) { switch (this._state) { case STATES.READ_NEXT_LINE: this.lineReader = new LineReader(); reader.pushReader(this.lineReader); this._state = STATES.READ_NEXT_LINE_RETURN; needMoreData = null; break; case STATES.READ_NEXT_LINE_RETURN: headerLineBuffer = this.lineReader.getLine(); if (headerLineBuffer.length > 0) { this.addHeaderLine(headerLineBuffer); this._state = STATES.READ_NEXT_LINE; } else { this._state = STATES.FINISHED; } break; default: break; } } if (this._state === STATES.FINISHED) { this.emit(EVENTS.END); } return needMoreData; // all headers read, } /** * Parses a header line. The parsed header line is emitted and stored for later use * * @param {string} headerLine */ addHeaderLine(headerLine) { const headerInfo = headerLineParser(headerLine); const nameStrLower = headerInfo.getName().toLowerCase(); // mimic nodejs style (see Class: http.IncomingMessage) attribute headers if (nameStrLower === 'set-cookie') { if (this._headers['set-cookie']) { this._headers['set-cookie'].push(headerInfo.getValue()); } else { this._headers['set-cookie'] = [headerInfo.getValue()]; } } else { this._headers[nameStrLower] = headerInfo.getValue(); // as defined in } this._headerInfosRaw[nameStrLower] = headerInfo.getValue(); this._headerInfos[nameStrLower] = headerInfo; this.emit(EVENTS.DATA, headerInfo.getName(), headerInfo.getValue(), headerInfo); } } HeaderReader.EVENTS = EVENTS; module.exports = HeaderReader;