@sap/odata-v4
Version:
OData V4.0 server library
163 lines (135 loc) • 4.97 kB
JavaScript
'use strict';
const HeaderReader = require('./HeaderReader');
const LineReader = require('./LineReader');
const Reader = require('./Reader');
/**
* States
* @enum {number}
* @readonly
*/
const STATES = {
READ_REQUEST_LINE: 0,
READ_REQUEST_LINE_RETURN: 1,
RUN_HEADER_READER: 2,
RUN_HEADER_READER_RETURN: 3,
RUN_READER: 4,
RUN_READER_RETURN: 5,
FINISHED: 6
};
/**
* Events
* @enum {string}
* @readonly
*/
const EVENTS = {
START: 'request.start',
REQUEST_LINE: 'request.requestline',
HEADERS: 'request.headers',
BODY_START: 'request.body.start',
BODY_END: 'request.body.end',
END: 'request.end'
};
const BODY_PREFIX = 'body';
/**
*
* Reading a application/http request from the cache. The request must be in the format described by mime type application/http
* and the cache must start with the request line
*
* @extends Reader
*/
class ApplicationHttpReader extends Reader {
/**
* Factory for automatic creation depending on content-type
* @returns {ApplicationHttpReader} a new instance of this class
*/
static createInstance() {
return new ApplicationHttpReader();
}
/**
* Constructor
*/
constructor() {
super();
this._state = STATES.READ_REQUEST_LINE;
this._headerInfos = null;
this._headerReader = null;
this._bodyReader = null;
/**
* Request line (e.g. POST /service/$batch HTTP/1.1) without leading/trailing CRLFs
* @private
*/
this._requestLine = null;
this._stopPattern = null;
}
/**
* Set the stop pattern. E.g. when mime type application/http is used inside a multipart request
*
* @param {string} stopPattern
*/
setStopPattern(stopPattern) {
this._stopPattern = stopPattern;
}
/**
* Read data from cache until the boundary 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
* @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;
while (needMoreData === false && this._state !== STATES.FINISHED) {
switch (this._state) {
case STATES.READ_REQUEST_LINE:
this.emit(EVENTS.START);
this.lineReader = new LineReader();
reader.pushReader(this.lineReader);
this._state = STATES.READ_REQUEST_LINE_RETURN;
needMoreData = null;
break;
case STATES.READ_REQUEST_LINE_RETURN:
this._requestLine = this.lineReader.getLine();
this.emit(EVENTS.REQUEST_LINE, this._requestLine);
this._state = STATES.RUN_HEADER_READER;
break;
case STATES.RUN_HEADER_READER:
this._headerReader = new HeaderReader().setEmitter(this._emitter);
reader.pushReader(this._headerReader);
this._state = STATES.RUN_HEADER_READER_RETURN;
needMoreData = null;
break;
case STATES.RUN_HEADER_READER_RETURN:
this._headerInfos = this._headerReader.getHeaderInfos();
this._headerInfosRaw = this._headerReader.getHeaderInfosRaw();
this.emit(EVENTS.HEADERS, this._headerInfosRaw, this._headerInfos);
this._state = STATES.RUN_READER;
break;
case STATES.RUN_READER:
this._bodyReader = reader.getNextReader(this._headerInfos).setEmitter(this._emitter, BODY_PREFIX);
if (this._stopPattern) {
this._bodyReader.setStopPattern(this._stopPattern);
}
reader.pushReader(this._bodyReader);
this.emit(EVENTS.BODY_START);
this._state = STATES.RUN_READER_RETURN;
needMoreData = null;
break;
case STATES.RUN_READER_RETURN:
this.emit(EVENTS.BODY_END);
this.emit(EVENTS.END);
this._state = STATES.FINISHED;
needMoreData = false;
break;
default:
break;
}
}
return needMoreData;
}
}
ApplicationHttpReader.EVENTS = EVENTS;
ApplicationHttpReader.BODY_PREFIX = BODY_PREFIX;
module.exports = ApplicationHttpReader;