all-node-oracle
Version:
A NodeJS and Oracle DB integration, NodeJS act as http gateway for plsql server pages
189 lines (171 loc) • 4.93 kB
JavaScript
var addMap = require('./util/util.js').addMap
, util = require("util")
, events = require("events")
, reqCnt = 0
, debug = require('debug')('noradle:steps')
;
function Request(oraSock, env){
events.EventEmitter.call(this);
this.oraSock = oraSock;
this._buf = ['', ''];
this.quitting = false;
this.follows = [];
this.env = env;
this.reqCnt = ++reqCnt;
oraSock.on('socket_released', function abort(){
debug('oraSock end/error, aborted!');
req.emit('abort');
// todo: signal client
});
}
util.inherits(Request, events.EventEmitter);
Request.prototype.init = function(protocol, hprof){
this._buf[0] = protocol;
this._buf[1] = hprof || '';
return this;
};
Request.prototype.addHeaders = function(obj, prefix){
addMap(this._buf, obj, prefix);
return this;
};
Request.prototype.addHeader = function(name, value){
this._buf.push(name, value);
return this;
};
Request.prototype._sendHeaders = function(){
try {
this.oraSock.write(this._buf.join('\r\n') + '\r\n\r\n\r\n');
} catch (e) {
console.error('sendReqHead exception.');
}
return this;
};
Request.prototype.write = function(chunk){
this.oraSock.write(chunk);
};
Request.prototype.end = function(cb){
var req = this, res, c = this.oraSock, hasRead = false, hLen, bLen, cLen, bom, cSeq = 0;
req._sendHeaders();
debug(req.reqCnt, req.env, 'header sent');
c.on('readable', read);
function resFin(){
debug(req.reqCnt, req.env, 'all got');
res.emit('end');
if (req.follows.length === 0) {
c.removeListener('readable', read);
c.removeAllListeners('socket_released');
req.emit('fin');
} else {
debug(req.reqCnt, req.env, 'has follow', req.follows);
res = undefined;
hLen = undefined;
read();
}
}
function read(){
if (!hasRead) {
hasRead = true;
debug(req.reqCnt, req.env, 'first got');
}
var data;
// 1. expect response header length
if (!hLen) {
if (null === (data = c.read(4))) {
debug(req.reqCnt, req.env, 'null hLen');
return;
}
if ((hLen = data.readInt32BE(0)) < 0) {
// todo:
debug(req.reqCnt, req.env, 'quitting signal received');
req.quitting = true;
hLen = undefined;
read();
return;
}
debug(req.reqCnt, req.env, 'hLen got', hLen);
}
if (!res) {
// 2. expect whole response header text
if (null === (data = c.read(hLen))) {
return;
}
var oraResHead = data.toString('ascii').split('\r\n')
, status = parseInt(oraResHead.shift().split(' ').shift())
, header = {}
;
oraResHead.pop();
oraResHead.forEach(function(nv){
nv = nv.split(": ");
// todo: multi occurrence headers not supported by now
if (nv[0] === 'Set-Cookie') {
if (header[nv[0]]) header['Set-Cookie'].push(nv[1]);
else header['Set-Cookie'] = [nv[1]];
} else {
header[nv[0]] = nv[1];
}
});
debug(req.reqCnt, req.env, 'header got');
// 3. fix headers, bLen
bLen = parseInt(header['Content-Length']);
bom = header['x-pw-bom-hex'];
if (bom && bLen >= 0) {
bLen += bom.length / 2;
header['Content-Length'] = bLen.toString();
}
// 4. after header got, do callback with res
res = new Response(status, header);
req.emit('response', res);
cb(res);
// 5. empty body? just do fin work
if (bLen === 0) {
resFin();
return;
}
}
if (bLen) {
// 7. expect whole response body
if (null === (data = c.read(bLen))) {
return;
}
res.emit('data', data);
resFin();
return;
} else {
// transfer-encoding = chunked
if (bom) {
if (null === (data = c.read(bom.length / 2))) {
return;
}
res.emit('data', data);
bom = undefined;
}
while (true) {
if (!cLen) {
// 4. expect chunk length value
if (null === (data = c.read(4))) {
return;
}
if (0 === (cLen = data.readInt32BE(0))) {
debug('chunked transfer end');
resFin();
return;
}
}
// 5. expect whole chunk body
if (null === (data = c.read(cLen))) {
return;
}
debug('chunk', ++cSeq, data.toString('utf8').substr(0, 20));
res.emit('data', data);
cLen = 0;
}
}
}
};
function Response(status, headers){
events.EventEmitter.call(this);
this.status = status;
this.headers = headers;
}
util.inherits(Response, events.EventEmitter);
module.exports = Request;