@sap/odata-v4
Version:
OData V4.0 server library
142 lines (118 loc) • 4.21 kB
JavaScript
'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;