pax_global_header 0000666 0000000 0000000 00000000064 12622547357 0014527 g ustar 00root root 0000000 0000000 52 comment=9f50d333eeae94f9c18ff83c644897cb68e06714
node-fastcgi-stream-1.0.0/ 0000775 0000000 0000000 00000000000 12622547357 0015361 5 ustar 00root root 0000000 0000000 node-fastcgi-stream-1.0.0/.eslintignore 0000664 0000000 0000000 00000000012 12622547357 0020055 0 ustar 00root root 0000000 0000000 coverage/
node-fastcgi-stream-1.0.0/.eslintrc 0000664 0000000 0000000 00000000645 12622547357 0017212 0 ustar 00root root 0000000 0000000 {
"rules": {
"indent": [
2,
2,
{"SwitchCase": 1}
],
"quotes": [
2,
"single"
],
"linebreak-style": [
2,
"unix"
],
"semi": [
2,
"always"
]
},
"env": {
"node": true,
"mocha": true
},
"extends": "eslint:recommended"
} node-fastcgi-stream-1.0.0/.gitignore 0000664 0000000 0000000 00000000031 12622547357 0017343 0 ustar 00root root 0000000 0000000 coverage/
/node_modules/
node-fastcgi-stream-1.0.0/.project 0000664 0000000 0000000 00000000324 12622547357 0017027 0 ustar 00root root 0000000 0000000
node-fastcgi-stream
node-fastcgi-stream-1.0.0/.travis.yml 0000664 0000000 0000000 00000001576 12622547357 0017503 0 ustar 00root root 0000000 0000000 language: node_js
sudo: false
cache:
directories:
- node_modules
matrix:
include:
- node_js: '0.10'
- node_js: '0.11'
- node_js: '4.2'
- node_js: '5.0'
env: npm_config_coverage=1
addons:
code_climate:
repo_token:
secure: goYYAuK21Q29GJEOTgmDNYJxUntJ6fip4WQTYUToPgeFUGg4Cm2gcL+7AW0sbdDqob/NXBLnoeWhJM+trjCY+J8Atsn+m0rTZBcZQ1QVPJg4XGXw9Zxe5Budv2CbAGaU4BMZnIjrUZgcXaO60FqHoHbba4O4CrVEDHCKnmqd06A=
after_success: |
if [ -n "$COVERAGE" ]; then
npm install -g codeclimate-test-reporter
codeclimate-test-reporter < coverage/lcov.info
fi
deploy:
provider: npm
email: me@samcday.com.au
api_key:
secure: io5NLBTubGHTq7Iwhshg8oRnLnKmP3e3J8A2+XLgoEFdQvgDHuTe2WHpwPwor2bZR7pPnBGNgfuoZp2HYO0euK5BctCei0G+spH838f1DMeBp2ZE4U7i+7M9zA0fZIdXOR6y0FUC56IVOf8t5Nl9Q3BlxJiveQgw3Qq01DhtQuE=
on:
tags: true
repo: samcday/node-fastcgi-stream
node: '5.0'
node-fastcgi-stream-1.0.0/README.md 0000664 0000000 0000000 00000011430 12622547357 0016637 0 ustar 00root root 0000000 0000000 # fastcgi-stream
[![Build Status][badge-travis-img]][badge-travis-url]
[![Dependency Information][badge-david-img]][badge-david-url]
[![Code Climate][badge-climate-img]][badge-climate-url]
[![Test Coverage][badge-coverage-img]][badge-coverage-url]
[![npmjs.org][badge-npm-img]][badge-npm-url]
Read & write FastCGI records from a node.js stream like a boss.
## Quickstart
```
npm install fastcgi-stream --save
```
The FastCGI stream library has two main pieces, the `FastCGIStream` itself and the records that can be sent and received on it.
The `FastCGIStream` wraps an existing `Stream` to send/receive FCGI records on. 99% of the time this is going to be a `net.Socket`.
```js
var fastcgi = require('fastcgi-stream');
var fcgiStream = new fastcgi.FastCGIStream(mySocket);
// Send FastCGI records.
fcgiStream.writeRecord(requestId, new fastcgi.records.BeginRequest(
fastcgi.records.BeginRequest.roles.RESPONDER,
fastcgi.records.BeginRequest.flags.KEEP_CONN
));
// Receive FastCGI records.
fcgiStream.on('record', function(requestId, record) {
if(requestId == fastcgi.constants.NULL_REQUEST_ID) {
// Management record.
}
else {
switch(record.TYPE) {
case fastcgi.records.BeginRequest.TYPE: {
// Request beginning. What role are we being asked to fulfill?
if(record.role == fastcgi.records.BeginRequest.role.RESPONDER) {
// Etc...
}
break;
}
}
}
});
```
## Records
All record objects live in the `fastcgi.records` namespace. Each record will now be listed. The listing will detail the constructor and parameters each record contains.
Constructor args are never mandatory, you can pass as many or as few arguments as you like.
### BeginRequest
var record = new fastcgi.records.BeginRequest(role, flags);
* `.role` - the role being requested. Possible roles as follows:
* `fastcgi.records.BeginRequest.roles.RESPONDER`
* `fastcgi.records.BeginRequest.roles.AUTHORIZER`
* `fastcgi.records.BeginRequest.roles.FILTER`
* `.flags` - additional flags for the request. There is only one in the specification:
* `fastcgi.records.BeginRequest.flags.KEEP_CONN`
### AbortRequest
var record = new fastcgi.records.AbortRequest();
### EndRequest
var record = new fastcgi.records.EndRequest(appStatus, protocolStatus);
* `.appStatus` - application return status code
* `.protocolStatus` - protocol return status code, can be one of the following:
* `fastcgi.records.EndRequest.protocolStatus.REQUEST_COMPLETE`
* `fastcgi.records.EndRequest.protocolStatus.CANT_MPX_CONN`
* `fastcgi.records.EndRequest.protocolStatus.OVERLOADED`
* `fastcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE`
### Params
```js
var params = [
['Name', 'Value'],
['AnotherName', 'AnotherValue']
];
// Params is optional.
var record = new fastcgi.records.Params(params);
```
`.params` - an array of name/value array pairs
### StdIn/StdOut/StdErr/Data
All of these records take the same constructor and have the same properties.
```js
var body = 'String';
var record = new fastcgi.records.StdIn(body);
// .. or ..
var body = new Buffer('Contents.');
var record = new fastcgi.records.StdIn(body);
```
### GetValues
```js
var values = ['Name', 'AnotherName'];
var record = new fastcgi.records.GetValues(values);
```
`.values` - array of values being requested
### GetValuesResult
```js
var result = [
['Name', 'Value'],
['AnotherName', 'AnotherValue']
];
var record = new fastcgi.records.GetValuesResult(result);
```
`.values` - array of name/value pairs representing the result.
### UnknownType
```js
var record = new fastcgi.records.UnknownType(type);
```
`.type` - the type of record that was not recognized.
# License
node-fastcgi-stream is free and unencumbered public domain software. For more information, see the accompanying UNLICENSE file.
[badge-travis-img]: https://img.shields.io/travis/samcday/node-fastcgi-stream.svg?style=flat-square
[badge-travis-url]: https://travis-ci.org/samcday/node-fastcgi-stream
[badge-david-img]: https://img.shields.io/david/samcday/node-fastcgi-stream.svg?style=flat-square
[badge-david-url]: https://david-dm.org/samcday/node-fastcgi-stream
[badge-npm-img]: https://img.shields.io/npm/dm/fastcgi-stream.svg
[badge-npm-url]: https://www.npmjs.org/package/fastcgi-stream
[badge-climate-img]: https://img.shields.io/codeclimate/github/samcday/node-fastcgi-stream.svg?style=flat-square
[badge-climate-url]: https://codeclimate.com/github/samcday/node-fastcgi-stream
[badge-coverage-img]: https://img.shields.io/codeclimate/coverage/github/samcday/node-fastcgi-stream.svg?style=flat-square
[badge-coverage-url]: https://codeclimate.com/github/samcday/node-fastcgi-stream
[node-docs-stream]: http://nodejs.org/api/stream.html
node-fastcgi-stream-1.0.0/TODO.md 0000664 0000000 0000000 00000001104 12622547357 0016444 0 ustar 00root root 0000000 0000000 * sanity checks in test suite to ensure you can't write params with name/values larger than 2147483647 bytes (each). Err correction, a single param length shouldn't exceed body length, maximum should probably be 65,530. This is maximum body size - 4 (for length preamble of pair item) - 1 (length preamble for other pair item)
* sanity checks to make sure you can't write stdin/stdout/stderr/data records with a body larger than 65535 bytes.
* can name/value pairs contain unicode data?
* test code is looking pretty butt-ugly now. needs a cleanup for readability and code review. node-fastcgi-stream-1.0.0/UNLICENSE 0000664 0000000 0000000 00000002273 12622547357 0016635 0 ustar 00root root 0000000 0000000 This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to
node-fastcgi-stream-1.0.0/benchmark/ 0000775 0000000 0000000 00000000000 12622547357 0017313 5 ustar 00root root 0000000 0000000 node-fastcgi-stream-1.0.0/benchmark/read_records.js 0000664 0000000 0000000 00000002532 12622547357 0022307 0 ustar 00root root 0000000 0000000 /* eslint no-console:0 */
'use strict';
var DuplexStream = require('duplex-stream');
var fastcgi = require('../lib');
var streamBuffers = require('stream-buffers');
var runTest = function(constructor, num) {
num = num || 1000;
// Make a write buffer big enough to fit everything.
var writeStream = new streamBuffers.WritableStreamBuffer({
});
var readStream = new streamBuffers.ReadableStreamBuffer();
var fastcgiStream = new fastcgi.FastCGIStream(new DuplexStream(readStream, writeStream));
for(var i = 0; i < num; i++) {
fastcgiStream.writeRecord(i+1, constructor());
}
console.log('starting');
var running = true;
var iter = 0;
readStream.pause();
readStream.put(writeStream.getContents());
fastcgiStream.on('record', function() {
if(running) iter++;
});
setTimeout(function() {
running = false;
console.log('Read ' + iter + ' records.');
readStream.pause();
}, 1000);
readStream.resume();
};
//runTest(function() { return new fastcgi.records.BeginRequest(Math.floor(Math.random()*300000 + 1), Math.floor(Math.random()*255 + 1)); }, 20000);
var myParams = [['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA']];
runTest(function() { return new fastcgi.records.Params(myParams); }, 10000); node-fastcgi-stream-1.0.0/benchmark/write_records.js 0000664 0000000 0000000 00000002303 12622547357 0022522 0 ustar 00root root 0000000 0000000 /* eslint no-console:0 */
'use strict';
var streamBuffers = require('stream-buffers');
var fastcgi = require('../lib');
var createRecords = function(constructor, num) {
num = num || 1000;
// Make a write buffer big enough to fit everything.
var writeStream = new streamBuffers.WritableStreamBuffer({
});
var fastcgiStream = new fastcgi.FastCGIStream(writeStream);
var start = Date.now();
var running = true;
var i = 1;
setTimeout(function() {
running = false;
console.log('Took ' + (Date.now() - start) + 'ms to write ' + i + ' records');
console.log('Buffer has ' + writeStream.size() + ' bytes.');
}, 1000);
var doWrite = function() {
fastcgiStream.writeRecord(i++, constructor());
if(running) process.nextTick(doWrite);
};
process.nextTick(doWrite);
};
//createRecords(function() { return new fastcgi.records.BeginRequest(Math.floor(Math.random()*300000 + 1), Math.floor(Math.random()*255 + 1)); }, 10000);
var myParams = [['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA'], ['LOLOLOLOLOL', 'HAHAHAHA']];
createRecords(function() { return new fastcgi.records.Params(myParams); }, 10000);
node-fastcgi-stream-1.0.0/lib/ 0000775 0000000 0000000 00000000000 12622547357 0016127 5 ustar 00root root 0000000 0000000 node-fastcgi-stream-1.0.0/lib/buffer_utils.js 0000664 0000000 0000000 00000002453 12622547357 0021162 0 ustar 00root root 0000000 0000000 'use strict';
// Some quick extensions to Buffer. Primarily adding methods to read/write data in network byte ordering (big endian).
module.exports = {
getInt16: function(buffer, offset) {
return buffer[offset] << 8 | buffer[offset + 1];
},
getInt32: function(buffer, offset) {
var val = ((buffer[offset] & 0x7F) << 24) | (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | (buffer[offset + 3]);
// If the most significant bit is 1, then we add 2147483648 to the number.
// This occurs because whenever we use bitwise operations, Javascript will treat the number as a 32bit SIGNED integer, which is bad as the MSB
// (most significant bit) indicates sign (+/-) of number. So why do we add 2147483648 to the number manually if the MSB is set?
// Simple, because 2147483648 == 10000000000000000000000000000000, which is the most significant bit :)
if(buffer[offset] & 0x80) val += 2147483648;
return val;
},
setInt16: function(buffer, offset, val) {
val = val & 0xFFFF;
buffer[offset] = (val & 0xFF00) >> 8;
buffer[offset + 1] = val;
},
setInt32: function(buffer, offset, val) {
buffer[offset] = val >> 24 & 0xFF;
buffer[offset + 1] = val >> 16 & 0xFF;
buffer[offset + 2] = val >> 8 & 0xFF;
buffer[offset + 3] = val & 0xFF;
}
};
node-fastcgi-stream-1.0.0/lib/constants.js 0000664 0000000 0000000 00000000214 12622547357 0020476 0 ustar 00root root 0000000 0000000 'use strict';
module.exports = {
VERSION: 1,
HEADER_LEN: 8,
MAX_CONTENT_SIZE: 65535,
MAX_PADDING_SIZE: 255,
NULL_REQUEST_ID: 0
}; node-fastcgi-stream-1.0.0/lib/index.js 0000664 0000000 0000000 00000000220 12622547357 0017566 0 ustar 00root root 0000000 0000000 'use strict';
module.exports = {
records: require('./records'),
FastCGIStream: require('./stream'),
constants: require('./constants')
};
node-fastcgi-stream-1.0.0/lib/records.js 0000664 0000000 0000000 00000001024 12622547357 0020123 0 ustar 00root root 0000000 0000000 'use strict';
module.exports = {
BeginRequest: require('./records/begin_request'),
AbortRequest: require('./records/abort_request'),
EndRequest: require('./records/end_request'),
Params: require('./records/params'),
StdIn: require('./records/stdin'),
StdOut: require('./records/stdout'),
StdErr: require('./records/stderr'),
Data: require('./records/data'),
GetValues: require('./records/get_values'),
GetValuesResult: require('./records/get_values_result'),
UnknownType: require('./records/unknown_type')
};
node-fastcgi-stream-1.0.0/lib/records/ 0000775 0000000 0000000 00000000000 12622547357 0017570 5 ustar 00root root 0000000 0000000 node-fastcgi-stream-1.0.0/lib/records/abort_request.js 0000664 0000000 0000000 00000000265 12622547357 0023010 0 ustar 00root root 0000000 0000000 'use strict';
var AbortRequest = module.exports = function AbortRequest() {
this.getSize = function() {
return 0;
};
};
AbortRequest.prototype.TYPE = AbortRequest.TYPE = 2; node-fastcgi-stream-1.0.0/lib/records/begin_request.js 0000664 0000000 0000000 00000001177 12622547357 0022770 0 ustar 00root root 0000000 0000000 'use strict';
var bufferUtils = require('../buffer_utils');
var BeginRequest = module.exports = function BeginRequest(role, flags) {
this.role = role || 0;
this.flags = flags || 0;
this.getSize = function() {
return 8;
};
this.write = function(buffer) {
bufferUtils.setInt16(buffer, 0, this.role);
buffer[2] = this.flags;
};
this.read = function(buffer) {
this.role = bufferUtils.getInt16(buffer, 0);
this.flags = buffer[2];
};
};
BeginRequest.prototype.TYPE = BeginRequest.TYPE = 1;
BeginRequest.roles = {
RESPONDER: 1,
AUTHORIZER: 2,
FILTER: 3
};
BeginRequest.flags = {
KEEPCONN: 1
}; node-fastcgi-stream-1.0.0/lib/records/common.js 0000664 0000000 0000000 00000004233 12622547357 0021420 0 ustar 00root root 0000000 0000000 // Some common functionality shared amongst record types.
/* jshint bitwise: true */
'use strict';
var bufferUtils = require('../buffer_utils');
function getPairLengths(pair) {
return pair.map(function (element) {
return Buffer.byteLength(element);
});
}
exports.calculateNameValuePairsLength = function(pairs) {
var size = 0;
pairs.forEach(function(pair) {
if(!Array.isArray(pair)) {
pair = [pair, ''];
}
var lengths = getPairLengths(pair);
size += lengths[0];
size += lengths[1];
size += (lengths[0] > 127) ? 4 : 1;
size += (lengths[1] > 127) ? 4 : 1;
});
return size;
};
exports.writeNameValuePairs = function(buffer, pairs) {
var index = 0;
pairs.forEach(function(pair) {
if(!Array.isArray(pair)) {
pair = [pair, ''];
}
var lengths = getPairLengths(pair);
if(lengths[0] > 127) {
bufferUtils.setInt32(buffer, index, lengths[0]+ 2147483648);
index += 4;
}
else {
buffer[index++] = lengths[0];
}
if(lengths[1]> 127) {
bufferUtils.setInt32(buffer, index, lengths[1]+ 2147483648);
index += 4;
}
else {
buffer[index++] = lengths[1];
}
buffer.write(pair[0], index);
index += lengths[0];
buffer.write(pair[1], index);
index += lengths[1];
});
};
function readLength(buffer, offset) {
var length = buffer[offset++];
if(length & 0x80) {
length = ((length - 128) << 24) | (buffer[offset++] << 16) | (buffer[offset++] << 8) | buffer[offset++];
}
return [length, offset];
}
exports.readNameValuePairs = function(buffer) {
var offset = 0, len = buffer.length, pairs = [], keyLength, valueLength, key, value;
while(offset < len) {
var result = readLength(buffer, offset);
keyLength = result[0];
offset = result[1];
result = readLength(buffer, offset);
valueLength = result[0];
offset = result[1];
key = buffer.toString('utf8', offset, offset + keyLength);
offset += keyLength;
if(valueLength) {
value = buffer.toString('utf8', offset, offset + valueLength);
offset += valueLength;
}
pairs.push(valueLength ? [key, value] : key);
}
return pairs;
};
node-fastcgi-stream-1.0.0/lib/records/data.js 0000664 0000000 0000000 00000001271 12622547357 0021040 0 ustar 00root root 0000000 0000000 'use strict';
var Data = module.exports = function Data(data) {
this.data = data || '';
this.getSize = function() {
return Buffer.isBuffer(this.data) ? this.data.length : Buffer.byteLength(this.data);
};
this.write = function(buffer) {
if(this.data) {
if(Buffer.isBuffer(this.data)) {
this.data.copy(buffer);
}
else {
buffer.write(this.data, 0, this.encoding || 'utf8');
}
}
};
this.read = function(buffer) {
if(this.encoding) {
this.data = buffer.toString(this.encoding);
}
else {
this.data = new Buffer(buffer.length);
buffer.copy(this.data);
}
};
};
Data.prototype.TYPE = Data.TYPE = 8; node-fastcgi-stream-1.0.0/lib/records/end_request.js 0000664 0000000 0000000 00000001276 12622547357 0022452 0 ustar 00root root 0000000 0000000 'use strict';
var bufferUtils = require('../buffer_utils');
var EndRequest = module.exports = function EndRequest(appStatus, protocolStatus) {
this.appStatus = appStatus || 0;
this.protocolStatus = protocolStatus || 0;
this.getSize = function() {
return 8;
};
this.write = function(buffer) {
bufferUtils.setInt32(buffer, 0, this.appStatus);
buffer[4] = this.protocolStatus;
};
this.read = function(buffer) {
this.appStatus = bufferUtils.getInt32(buffer, 0);
this.protocolStatus = buffer[4];
};
};
EndRequest.prototype.TYPE = EndRequest.TYPE = 3;
EndRequest.protocolStatus = {
REQUEST_COMPLETE: 0,
CANT_MPX_CONN: 1,
OVERLOADED: 2,
UNKNOWN_ROLE: 3
}; node-fastcgi-stream-1.0.0/lib/records/get_values.js 0000664 0000000 0000000 00000000736 12622547357 0022272 0 ustar 00root root 0000000 0000000 'use strict';
var common = require('./common');
var GetValues = module.exports = function GetValues(values) {
this.values = values || [];
this.getSize = function() {
return common.calculateNameValuePairsLength(this.values);
};
this.write = function(buffer) {
common.writeNameValuePairs(buffer, this.values);
};
this.read = function(buffer) {
this.values = common.readNameValuePairs(buffer);
};
};
GetValues.prototype.TYPE = GetValues.TYPE = 9; node-fastcgi-stream-1.0.0/lib/records/get_values_result.js 0000664 0000000 0000000 00000000767 12622547357 0023674 0 ustar 00root root 0000000 0000000 'use strict';
var common = require('./common');
var GetValuesResult = module.exports = function GetValuesResult(values) {
this.values = values || [];
this.getSize = function() {
return common.calculateNameValuePairsLength(this.values);
};
this.write = function(buffer) {
common.writeNameValuePairs(buffer, this.values);
};
this.read = function(buffer) {
this.values = common.readNameValuePairs(buffer);
};
};
GetValuesResult.prototype.TYPE = GetValuesResult.TYPE = 10; node-fastcgi-stream-1.0.0/lib/records/params.js 0000664 0000000 0000000 00000000724 12622547357 0021414 0 ustar 00root root 0000000 0000000 'use strict';
var common = require('./common');
var Params = module.exports = function Params(params) {
this.params = params || [];
this.getSize = function() {
return common.calculateNameValuePairsLength(this.params);
};
this.write = function(buffer) {
common.writeNameValuePairs(buffer, this.params);
};
this.read = function(buffer) {
this.params = common.readNameValuePairs(buffer);
};
};
Params.prototype.TYPE = Params.TYPE = 4; node-fastcgi-stream-1.0.0/lib/records/stderr.js 0000664 0000000 0000000 00000001301 12622547357 0021424 0 ustar 00root root 0000000 0000000 'use strict';
var StdErr = module.exports = function StdErr(data) {
this.data = data || '';
this.getSize = function() {
return Buffer.isBuffer(this.data) ? this.data.length : Buffer.byteLength(this.data);
};
this.write = function(buffer) {
if(this.data) {
if(Buffer.isBuffer(this.data)) {
this.data.copy(buffer);
}
else {
buffer.write(this.data, 0, this.encoding || 'utf8');
}
}
};
this.read = function(buffer) {
if(this.encoding) {
this.data = buffer.toString(this.encoding);
}
else {
this.data = new Buffer(buffer.length);
buffer.copy(this.data);
}
};
};
StdErr.prototype.TYPE = StdErr.TYPE = 7; node-fastcgi-stream-1.0.0/lib/records/stdin.js 0000664 0000000 0000000 00000001276 12622547357 0021255 0 ustar 00root root 0000000 0000000 'use strict';
var StdIn = module.exports = function StdIn(data) {
this.data = data || '';
this.getSize = function() {
return Buffer.isBuffer(this.data) ? this.data.length : Buffer.byteLength(this.data);
};
this.write = function(buffer) {
if(this.data) {
if(Buffer.isBuffer(this.data)) {
this.data.copy(buffer);
}
else {
buffer.write(this.data, 0, this.encoding || 'utf8');
}
}
};
this.read = function(buffer) {
if(this.encoding) {
this.data = buffer.toString(this.encoding);
}
else {
this.data = new Buffer(buffer.length);
buffer.copy(this.data);
}
};
};
StdIn.prototype.TYPE = StdIn.TYPE = 5;
node-fastcgi-stream-1.0.0/lib/records/stdout.js 0000664 0000000 0000000 00000001301 12622547357 0021443 0 ustar 00root root 0000000 0000000 'use strict';
var StdOut = module.exports = function StdOut(data) {
this.data = data || '';
this.getSize = function() {
return Buffer.isBuffer(this.data) ? this.data.length : Buffer.byteLength(this.data);
};
this.write = function(buffer) {
if(this.data) {
if(Buffer.isBuffer(this.data)) {
this.data.copy(buffer);
}
else {
buffer.write(this.data, 0, this.encoding || 'utf8');
}
}
};
this.read = function(buffer) {
if(this.encoding) {
this.data = buffer.toString(this.encoding);
}
else {
this.data = new Buffer(buffer.length);
buffer.copy(this.data);
}
};
};
StdOut.prototype.TYPE = StdOut.TYPE = 6; node-fastcgi-stream-1.0.0/lib/records/unknown_type.js 0000664 0000000 0000000 00000000533 12622547357 0022667 0 ustar 00root root 0000000 0000000 'use strict';
var UnknownType = module.exports = function UnknownType(type) {
this.type = type || 0;
this.getSize = function() {
return 8;
};
this.write = function(buffer) {
buffer[0] = this.type;
};
this.read = function(buffer) {
this.type = buffer[0];
};
};
UnknownType.prototype.TYPE = UnknownType.TYPE = 11; node-fastcgi-stream-1.0.0/lib/stream.js 0000664 0000000 0000000 00000010472 12622547357 0017764 0 ustar 00root root 0000000 0000000 'use strict';
var BufferList = require('bufferlist').BufferList;
var bufferUtils = require('./buffer_utils.js');
var constants = require('./constants');
var events = require('events');
var records = require('./records');
var util = require('util');
// Lookup table for FCGI records.
var fcgiRecords = {};
fcgiRecords[records.BeginRequest.TYPE] = records.BeginRequest;
fcgiRecords[records.AbortRequest.TYPE] = records.AbortRequest;
fcgiRecords[records.EndRequest.TYPE] = records.EndRequest;
fcgiRecords[records.Params.TYPE] = records.Params;
fcgiRecords[records.StdIn.TYPE] = records.StdIn;
fcgiRecords[records.StdOut.TYPE] = records.StdOut;
fcgiRecords[records.StdErr.TYPE] = records.StdErr;
fcgiRecords[records.Data.TYPE] = records.Data;
fcgiRecords[records.GetValues.TYPE] = records.GetValues;
fcgiRecords[records.GetValuesResult.TYPE] = records.GetValuesResult;
fcgiRecords[records.UnknownType.TYPE] = records.UnknownType;
// TODO: need to figure out an effective way to deal with incoming data. I would like to allocate a read buffer with maximum record length just like I do for the
// write buffer, however I don't think this is feasible, given that even 100 active streams would be 6.5mb in memory. For now just to get things running I'm going to
// allocate a buffer the size of the record body every time a record comes in. I'm thinking the best bet might be to have a pool of Buffers allocated when the module first
// boots up. Then I can setup a very lightweight pool manager to dish out Buffers when they're requested for incoming records. With this approach I'd then have to write some
// contingency code for peak load, increasing the Buffer pool as necessary... Although this might actually be an idea for another library - a generic pool manager that responds to
// higher load by allocating more resources in the Buffer, then deallocating them when all is quiet... But I digress.
var FastCGIStream = module.exports = function(stream) {
var that = this;
var bufferList = new BufferList();
// These variables are used for an incoming record.
var type = 0, requestId = 0, contentLength = 0, paddingLength = 0;
var processIncomingData = function() {
// There are two different states we can be in.
while(bufferList.length) {
// We don't have shit, and we're waiting for a record header.
if(bufferList.length >= 8 && !type) {
var headerData = bufferList.take(8);
bufferList.advance(8);
type = headerData[1];
requestId = bufferUtils.getInt16(headerData, 2);
contentLength = bufferUtils.getInt16(headerData, 4);
paddingLength = headerData[6];
}
// We have record header and we're waiting for record body.
if(type && (bufferList.length >= (contentLength + paddingLength))) {
var record = new fcgiRecords[type];
if(contentLength) {
var body = bufferList.take(contentLength);
record.read(body);
bufferList.advance(contentLength);
}
if(paddingLength) {
bufferList.advance(paddingLength);
}
that.emit('record', requestId, record);
// If there's no padding for us to handle, then let's reset state for another record.
type = 0;
requestId = 0;
contentLength = 0;
paddingLength = 0;
}
else {
return;
}
}
};
stream.on('data', function(data) {
bufferList.push(data);
processIncomingData();
});
this.writeRecord = function(requestId, record, callback) {
var recordBodyLength = record.getSize();
var paddingLength = recordBodyLength % 8; // Align the record to an 8 byte boundary.
var fullLength = 8 + recordBodyLength + paddingLength;
/* Allocate a new buffer, since stream.write() will hold the pointer to the buffer
* until the operation finishes, which may not occur immediately.
*/
var writeBuffer = new Buffer(fullLength);
writeBuffer[0] = constants.VERSION;
writeBuffer[1] = record.TYPE;
bufferUtils.setInt16(writeBuffer, 2, requestId);
bufferUtils.setInt16(writeBuffer, 4, recordBodyLength);
writeBuffer[6] = paddingLength;
writeBuffer[7] = 0;
if(recordBodyLength) record.write(writeBuffer.slice(8, fullLength));
return stream.write(writeBuffer, callback);
};
};
util.inherits(FastCGIStream, events.EventEmitter);
node-fastcgi-stream-1.0.0/package.json 0000664 0000000 0000000 00000001344 12622547357 0017651 0 ustar 00root root 0000000 0000000 {
"name": "fastcgi-stream",
"version": "1.0.0",
"license": "Unlicense",
"description": "Fast FastCGI Stream wrapper for reading/writing FCGI records.",
"keywords": "fcgi, fastcgi",
"author": "Sam Day ",
"main": "./lib/index.js",
"engines": {
"node": ">= 0.10.0"
},
"dependencies": {
"bufferlist": ">= 0.0.6"
},
"repository": {
"type": "git",
"url": "https://github.com/samcday/node-fastcgi-stream.git"
},
"scripts": {
"test": "istanbul test -- _mocha",
"lint": "eslint ."
},
"devDependencies": {
"chai": "^3.4.1",
"duplex-stream": ">= 0.1.0",
"eslint": "^1.9.0",
"istanbul": "^0.4.0",
"mocha": "^2.3.4",
"stream-buffers": ">= 0.2.1"
}
}
node-fastcgi-stream-1.0.0/test/ 0000775 0000000 0000000 00000000000 12622547357 0016340 5 ustar 00root root 0000000 0000000 node-fastcgi-stream-1.0.0/test/common.js 0000664 0000000 0000000 00000003760 12622547357 0020174 0 ustar 00root root 0000000 0000000 'use strict';
var DuplexStream = require('duplex-stream');
var fastcgi = require('../lib/');
var streamBuffers = require('stream-buffers');
exports.createFCGIStream = function(chunkSize) {
var readableStream = new streamBuffers.ReadableStreamBuffer({
chunkSize: chunkSize || streamBuffers.DEFAULT_CHUNK_SIZE
});
var writableStream = new streamBuffers.WritableStreamBuffer();
var fcgiStream = new fastcgi.FastCGIStream(new DuplexStream(readableStream, writableStream));
fcgiStream._readableStream = readableStream;
fcgiStream._writableStream = writableStream;
return fcgiStream;
};
exports.createDummyBuffer = function() {
var dummyBuffer = new Buffer(1025);
for(var i = 0, len = dummyBuffer.length; i < len; i++) {
dummyBuffer[i] = Math.floor(Math.random() * 255 + 1);
}
return dummyBuffer;
};
exports.fixtures = {
largeByte: 254,
largeShort: 0xFFFF,
largeInt32: 4294967295,
smallParams: [['Test', 'Value'], ['AnotherTest', 'AnotherValue']],
largeParams: [['ThisIsAReallyLongHeaderNameItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah', 'ThisIsAReallyLongHeaderValueItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah'], ['AnotherTest', 'AnotherValue']],
smallUnicodeParams: [['Параметр', 'Значение']],
largeUnicodeParams: [['Параметр', 'Очень-очень-длинное-значение-которое-явно-больше-ста-двадцати-семи-байт-в-длину']],
smallKeys: ['Test', 'Value', 'AnotherTest', 'AnotherValue'],
largeKeys: ['ThisIsAReallyLongHeaderNameItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah', 'ThisIsAReallyLongHeaderValueItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah', 'AnotherTest', 'AnotherValue'],
basicString: 'Basic String',
unicodeString: '\u00bd + \u00bc = \u00be'
};
node-fastcgi-stream-1.0.0/test/general.js 0000664 0000000 0000000 00000002744 12622547357 0020322 0 ustar 00root root 0000000 0000000 'use strict';
var assert = require('assert');
var fastcgi = require('../lib/');
describe('General constants', function() {
it('VERSION', function() {
assert.equal(fastcgi.constants.VERSION, 1);
});
it('HEADER_LEN', function() {
assert.equal(fastcgi.constants.HEADER_LEN, 8);
});
it('NULL_REQUEST_ID', function() {
assert.strictEqual(fastcgi.constants.NULL_REQUEST_ID, 0);
});
});
// Check constants.
describe('BeginRequest Role constants', function() {
it('RESPONDER', function() {
assert.equal(fastcgi.records.BeginRequest.roles.RESPONDER, 1);
});
it('AUTHORIZER', function() {
assert.equal(fastcgi.records.BeginRequest.roles.AUTHORIZER, 2);
});
it('FILTER', function() {
assert.equal(fastcgi.records.BeginRequest.roles.FILTER, 3);
});
});
describe('BeginRequest Flags constants', function() {
it('KEEP_CONN', function() {
assert.equal(fastcgi.records.BeginRequest.flags.KEEP_CONN);
});
});
describe('EndRequest Protocol Status constants', function() {
it('REQUEST_COMPLETE', function() {
assert.equal(fastcgi.records.EndRequest.protocolStatus.REQUEST_COMPLETE, 0);
});
it('CANT_MPX_CONN', function() {
assert.equal(fastcgi.records.EndRequest.protocolStatus.CANT_MPX_CONN, 1);
});
it('OVERLOADED', function() {
assert.equal(fastcgi.records.EndRequest.protocolStatus.OVERLOADED, 2);
});
it('UNKNOWN_ROLE', function() {
assert.equal(fastcgi.records.EndRequest.protocolStatus.UNKNOWN_ROLE, 3);
});
});
node-fastcgi-stream-1.0.0/test/record-read.js 0000664 0000000 0000000 00000036051 12622547357 0021072 0 ustar 00root root 0000000 0000000 'use strict';
var common = require('./common');
var expect = require('chai').expect;
var fastcgi = require('../lib/');
function addReadRecordTests(theRecord) {
switch(theRecord.TYPE) {
case fastcgi.records.BeginRequest.TYPE: {
it('*role* is correct', function() {
expect(this.record.role).to.eql(theRecord.role);
});
it('*flags* is correct', function() {
expect(this.record.flags).to.eql(theRecord.flags);
});
break;
}
case fastcgi.records.EndRequest.TYPE: {
it('*appStatus* is correct', function() {
expect(this.record.appStatus).to.eql(theRecord.appStatus);
});
it('*protocolStatus* is correct', function() {
expect(this.record.protocolStatus).to.eql(theRecord.protocolStatus);
});
break;
}
case fastcgi.records.UnknownType.TYPE: {
it('*type* is correct', function() {
expect(this.record.type).to.eql(theRecord.type);
});
break;
}
case fastcgi.records.StdIn.TYPE:
case fastcgi.records.StdOut.TYPE:
case fastcgi.records.StdErr.TYPE:
case fastcgi.records.Data.TYPE: {
it('body data is correct', function() {
var originalRecordBuffer = Buffer.isBuffer(theRecord.data) ? theRecord.data : new Buffer(theRecord.data);
var actualRecordBuffer = Buffer.isBuffer(this.record.data) ? this.record.data : new Buffer(this.record.data);
expect(actualRecordBuffer).to.eql(originalRecordBuffer);
});
break;
}
case fastcgi.records.Params.TYPE:
case fastcgi.records.GetValues.TYPE:
case fastcgi.records.GetValuesResult.TYPE: {
it('params are correct', function() {
var theParams;
if (theRecord.TYPE === fastcgi.records.Params.TYPE) {
theParams = this.record.params;
} else {
theParams = this.record.values;
}
var theRecordParams = theRecord.params || theRecord.values || theRecord.result;
expect(theParams).to.deep.eql(theRecordParams);
});
break;
}
}
}
function createReadRecordTest(desc, chunkSize, theRecord) {
if(typeof(chunkSize) !== 'number') {
theRecord = chunkSize;
chunkSize = undefined;
}
var theRequestId = Math.floor(Math.random() * 65535 + 1);
describe(desc, function() {
before(function(done) {
var self = this;
this.fastcgiStream = common.createFCGIStream(chunkSize);
this.fastcgiStream.on('record', function(requestId, record) {
self.requestId = requestId;
self.record = record;
done();
}.bind(this));
this.fastcgiStream.writeRecord(theRequestId, theRecord);
var buf = this.fastcgiStream._writableStream.getContents();
this.fastcgiStream._readableStream.put(buf);
});
it('read buffer was fully read', function() {
expect(this.fastcgiStream._readableStream.size()).to.eql(0);
});
it('*requestId* is correct', function() {
expect(this.requestId).to.eql(theRequestId);
});
it('record is correct type', function() {
expect(this.record.TYPE).to.eql(theRecord.TYPE);
});
addReadRecordTests(theRecord);
});
}
function createMultiReadRecordTest(desc, constructor) {
var requestIds = [];
var records = [];
var fastcgiStream = common.createFCGIStream();
var theRecord = constructor();
for(var i = 0; i < 5; i++) {
var requestId = Math.floor(Math.random() * 65535 + 1);
requestIds.push(requestId);
records.push(theRecord);
fastcgiStream.writeRecord(requestId, theRecord);
}
var buf = fastcgiStream._writableStream.getContents();
describe(desc, function() {
before(function(done) {
this.actualRequestIds = [];
this.actualRecords = [];
fastcgiStream.on('record', function(requestId, record) {
this.actualRequestIds.push(requestId);
this.actualRecords.push(record);
if(this.actualRequestIds.length == 5) done();
}.bind(this));
fastcgiStream._readableStream.put(buf);
});
for(var i = 0; i < 5; i++) {
describe('(record #' + i + ')', (function(i) {
return function() {
before(function() {
this.requestId = this.actualRequestIds[i];
this.record = this.actualRecords[i];
});
it('*requestId* is correct', function() {
expect(this.requestId).to.eql(requestIds[i]);
});
it('record is correct type', function() {
expect(this.record.TYPE).to.eql(records[i].TYPE);
});
addReadRecordTests(theRecord);
};
})(i));
}
});
}
// We're verifying the record reading against records written out by the same system.
// You might consider this dumb, but I think it's okay, given that we've already run a shiteload of sanity checks on the records.
// And we've tested the raw binary output of the writing process, making sure it's according to spec.
describe('FastCGIStream reading', function() {
createReadRecordTest('an FCGI_BEGIN_REQUEST', new fastcgi.records.BeginRequest(fastcgi.records.BeginRequest.roles.Responder, 254));
createReadRecordTest('an FCGI_ABORT_REQUEST', new fastcgi.records.AbortRequest());
createReadRecordTest('an FCGI_END_REQUEST', new fastcgi.records.EndRequest(4294967295, 254));
createReadRecordTest('an FCGI_PARAMS (empty)', new fastcgi.records.Params());
createReadRecordTest('an FCGI_PARAMS (small name/value pairs)', new fastcgi.records.Params(common.fixtures.smallParams));
createReadRecordTest('an FCGI_PARAMS (large name/value pairs)', new fastcgi.records.Params(common.fixtures.largeParams));
createReadRecordTest('an FCGI_STDIN (empty)', new fastcgi.records.StdIn());
createReadRecordTest('an FCGI_STDIN (string)', new fastcgi.records.StdIn(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDIN (unicode string)', new fastcgi.records.StdIn(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDIN (buffer)', new fastcgi.records.StdIn(common.createDummyBuffer()));
createReadRecordTest('an FCGI_STDOUT (empty)', new fastcgi.records.StdOut());
createReadRecordTest('an FCGI_STDOUT (string)', new fastcgi.records.StdOut(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDOUT (unicode string)', new fastcgi.records.StdOut(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDOUT (buffer)', new fastcgi.records.StdOut(common.createDummyBuffer()));
createReadRecordTest('an FCGI_STDERR (empty)', new fastcgi.records.StdErr());
createReadRecordTest('an FCGI_STDERR (string)', new fastcgi.records.StdErr(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDERR (unicode string)', new fastcgi.records.StdErr(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDERR (buffer)', new fastcgi.records.StdErr(common.createDummyBuffer()));
createReadRecordTest('an FCGI_DATA (empty)', new fastcgi.records.Data());
createReadRecordTest('an FCGI_DATA (string)', new fastcgi.records.Data(common.fixtures.basicString));
createReadRecordTest('an FCGI_DATA (unicode string)', new fastcgi.records.Data(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_DATA (buffer)', new fastcgi.records.Data(common.createDummyBuffer()));
createReadRecordTest('an FCGI_GET_VALUES (empty)', new fastcgi.records.GetValues());
createReadRecordTest('an FCGI_GET_VALUES (small name/value pairs)', new fastcgi.records.GetValues(common.fixtures.smallKeys));
createReadRecordTest('an FCGI_GET_VALUES (large name/value pairs)', new fastcgi.records.GetValues(common.fixtures.largeKeys));
createReadRecordTest('an FCGI_GET_VALUES_RESULT (empty)', new fastcgi.records.GetValuesResult());
createReadRecordTest('an FCGI_GET_VALUES_RESULT (small name/value pairs)', new fastcgi.records.GetValuesResult(common.fixtures.smallParams));
createReadRecordTest('an FCGI_GET_VALUES_RESULT (large name/value pairs)', new fastcgi.records.GetValuesResult(common.fixtures.largeParams));
createReadRecordTest('an FCGI_UNKNOWN_TYPE', new fastcgi.records.UnknownType(common.fixtures.largeByte));
});
describe('FastCGIStream reading (trickle-fed)', function() {
createReadRecordTest('an FCGI_BEGIN_REQUEST', 1, new fastcgi.records.BeginRequest(fastcgi.records.BeginRequest.roles.Responder, 254));
createReadRecordTest('an FCGI_ABORT_REQUEST', 1, new fastcgi.records.AbortRequest());
createReadRecordTest('an FCGI_END_REQUEST', 1, new fastcgi.records.EndRequest(4294967295, 254));
createReadRecordTest('an FCGI_PARAMS (empty)', 1, new fastcgi.records.Params());
createReadRecordTest('an FCGI_PARAMS (small name/value pairs)', 1, new fastcgi.records.Params(common.fixtures.smallParams));
createReadRecordTest('an FCGI_PARAMS (large name/value pairs)', 1, new fastcgi.records.Params(common.fixtures.largeParams));
createReadRecordTest('an FCGI_STDIN (empty)', 1, new fastcgi.records.StdIn());
createReadRecordTest('an FCGI_STDIN (string)', 1, new fastcgi.records.StdIn(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDIN (unicode string)', 1, new fastcgi.records.StdIn(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDIN (buffer)', 1, new fastcgi.records.StdIn(common.createDummyBuffer()));
createReadRecordTest('an FCGI_STDOUT (empty)', 1, new fastcgi.records.StdOut());
createReadRecordTest('an FCGI_STDOUT (string)', 1, new fastcgi.records.StdOut(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDOUT (unicode string)', 1, new fastcgi.records.StdOut(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDOUT (buffer)', 1, new fastcgi.records.StdOut(common.createDummyBuffer()));
createReadRecordTest('an FCGI_STDERR (empty)', 1, new fastcgi.records.StdErr());
createReadRecordTest('an FCGI_STDERR (string)', 1, new fastcgi.records.StdErr(common.fixtures.basicString));
createReadRecordTest('an FCGI_STDERR (unicode string)', 1, new fastcgi.records.StdErr(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_STDERR (buffer)', 1, new fastcgi.records.StdErr(common.createDummyBuffer()));
createReadRecordTest('an FCGI_DATA (empty)', 1, new fastcgi.records.Data());
createReadRecordTest('an FCGI_DATA (string)', 1, new fastcgi.records.Data(common.fixtures.basicString));
createReadRecordTest('an FCGI_DATA (unicode string)', 1, new fastcgi.records.Data(common.fixtures.unicodeString));
createReadRecordTest('an FCGI_DATA (buffer)', 1, new fastcgi.records.Data(common.createDummyBuffer()));
createReadRecordTest('an FCGI_GET_VALUES (empty)', 1, new fastcgi.records.GetValues());
createReadRecordTest('an FCGI_GET_VALUES (small name/value pairs)', 1, new fastcgi.records.GetValues(common.fixtures.smallKeys));
createReadRecordTest('an FCGI_GET_VALUES (large name/value pairs)', 1, new fastcgi.records.GetValues(common.fixtures.largeKeys));
createReadRecordTest('an FCGI_GET_VALUES_RESULT (empty)', 1, new fastcgi.records.GetValuesResult());
createReadRecordTest('an FCGI_GET_VALUES_RESULT (small name/value pairs)', 1, new fastcgi.records.GetValuesResult(common.fixtures.smallParams));
createReadRecordTest('an FCGI_GET_VALUES_RESULT (large name/value pairs)', 1, new fastcgi.records.GetValuesResult(common.fixtures.largeParams));
createReadRecordTest('an FCGI_UNKNOWN_TYPE', 1, new fastcgi.records.UnknownType(common.fixtures.largeByte));
});
describe('FastCGIStream reading (multiple records)', function() {
createMultiReadRecordTest('an FCGI_BEGIN_REQUEST', function() { return new fastcgi.records.BeginRequest(fastcgi.records.BeginRequest.roles.Responder, 254); });
createMultiReadRecordTest('an FCGI_ABORT_REQUEST', function() { return new fastcgi.records.AbortRequest(); });
createMultiReadRecordTest('an FCGI_END_REQUEST', function() { return new fastcgi.records.EndRequest(4294967295, 254); });
createMultiReadRecordTest('an FCGI_PARAMS (empty)', function() { return new fastcgi.records.Params(); });
createMultiReadRecordTest('an FCGI_PARAMS (small name/value pairs)', function() { return new fastcgi.records.Params(common.fixtures.smallParams); });
createMultiReadRecordTest('an FCGI_PARAMS (large name/value pairs)', function() { return new fastcgi.records.Params(common.fixtures.largeParams); });
createMultiReadRecordTest('an FCGI_STDIN (empty)', function() { return new fastcgi.records.StdIn(); });
createMultiReadRecordTest('an FCGI_STDIN (string)', function() { return new fastcgi.records.StdIn(common.fixtures.basicString); });
createMultiReadRecordTest('an FCGI_STDIN (unicode string)', function() { return new fastcgi.records.StdIn(common.fixtures.unicodeString); });
createMultiReadRecordTest('an FCGI_STDIN (buffer)', function() { return new fastcgi.records.StdIn(common.createDummyBuffer()); });
createMultiReadRecordTest('an FCGI_STDOUT (empty)', function() { return new fastcgi.records.StdOut(); });
createMultiReadRecordTest('an FCGI_STDOUT (string)', function() { return new fastcgi.records.StdOut(common.fixtures.basicString); });
createMultiReadRecordTest('an FCGI_STDOUT (unicode string)', function() { return new fastcgi.records.StdOut(common.fixtures.unicodeString); });
createMultiReadRecordTest('an FCGI_STDOUT (buffer)', function() { return new fastcgi.records.StdOut(common.createDummyBuffer()); });
createMultiReadRecordTest('an FCGI_STDERR (empty)', function() { return new fastcgi.records.StdErr(); });
createMultiReadRecordTest('an FCGI_STDERR (string)', function() { return new fastcgi.records.StdErr(common.fixtures.basicString); });
createMultiReadRecordTest('an FCGI_STDERR (unicode string)', function() { return new fastcgi.records.StdErr(common.fixtures.unicodeString); });
createMultiReadRecordTest('an FCGI_STDERR (buffer)', function() { return new fastcgi.records.StdErr(common.createDummyBuffer()); });
createMultiReadRecordTest('an FCGI_DATA (empty)', function() { return new fastcgi.records.Data(); });
createMultiReadRecordTest('an FCGI_DATA (string)', function() { return new fastcgi.records.Data(common.fixtures.basicString); });
createMultiReadRecordTest('an FCGI_DATA (unicode string)', function() { return new fastcgi.records.Data(common.fixtures.unicodeString); });
createMultiReadRecordTest('an FCGI_DATA (buffer)', function() { return new fastcgi.records.Data(common.createDummyBuffer()); });
createMultiReadRecordTest('an FCGI_GET_VALUES (empty)', function() { return new fastcgi.records.GetValues(); });
createMultiReadRecordTest('an FCGI_GET_VALUES (small name/value pairs)', function() { return new fastcgi.records.GetValues(common.fixtures.smallKeys); });
createMultiReadRecordTest('an FCGI_GET_VALUES (large name/value pairs)', function() { return new fastcgi.records.GetValues(common.fixtures.largeKeys); });
createMultiReadRecordTest('an FCGI_GET_VALUES_RESULT (empty)', function() { return new fastcgi.records.GetValuesResult(); });
createMultiReadRecordTest('an FCGI_GET_VALUES_RESULT (small name/value pairs)', function() { return new fastcgi.records.GetValuesResult(common.fixtures.smallParams); });
createMultiReadRecordTest('an FCGI_GET_VALUES_RESULT (large name/value pairs)', function() { return new fastcgi.records.GetValuesResult(common.fixtures.largeParams); });
createMultiReadRecordTest('an FCGI_UNKNOWN_TYPE', function() { return new fastcgi.records.UnknownType(common.fixtures.largeByte); });
});
node-fastcgi-stream-1.0.0/test/record-write.js 0000664 0000000 0000000 00000022633 12622547357 0021312 0 ustar 00root root 0000000 0000000 'use strict';
var bufferUtils = require('../lib/buffer_utils.js');
var common = require('./common');
var expect = require('chai').expect;
var fastcgi = require('../lib/');
var createWriteRecordTest = function(desc, record) {
var requestId = Math.floor(Math.random() * 65535 + 1);
describe(desc, function() {
before(function() {
this.fcgiStream = common.createFCGIStream();
this.fcgiStream.writeRecord(requestId, record);
});
it('writes at least 8 bytes', function() {
expect(this.fcgiStream._writableStream.size()).to.be.at.least(8);
});
it('', function() {
this.header = this.fcgiStream._writableStream.getContents(8);
this.contentLength = bufferUtils.getInt16(this.header, 4);
this.paddingLength = this.header[6];
});
describe('header', function() {
it('has correct version', function() {
expect(this.header[0]).to.eql(1);
});
it('has correct type', function() {
expect(this.header[1]).to.eql(record.TYPE);
});
it('has correct requestId', function() {
expect(bufferUtils.getInt16(this.header, 2)).to.eql(requestId);
});
it('body size matches record calculation', function() {
expect(this.contentLength).to.eql(record.getSize());
});
it('body and padding are correct length', function() {
expect(this.fcgiStream._writableStream.size()).to.eql(this.contentLength + this.paddingLength);
});
it('padding is correct length', function() {
expect(this.paddingLength).to.eql(this.contentLength % 8);
});
});
if(record.getSize()) {
describe('the body', function() {
createRecordBodyTests(record);
});
}
});
};
function createRecordBodyTests(record) {
before(function() {
this.body = this.fcgiStream._writableStream.getContents(this.contentLength);
});
switch(record.TYPE) {
case fastcgi.records.BeginRequest.TYPE: {
it('role is correct', function() {
expect(bufferUtils.getInt16(this.body, 0)).to.eql(record.role);
});
it('flags are correct', function() {
expect(this.body[2]).to.eql(record.flags);
});
break;
}
case fastcgi.records.EndRequest.TYPE: {
it('appStatus is correct', function() {
expect(bufferUtils.getInt32(this.body, 0)).to.eql(record.appStatus);
});
it('protocolStatus is correct', function() {
expect(this.body[4]).to.eql(record.protocolStatus);
});
break;
}
case fastcgi.records.Params.TYPE:
case fastcgi.records.GetValues.TYPE:
case fastcgi.records.GetValuesResult.TYPE: {
var theParams = record.params || record.values || record.result;
// Calculate size for each name/value pair, including the length preambles.
var totalSize = 0;
var paramOffsets = [];
var pairSizes = theParams.map(function(param) {
if(!Array.isArray(param)) param = [param, ''];
var offset = totalSize;
var keySize, valueSize, paramSize = 0;
var lengths = param.map(function (element) {
return Buffer.byteLength(element);
});
paramSize += (lengths[0] > 127) ? 4 : 1;
paramSize += (lengths[1] > 127) ? 4 : 1;
keySize = lengths[0];
valueSize = lengths[1];
paramSize += keySize + valueSize;
paramOffsets.push({start: offset, end: offset + paramSize});
totalSize += paramSize;
return {keySize: keySize, valueSize: valueSize};
});
it('overall length is correct', function() {
expect(this.body.length).to.eql(totalSize);
});
theParams.forEach(function(param, index) {
if(!Array.isArray(param)) param = [param, ''];
describe('param *' + param[0] + '*', function() {
before(function() {
var offset = paramOffsets[index];
this.paramBuffer = this.body.slice(offset.start, offset.end);
});
it('key length is correct', function() {
if(pairSizes[index].keySize > 127) {
expect(bufferUtils.getInt32(this.paramBuffer, 0)).to.eql(pairSizes[index].keySize + 2147483648);
}
else {
expect(this.paramBuffer[0]).to.eql(pairSizes[index].keySize);
}
});
it('value length is correct', function() {
var offset = (pairSizes[index].keySize > 127) ? 4 : 1;
if(pairSizes[index].valueSize > 127) {
expect(bufferUtils.getInt32(this.paramBuffer, offset)).to.eql(pairSizes[index].valueSize + 2147483648);
}
else {
expect(this.paramBuffer[offset]).to.eql(pairSizes[index].valueSize);
}
});
it('key is correct', function() {
var offset = ((pairSizes[index].keySize > 127) ? 4 : 1) + ((pairSizes[index].valueSize > 127) ? 4 : 1);
expect(this.paramBuffer.toString('utf8', offset, pairSizes[index].keySize + offset)).to.eql(param[0]);
});
it('value is correct', function() {
var offset = ((pairSizes[index].keySize > 127) ? 4 : 1) + ((pairSizes[index].valueSize > 127) ? 4 : 1);
expect(this.paramBuffer.toString('utf8', pairSizes[index].keySize + offset)).to.eql(param[1]);
});
});
});
break;
}
case fastcgi.records.Data.TYPE:
case fastcgi.records.StdIn.TYPE:
case fastcgi.records.StdOut.TYPE:
case fastcgi.records.StdErr.TYPE: {
it('body data is correct', function() {
var dataAsBuffer = Buffer.isBuffer(record.data) ? record.data : new Buffer(record.data);
for(var i = 0; i < dataAsBuffer.length; i++) {
expect(this.body[i]).to.eql(dataAsBuffer[i], 'Data at index #' + i + ' does not match.');
}
});
break;
}
case fastcgi.records.UnknownType.TYPE: {
it('type is correct', function() {
expect(this.body[0]).to.eql(record.type);
});
break;
}
}
}
describe('FastCGIStream writing', function() {
createWriteRecordTest('an FCGI_BEGIN_REQUEST', new fastcgi.records.BeginRequest(common.fixtures.largeShort, common.fixtures.largeByte));
createWriteRecordTest('an FCGI_ABORT_REQUEST', new fastcgi.records.AbortRequest());
createWriteRecordTest('an FCGI_END_REQUEST', new fastcgi.records.EndRequest(common.fixtures.largeInt32, common.fixtures.largeByte));
createWriteRecordTest('an FCGI_PARAMS (empty)', new fastcgi.records.Params());
createWriteRecordTest('an FCGI_PARAMS (small name/value pairs)', new fastcgi.records.Params(common.fixtures.smallParams));
createWriteRecordTest('an FCGI_PARAMS (large name/value pairs)', new fastcgi.records.Params(common.fixtures.largeParams));
createWriteRecordTest('an FCGI_PARAMS (small unicode name/value pairs)', new fastcgi.records.Params(common.fixtures.smallUnicodeParams));
createWriteRecordTest('an FCGI_PARAMS (large unicode name/value pairs)', new fastcgi.records.Params(common.fixtures.largeUnicodeParams));
createWriteRecordTest('an FCGI_STDIN (empty)', new fastcgi.records.StdIn());
createWriteRecordTest('an FCGI_STDIN (string)', new fastcgi.records.StdIn(common.fixtures.basicString));
createWriteRecordTest('an FCGI_STDIN (unicode string)', new fastcgi.records.StdIn(common.fixtures.unicodeString));
createWriteRecordTest('an FCGI_STDIN (buffer)', new fastcgi.records.StdIn(common.createDummyBuffer()));
createWriteRecordTest('an FCGI_STDOUT (empty)', new fastcgi.records.StdOut());
createWriteRecordTest('an FCGI_STDOUT (string)', new fastcgi.records.StdOut(common.fixtures.basicString));
createWriteRecordTest('an FCGI_STDOUT (unicode string)', new fastcgi.records.StdOut(common.fixtures.unicodeString));
createWriteRecordTest('an FCGI_STDOUT (buffer)', new fastcgi.records.StdOut(common.createDummyBuffer()));
createWriteRecordTest('an FCGI_STDERR (empty)', new fastcgi.records.StdErr());
createWriteRecordTest('an FCGI_STDERR (string)', new fastcgi.records.StdErr(common.fixtures.basicString));
createWriteRecordTest('an FCGI_STDERR (unicode string)', new fastcgi.records.StdErr(common.fixtures.unicodeString));
createWriteRecordTest('an FCGI_STDERR (buffer)', new fastcgi.records.StdErr(common.createDummyBuffer()));
createWriteRecordTest('an FCGI_DATA (empty)', new fastcgi.records.Data());
createWriteRecordTest('an FCGI_DATA (string)', new fastcgi.records.Data(common.fixtures.basicString));
createWriteRecordTest('an FCGI_DATA (unicode string)', new fastcgi.records.Data(common.fixtures.unicodeString));
createWriteRecordTest('an FCGI_DATA (buffer)', new fastcgi.records.Data(common.createDummyBuffer()));
createWriteRecordTest('an FCGI_GET_VALUES (empty)', new fastcgi.records.GetValues());
createWriteRecordTest('an FCGI_GET_VALUES (small name/value pairs)', new fastcgi.records.GetValues(common.fixtures.smallKeys));
createWriteRecordTest('an FCGI_GET_VALUES (large name/value pairs)', new fastcgi.records.GetValues(common.fixtures.largeKeys));
createWriteRecordTest('an FCGI_GET_VALUES_RESULT (empty)', new fastcgi.records.GetValuesResult());
createWriteRecordTest('an FCGI_GET_VALUES_RESULT (small name/value pairs)', new fastcgi.records.GetValuesResult(common.fixtures.smallParams));
createWriteRecordTest('an FCGI_GET_VALUES_RESULT (large name/value pairs)', new fastcgi.records.GetValuesResult(common.fixtures.largeParams));
createWriteRecordTest('an FCGI_UNKNOWN_TYPE', new fastcgi.records.UnknownType(common.fixtures.largeByte));
});
node-fastcgi-stream-1.0.0/test/records.js 0000664 0000000 0000000 00000012725 12622547357 0020346 0 ustar 00root root 0000000 0000000 'use strict';
var assert = require('assert');
var fastcgi = require('../lib/');
function createRecordSanityChecks(opts) {
var record = new opts.class();
if (opts.props) {
Object.keys(opts.props).forEach(function(valueName) {
record[valueName] = opts.props[valueName];
});
}
describe('Record type ' + opts.class.name, function() {
if(opts.expectedSize !== undefined) {
it('calculates its size correctly', function() {
assert.equal(record.getSize(), opts.expectedSize);
});
}
if(opts.expectedType) {
it('record has correct type', function() {
assert.equal(record.TYPE, opts.expectedType);
});
it('record constructor has correct type', function() {
assert.equal(opts.class.TYPE, opts.expectedType);
});
}
});
}
createRecordSanityChecks({
class: fastcgi.records.BeginRequest,
expectedSize: 8,
expectedType: 1
});
createRecordSanityChecks({
class: fastcgi.records.AbortRequest,
expectedSize: 0,
expectedType: 2
});
createRecordSanityChecks({
class: fastcgi.records.EndRequest,
expectedSize: 8,
expectedType: 3
});
createRecordSanityChecks({
class: fastcgi.records.Params,
expectedSize: 0,
expectedType: 4
});
createRecordSanityChecks({
class: fastcgi.records.Params,
props: {
params: [
['Test', 'Value'],
['AnotherTest', 'AnotherValue']
]
},
expectedSize: 36
});
createRecordSanityChecks({
class: fastcgi.records.Params,
props: {
params: [
['Test', 'Value'],
['ThisIsAReallyLongHeaderNameItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah', 'ThisIsAReallyLongHeaderValueItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah']
]
},
expectedSize: 280
});
createRecordSanityChecks({
class: fastcgi.records.Params,
props: {
params: [
['Параметр', 'Значение']
]
},
expectedSize: 1 + 16 + 1 + 16
});
createRecordSanityChecks({
class: fastcgi.records.Params,
props: {
params: [
['Параметр', 'Очень-очень-длинное-значение-которое-явно-больше-ста-двадцати-семи-байт-в-длину']
]
},
expectedSize: 1 + 16 + 4 + 146
});
createRecordSanityChecks({
class: fastcgi.records.StdIn,
expectedSize: 0,
expectedType: 5
});
createRecordSanityChecks({
class: fastcgi.records.StdIn,
props: {
data: 'Hello'
},
expectedSize: 5
});
createRecordSanityChecks({
class: fastcgi.records.StdIn,
props: {
data: '\u00bd + \u00bc = \u00be'
},
expectedSize: 12
});
createRecordSanityChecks({
class: fastcgi.records.StdIn,
props: {
data: new Buffer(10)
},
expectedSize: 10
});
createRecordSanityChecks({
class: fastcgi.records.StdOut,
expectedSize: 0,
expectedType: 6
});
createRecordSanityChecks({
class: fastcgi.records.StdOut,
props: {
data: 'Hello'
},
expectedSize: 5
});
createRecordSanityChecks({
class: fastcgi.records.StdOut,
props: {
data: '\u00bd + \u00bc = \u00be'
},
expectedSize: 12
});
createRecordSanityChecks({
class: fastcgi.records.StdOut,
props: {
data: new Buffer(10)
},
expectedSize: 10
});
createRecordSanityChecks({
class: fastcgi.records.StdErr,
expectedSize: 0,
expectedType: 7
});
createRecordSanityChecks({
class: fastcgi.records.StdErr,
props: {
data: 'Hello'
},
expectedSize: 5
});
createRecordSanityChecks({
class: fastcgi.records.StdErr,
props: {
data: '\u00bd + \u00bc = \u00be'
},
expectedSize: 12
});
createRecordSanityChecks({
class: fastcgi.records.StdErr,
props: {
data: new Buffer(10)
},
expectedSize: 10
});
createRecordSanityChecks({
class: fastcgi.records.Data,
expectedSize: 0,
expectedType: 8
});
createRecordSanityChecks({
class: fastcgi.records.Data,
props: {
data: 'Hello'
},
expectedSize: 5
});
createRecordSanityChecks({
class: fastcgi.records.Data,
props: {
data: '\u00bd + \u00bc = \u00be'
},
expectedSize: 12
});
createRecordSanityChecks({
class: fastcgi.records.Data,
props: {
data: new Buffer(10)
},
expectedSize: 10
});
createRecordSanityChecks({
class: fastcgi.records.GetValues,
expectedSize: 0,
expectedType: 9
});
createRecordSanityChecks({
class: fastcgi.records.GetValues,
props: {
values: ['Test', 'AnotherTest']
},
expectedSize: 19
});
createRecordSanityChecks({
class: fastcgi.records.GetValues,
props: {
values: ['Test', 'ThisIsAReallyLongHeaderNameItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah']
},
expectedSize: 141
});
createRecordSanityChecks({
class: fastcgi.records.GetValuesResult,
expectedSize: 0,
expectedType: 10
});
createRecordSanityChecks({
class: fastcgi.records.GetValuesResult,
props: {
values: [
['Test', 'Value'],
['AnotherTest', 'AnotherValue']
]
},
expectedSize: 36
});
createRecordSanityChecks({
class: fastcgi.records.GetValuesResult,
props: {
values: [
['Test', 'Value'],
['ThisIsAReallyLongHeaderNameItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah', 'ThisIsAReallyLongHeaderValueItIsGoingToExceedOneHundredAndTwentySevenBytesJustYouWatchAreYouReadyOkHereWeGoBlahBlahBlahBlahBlahBlah']
]
},
expectedSize: 280
});
createRecordSanityChecks({
class: fastcgi.records.UnknownType,
expectedSize: 8,
expectedType: 11
});