Added Node.js support.
This commit is contained in:
parent
141ee2aa32
commit
ea62327b00
13 changed files with 1667 additions and 0 deletions
|
@ -25,6 +25,10 @@ case "$nxt_module" in
|
|||
. auto/modules/ruby
|
||||
;;
|
||||
|
||||
nodejs)
|
||||
. auto/modules/nodejs
|
||||
;;
|
||||
|
||||
*)
|
||||
echo
|
||||
echo $0: error: invalid module \"$nxt_module\".
|
||||
|
|
161
auto/modules/nodejs
Normal file
161
auto/modules/nodejs
Normal file
|
@ -0,0 +1,161 @@
|
|||
|
||||
# Copyright (C) NGINX, Inc.
|
||||
|
||||
|
||||
shift
|
||||
|
||||
for nxt_option; do
|
||||
|
||||
case "$nxt_option" in
|
||||
-*=*) value=`echo "$nxt_option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;;
|
||||
*) value="" ;;
|
||||
esac
|
||||
|
||||
case "$nxt_option" in
|
||||
--node=*) NXT_NODE="$value" ;;
|
||||
--npm=*) NXT_NPM="$value" ;;
|
||||
--node-gyp=*) NXT_NODE_GYP="$value" ;;
|
||||
|
||||
--help)
|
||||
cat << END
|
||||
|
||||
--node=NAME set node executable
|
||||
--npm=NAME set npm executable
|
||||
--node-gyp=NAME set node-gyp executable
|
||||
|
||||
END
|
||||
exit 0
|
||||
;;
|
||||
|
||||
*)
|
||||
echo
|
||||
echo $0: error: invalid Node option \"$nxt_option\"
|
||||
echo
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
done
|
||||
|
||||
|
||||
if [ ! -f $NXT_AUTOCONF_DATA ]; then
|
||||
echo
|
||||
echo Please run common $0 before configuring module \"$nxt_module\".
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. $NXT_AUTOCONF_DATA
|
||||
|
||||
|
||||
$echo "configuring nodejs module"
|
||||
$echo "configuring nodejs module..." >> $NXT_AUTOCONF_ERR
|
||||
|
||||
NXT_NODE=${NXT_NODE=node}
|
||||
NXT_NPM=${NXT_NPM=npm}
|
||||
NXT_NODE_GYP=${NXT_NODE_GYP=node-gyp}
|
||||
|
||||
$echo -n "checking for node ..."
|
||||
$echo "checking for node ..." >> $NXT_AUTOCONF_ERR
|
||||
|
||||
if /bin/sh -c "${NXT_NODE} -v" >> $NXT_AUTOCONF_ERR 2>&1; then
|
||||
$echo " found"
|
||||
|
||||
NXT_NODE_VERSION="`${NXT_NODE} -v`"
|
||||
$echo " + node version ${NXT_NODE_VERSION}"
|
||||
|
||||
else
|
||||
$echo " not found"
|
||||
$echo
|
||||
$echo $0: error: no Node found.
|
||||
$echo
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
$echo -n "checking for npm ..."
|
||||
$echo "checking for npm ..." >> $NXT_AUTOCONF_ERR
|
||||
|
||||
if /bin/sh -c "${NXT_NPM} -v" >> $NXT_AUTOCONF_ERR 2>&1; then
|
||||
$echo " found"
|
||||
|
||||
NXT_NPM_VERSION="`${NXT_NPM} -v`"
|
||||
$echo " + npm version ${NXT_NPM_VERSION}"
|
||||
|
||||
else
|
||||
$echo " not found"
|
||||
$echo
|
||||
$echo $0: error: no npm found.
|
||||
$echo
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
$echo -n "checking for node-gyp ..."
|
||||
$echo "checking for node-gyp ..." >> $NXT_AUTOCONF_ERR
|
||||
|
||||
if /bin/sh -c "${NXT_NODE_GYP} -v" >> $NXT_AUTOCONF_ERR 2>&1; then
|
||||
$echo " found"
|
||||
|
||||
NXT_NODE_GYP_VERSION="`${NXT_NODE_GYP} -v`"
|
||||
$echo " + node-gyp version ${NXT_NODE_GYP_VERSION}"
|
||||
|
||||
else
|
||||
$echo " not found"
|
||||
$echo
|
||||
$echo $0: error: no node-gyp found.
|
||||
$echo
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if grep ^$NXT_NODE: $NXT_MAKEFILE 2>&1 > /dev/null; then
|
||||
$echo
|
||||
$echo $0: error: duplicate \"$NXT_NODE\" package configured.
|
||||
$echo
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
NXT_NODE_TMP=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http
|
||||
NXT_NODE_TARBALL=${PWD}/${NXT_BUILD_DIR}/${NXT_NODE}-unit-http.tar.gz
|
||||
NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src && \
|
||||
export UNIT_LIB_STATIC_PATH=${PWD}/${NXT_BUILD_DIR}/libunit.a"
|
||||
|
||||
cat << END >> $NXT_MAKEFILE
|
||||
|
||||
.PHONY: ${NXT_NODE}
|
||||
.PHONY: ${NXT_NODE}-copy
|
||||
.PHONY: ${NXT_NODE}-install
|
||||
.PHONY: ${NXT_NODE}-uninstall
|
||||
|
||||
all:
|
||||
|
||||
${NXT_NODE}: ${NXT_NODE}-copy $NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
|
||||
${NXT_NODE_EXPORTS} && \\
|
||||
cd ${NXT_NODE_TMP} && ${NXT_NODE_GYP} configure build clean
|
||||
|
||||
${NXT_NODE}-copy:
|
||||
mkdir -p ${NXT_BUILD_DIR}/src/
|
||||
cp -rp src/nodejs/ ${NXT_BUILD_DIR}/src/${NXT_NODE}
|
||||
|
||||
${NXT_NODE_TARBALL}: ${NXT_NODE}-copy
|
||||
tar -zcvf ${NXT_NODE_TARBALL} -C ${NXT_NODE_TMP} .
|
||||
|
||||
${NXT_NODE}-install: ${NXT_NODE_TARBALL} \
|
||||
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
|
||||
${NXT_NODE_EXPORTS} && \\
|
||||
${NXT_NPM} install -g ${NXT_NODE_TARBALL} --unsafe-perm=true
|
||||
|
||||
${NXT_NODE}-local-install: ${NXT_NODE_TARBALL} \
|
||||
$NXT_BUILD_DIR/$NXT_LIB_UNIT_STATIC
|
||||
${NXT_NODE_EXPORTS} && \\
|
||||
mkdir -p \$(DESTDIR) && \\
|
||||
cd \$(DESTDIR) && ${NXT_NPM} install ${NXT_NODE_TARBALL}
|
||||
|
||||
${NXT_NODE}-build: ${NXT_NODE}
|
||||
|
||||
${NXT_NODE}-publish: ${NXT_NODE}
|
||||
cd ${NXT_NODE_TMP} && ${NXT_NPM} publish
|
||||
|
||||
${NXT_NODE}-uninstall:
|
||||
${NXT_NPM} uninstall -g unit-http
|
||||
|
||||
END
|
|
@ -26,6 +26,8 @@ echo=$NXT_BUILD_DIR/echo
|
|||
NXT_LIB_AUX_CFLAGS=
|
||||
NXT_LIB_AUX_LIBS=
|
||||
|
||||
NXT_LIB_UNIT_STATIC='$NXT_LIB_UNIT_STATIC'
|
||||
|
||||
NXT_MODULES='$NXT_MODULES'
|
||||
|
||||
END
|
||||
|
|
2
src/nodejs/unit-http/README.md
Normal file
2
src/nodejs/unit-http/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
|
15
src/nodejs/unit-http/addon.cpp
Normal file
15
src/nodejs/unit-http/addon.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
|
||||
napi_value
|
||||
Init(napi_env env, napi_value exports)
|
||||
{
|
||||
return Unit::init(env, exports);
|
||||
}
|
||||
|
||||
NAPI_MODULE(Unit, Init)
|
12
src/nodejs/unit-http/binding.gyp
Normal file
12
src/nodejs/unit-http/binding.gyp
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
'targets': [{
|
||||
'target_name': "unit-http",
|
||||
'sources': ["unit.cpp", "addon.cpp"],
|
||||
'include_dirs': [
|
||||
"<!(echo $UNIT_SRC_PATH)"
|
||||
],
|
||||
'libraries': [
|
||||
"<!(echo $UNIT_LIB_STATIC_PATH)"
|
||||
]
|
||||
}]
|
||||
}
|
7
src/nodejs/unit-http/binding_pub.gyp
Normal file
7
src/nodejs/unit-http/binding_pub.gyp
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
'targets': [{
|
||||
'target_name': "unit-http",
|
||||
'sources': ["unit.cpp", "addon.cpp"],
|
||||
'libraries': ["unit"]
|
||||
}]
|
||||
}
|
23
src/nodejs/unit-http/http.js
Executable file
23
src/nodejs/unit-http/http.js
Executable file
|
@ -0,0 +1,23 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const server = require('unit-http/http_server');
|
||||
|
||||
const { Server } = server;
|
||||
|
||||
function createServer (requestHandler) {
|
||||
return new Server(requestHandler);
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
Server,
|
||||
STATUS_CODES: server.STATUS_CODES,
|
||||
createServer,
|
||||
IncomingMessage: server.ServerRequest,
|
||||
ServerResponse: server.ServerResponse
|
||||
};
|
331
src/nodejs/unit-http/http_server.js
Executable file
331
src/nodejs/unit-http/http_server.js
Executable file
|
@ -0,0 +1,331 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const http = require('http');
|
||||
const util = require('util');
|
||||
const unit_lib = require('unit-http/build/Release/unit-http.node');
|
||||
const unit_socket = require('unit-http/socket');
|
||||
|
||||
const { Socket } = unit_socket;
|
||||
|
||||
|
||||
function ServerResponse(req) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.headers = {};
|
||||
}
|
||||
util.inherits(ServerResponse, EventEmitter);
|
||||
|
||||
ServerResponse.prototype.statusCode = 200;
|
||||
ServerResponse.prototype.statusMessage = undefined;
|
||||
ServerResponse.prototype.headers_len = 0;
|
||||
ServerResponse.prototype.headers_count = 0;
|
||||
ServerResponse.prototype.headersSent = false;
|
||||
ServerResponse.prototype.finished = false;
|
||||
|
||||
ServerResponse.prototype._finish = function _finish() {
|
||||
this.headers = {};
|
||||
this.headers_len = 0;
|
||||
this.headers_count = 0;
|
||||
this.finished = true;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.assignSocket = function assignSocket(socket) {
|
||||
};
|
||||
|
||||
ServerResponse.prototype.detachSocket = function detachSocket(socket) {
|
||||
};
|
||||
|
||||
ServerResponse.prototype.writeContinue = function writeContinue(cb) {
|
||||
};
|
||||
|
||||
ServerResponse.prototype.writeProcessing = function writeProcessing(cb) {
|
||||
};
|
||||
|
||||
ServerResponse.prototype.setHeader = function setHeader(key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new TypeError('Key argument must be a string');
|
||||
}
|
||||
|
||||
let header_key_len = Buffer.byteLength(key + "", 'latin1');
|
||||
let header_len = 0
|
||||
let header_count = 0;
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
header_count = value.length;
|
||||
|
||||
value.forEach(function(val) {
|
||||
if (typeof val !== 'string') {
|
||||
throw new TypeError('Entry in arrey should be a string');
|
||||
}
|
||||
|
||||
header_len += Buffer.byteLength(val + "", 'latin1');
|
||||
});
|
||||
|
||||
} else {
|
||||
if (typeof value !== 'string') {
|
||||
throw new TypeError('Value argument must be a string or array');
|
||||
}
|
||||
|
||||
header_count = 1;
|
||||
header_len = Buffer.byteLength(value + "", 'latin1');
|
||||
}
|
||||
|
||||
this.removeHeader(key);
|
||||
|
||||
this.headers[key] = value;
|
||||
this.headers_len += header_len + (header_key_len * header_count);
|
||||
this.headers_count += header_count;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.getHeader = function getHeader(name) {
|
||||
return this.headers[name];
|
||||
};
|
||||
|
||||
ServerResponse.prototype.getHeaderNames = function getHeaderNames() {
|
||||
return Object.keys(this.headers);
|
||||
};
|
||||
|
||||
ServerResponse.prototype.getHeaders = function getHeaders() {
|
||||
return this.headers;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.hasHeader = function hasHeader(name) {
|
||||
return name in this.headers;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.removeHeader = function removeHeader(name) {
|
||||
if (!(name in this.headers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let name_len = Buffer.byteLength(name + "", 'latin1');
|
||||
|
||||
if (Array.isArray(this.headers[name])) {
|
||||
this.headers_count -= this.headers[name].length;
|
||||
this.headers_len -= this.headers[name].length * name_len;
|
||||
|
||||
this.headers[name].forEach(function(val) {
|
||||
this.headers_len -= Buffer.byteLength(val + "", 'latin1');
|
||||
});
|
||||
|
||||
} else {
|
||||
this.headers_count--;
|
||||
this.headers_len -= name_len + Buffer.byteLength(this.headers[name] + "", 'latin1');
|
||||
}
|
||||
|
||||
delete this.headers[name];
|
||||
};
|
||||
|
||||
ServerResponse.prototype.sendDate = function sendDate() {
|
||||
throw new Error("Not supported");
|
||||
};
|
||||
|
||||
ServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) {
|
||||
this.timeout = msecs;
|
||||
|
||||
if (callback) {
|
||||
this.on('timeout', callback);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// for Express
|
||||
ServerResponse.prototype._implicitHeader = function _implicitHeader() {
|
||||
this.writeHead(this.statusCode);
|
||||
};
|
||||
|
||||
ServerResponse.prototype.writeHead = writeHead;
|
||||
ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead;
|
||||
|
||||
function writeHead(statusCode, reason, obj) {
|
||||
var originalStatusCode = statusCode;
|
||||
|
||||
statusCode |= 0;
|
||||
|
||||
if (statusCode < 100 || statusCode > 999) {
|
||||
throw new ERR_HTTP_INVALID_STATUS_CODE(originalStatusCode);
|
||||
}
|
||||
|
||||
if (typeof reason === 'string') {
|
||||
this.statusMessage = reason;
|
||||
|
||||
} else {
|
||||
if (!this.statusMessage) {
|
||||
this.statusMessage = http.STATUS_CODES[statusCode] || 'unknown';
|
||||
}
|
||||
|
||||
obj = reason;
|
||||
}
|
||||
|
||||
this.statusCode = statusCode;
|
||||
|
||||
if (obj) {
|
||||
var k;
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
k = keys[i];
|
||||
|
||||
if (k) {
|
||||
this.setHeader(k, obj[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unit_lib.unit_response_headers(this, statusCode, this.headers, this.headers_count, this.headers_len);
|
||||
|
||||
this.headersSent = true;
|
||||
};
|
||||
|
||||
ServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
|
||||
var contentLength = 0;
|
||||
|
||||
if (!this.headersSent) {
|
||||
this.writeHead(this.statusCode);
|
||||
}
|
||||
|
||||
if (this.finished) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (typeof chunk === 'function') {
|
||||
callback = chunk;
|
||||
chunk = null;
|
||||
|
||||
} else if (typeof encoding === 'function') {
|
||||
callback = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (chunk) {
|
||||
if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
|
||||
throw new TypeError('First argument must be a string or Buffer');
|
||||
}
|
||||
|
||||
if (typeof chunk === 'string') {
|
||||
contentLength = Buffer.byteLength(chunk, encoding);
|
||||
|
||||
} else {
|
||||
contentLength = chunk.length;
|
||||
}
|
||||
|
||||
unit_lib.unit_response_write(this, chunk, contentLength);
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(this);
|
||||
}
|
||||
};
|
||||
|
||||
ServerResponse.prototype.write = function write(chunk, encoding, callback) {
|
||||
this._writeBody(chunk, encoding, callback);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
ServerResponse.prototype.end = function end(chunk, encoding, callback) {
|
||||
this._writeBody(chunk, encoding, callback);
|
||||
unit_lib.unit_response_end(this)
|
||||
|
||||
this.finished = true;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
function ServerRequest(server) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.server = server;
|
||||
}
|
||||
util.inherits(ServerRequest, EventEmitter);
|
||||
|
||||
ServerRequest.prototype.unpipe = undefined;
|
||||
|
||||
ServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) {
|
||||
this.timeout = msecs;
|
||||
|
||||
if (callback) {
|
||||
this.on('timeout', callback);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
ServerRequest.prototype.statusCode = function statusCode() {
|
||||
/* Only valid for response obtained from http.ClientRequest. */
|
||||
};
|
||||
|
||||
ServerRequest.prototype.statusMessage = function statusMessage() {
|
||||
/* Only valid for response obtained from http.ClientRequest. */
|
||||
};
|
||||
|
||||
ServerRequest.prototype.trailers = function trailers() {
|
||||
throw new Error("Not supported");
|
||||
};
|
||||
|
||||
ServerRequest.prototype.METHODS = function METHODS() {
|
||||
return http.METHODS;
|
||||
};
|
||||
|
||||
ServerRequest.prototype.STATUS_CODES = function STATUS_CODES() {
|
||||
return http.STATUS_CODES;
|
||||
};
|
||||
|
||||
ServerRequest.prototype.listeners = function listeners() {
|
||||
return [];
|
||||
};
|
||||
|
||||
ServerRequest.prototype.resume = function resume() {
|
||||
return [];
|
||||
};
|
||||
|
||||
function Server(requestListener) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.unit = new unit_lib.Unit();
|
||||
this.unit.createServer();
|
||||
|
||||
this.unit.server = this;
|
||||
|
||||
this.socket = Socket;
|
||||
this.request = ServerRequest;
|
||||
this.response = ServerResponse;
|
||||
|
||||
if (requestListener) {
|
||||
this.on('request', requestListener);
|
||||
}
|
||||
}
|
||||
util.inherits(Server, EventEmitter);
|
||||
|
||||
Server.prototype.setTimeout = function setTimeout(msecs, callback) {
|
||||
this.timeout = msecs;
|
||||
|
||||
if (callback) {
|
||||
this.on('timeout', callback);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Server.prototype.listen = function () {
|
||||
this.unit.listen();
|
||||
};
|
||||
|
||||
function connectionListener(socket) {
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
STATUS_CODES: http.STATUS_CODES,
|
||||
Server,
|
||||
ServerResponse,
|
||||
ServerRequest,
|
||||
_connectionListener: connectionListener
|
||||
};
|
29
src/nodejs/unit-http/package.json
Normal file
29
src/nodejs/unit-http/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "unit-http",
|
||||
"version": "1.0.0",
|
||||
"description": "HTTP module for NGINX Unit",
|
||||
"main": "http.js",
|
||||
"files": [
|
||||
"addon.cpp",
|
||||
"binding.gyp",
|
||||
"http_server.js",
|
||||
"http.js",
|
||||
"package.json",
|
||||
"socket.js",
|
||||
"unit.cpp",
|
||||
"unit.h",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "node-gyp clean",
|
||||
"configure": "node-gyp configure",
|
||||
"build": "node-gyp build",
|
||||
"install": "node-gyp configure build"
|
||||
},
|
||||
"author": "Alexander Borisov",
|
||||
"license": "Apache 2.0",
|
||||
"gypfile": true,
|
||||
"dependencies": {
|
||||
"node-addon-api": "1.2.0"
|
||||
}
|
||||
}
|
99
src/nodejs/unit-http/socket.js
Executable file
99
src/nodejs/unit-http/socket.js
Executable file
|
@ -0,0 +1,99 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const util = require('util');
|
||||
const unit_lib = require('unit-http/build/Release/unit-http.node');
|
||||
|
||||
function Socket(options) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (typeof options === 'number') {
|
||||
options = { fd: options };
|
||||
|
||||
} else if (options === undefined) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.readable = options.readable !== false;
|
||||
this.writable = options.writable !== false;
|
||||
}
|
||||
util.inherits(Socket, EventEmitter);
|
||||
|
||||
Socket.prototype.bufferSize = 0;
|
||||
Socket.prototype.bytesRead = 0;
|
||||
Socket.prototype.bytesWritten = 0;
|
||||
Socket.prototype.connecting = false;
|
||||
Socket.prototype.destroyed = false;
|
||||
Socket.prototype.localAddress = "";
|
||||
Socket.prototype.localPort = 0;
|
||||
Socket.prototype.remoteAddress = "";
|
||||
Socket.prototype.remoteFamily = "";
|
||||
Socket.prototype.remotePort = 0;
|
||||
|
||||
Socket.prototype.address = function address() {
|
||||
};
|
||||
|
||||
Socket.prototype.connect = function connect(options, callback) {
|
||||
if (callback !== null) {
|
||||
this.once('connect', cb);
|
||||
}
|
||||
|
||||
this.connecting = true;
|
||||
this.writable = true;
|
||||
};
|
||||
|
||||
Socket.prototype.address = function address() {
|
||||
};
|
||||
|
||||
Socket.prototype.destroy = function destroy(exception) {
|
||||
this.connecting = false;
|
||||
this.readable = false;
|
||||
this.writable = false;
|
||||
};
|
||||
|
||||
Socket.prototype.end = function end(data, encoding) {
|
||||
};
|
||||
|
||||
Socket.prototype.pause = function pause() {
|
||||
};
|
||||
|
||||
Socket.prototype.ref = function ref() {
|
||||
};
|
||||
|
||||
Socket.prototype.resume = function resume() {
|
||||
};
|
||||
|
||||
Socket.prototype.setEncoding = function setEncoding(encoding) {
|
||||
};
|
||||
|
||||
Socket.prototype.setKeepAlive = function setKeepAlive(enable, initialDelay) {
|
||||
};
|
||||
|
||||
Socket.prototype.setNoDelay = function setNoDelay(noDelay) {
|
||||
};
|
||||
|
||||
Socket.prototype.setTimeout = function setTimeout(msecs, callback) {
|
||||
this.timeout = msecs;
|
||||
|
||||
if (callback) {
|
||||
this.on('timeout', callback);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Socket.prototype.unref = function unref() {
|
||||
};
|
||||
|
||||
Socket.prototype.write = function write(data, encoding, callback) {
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
Socket
|
||||
};
|
905
src/nodejs/unit-http/unit.cpp
Normal file
905
src/nodejs/unit-http/unit.cpp
Normal file
|
@ -0,0 +1,905 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include "unit.h"
|
||||
|
||||
|
||||
napi_ref Unit::constructor_;
|
||||
|
||||
|
||||
Unit::Unit(napi_env env):
|
||||
env_(env),
|
||||
wrapper_(nullptr),
|
||||
unit_ctx_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Unit::~Unit()
|
||||
{
|
||||
napi_delete_reference(env_, wrapper_);
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::init(napi_env env, napi_value exports)
|
||||
{
|
||||
napi_value cons, fn;
|
||||
napi_status status;
|
||||
|
||||
napi_property_descriptor properties[] = {
|
||||
{ "createServer", 0, create_server, 0, 0, 0, napi_default, 0 },
|
||||
{ "listen", 0, listen, 0, 0, 0, napi_default, 0 }
|
||||
};
|
||||
|
||||
status = napi_define_class(env, "Unit", NAPI_AUTO_LENGTH, create, nullptr,
|
||||
2, properties, &cons);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_create_reference(env, cons, 1, &constructor_);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env, exports, "Unit", cons);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_create_function(env, NULL, 0, response_send_headers, NULL,
|
||||
&fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env, exports,
|
||||
"unit_response_headers", fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_create_function(env, NULL, 0, response_write, NULL, &fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env, exports, "unit_response_write", fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_create_function(env, NULL, 0, response_end, NULL, &fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env, exports, "unit_response_end", fn);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return exports;
|
||||
|
||||
failed:
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to define Unit class");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Unit::destroy(napi_env env, void *nativeObject, void *finalize_hint)
|
||||
{
|
||||
Unit *obj = reinterpret_cast<Unit *>(nativeObject);
|
||||
|
||||
delete obj;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::create(napi_env env, napi_callback_info info)
|
||||
{
|
||||
Unit *obj;
|
||||
napi_value target, cons, instance, jsthis;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_new_target(env, info, &target);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (target != nullptr) {
|
||||
/* Invoked as constructor: `new Unit(...)` */
|
||||
status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis,
|
||||
nullptr);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
obj = new Unit(env);
|
||||
|
||||
status = napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
|
||||
destroy, nullptr, &obj->wrapper_);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return jsthis;
|
||||
}
|
||||
|
||||
/* Invoked as plain function `Unit(...)`, turn into construct call. */
|
||||
status = napi_get_reference_value(env, constructor_, &cons);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_new_instance(env, cons, 0, nullptr, &instance);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return instance;
|
||||
|
||||
failed:
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to create Unit object");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::create_server(napi_env env, napi_callback_info info)
|
||||
{
|
||||
Unit *obj;
|
||||
size_t argc;
|
||||
napi_value jsthis;
|
||||
napi_status status;
|
||||
napi_value argv[1];
|
||||
nxt_unit_init_t unit_init;
|
||||
|
||||
argc = 1;
|
||||
|
||||
status = napi_get_cb_info(env, info, &argc, argv, &jsthis, nullptr);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(&unit_init, 0, sizeof(nxt_unit_init_t));
|
||||
|
||||
unit_init.data = obj;
|
||||
unit_init.callbacks.request_handler = request_handler;
|
||||
|
||||
obj->unit_ctx_ = nxt_unit_init(&unit_init);
|
||||
if (obj->unit_ctx_ == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
failed:
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to create Unit object");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::listen(napi_env env, napi_callback_info info)
|
||||
{
|
||||
int ret;
|
||||
Unit *obj;
|
||||
napi_value jsthis;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_cb_info(env, info, nullptr, nullptr, &jsthis, nullptr);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_unwrap(env, jsthis, reinterpret_cast<void **>(&obj));
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (obj->unit_ctx_ == NULL) {
|
||||
napi_throw_error(env, NULL, "Unit context was not created");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = nxt_unit_run(obj->unit_ctx_);
|
||||
if (ret != NXT_UNIT_OK) {
|
||||
napi_throw_error(env, NULL, "Failed to run Unit");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nxt_unit_done(obj->unit_ctx_);
|
||||
|
||||
return nullptr;
|
||||
|
||||
failed:
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to listen Unit socket");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Unit::request_handler(nxt_unit_request_info_t *req)
|
||||
{
|
||||
Unit *obj;
|
||||
napi_value socket, request, response;
|
||||
napi_value global, server_obj;
|
||||
napi_value req_argv[3];
|
||||
napi_status status;
|
||||
|
||||
obj = reinterpret_cast<Unit *>(req->unit->data);
|
||||
|
||||
napi_handle_scope scope;
|
||||
status = napi_open_handle_scope(obj->env_, &scope);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to create handle scope");
|
||||
return;
|
||||
}
|
||||
|
||||
server_obj = obj->get_server_object();
|
||||
if (server_obj == nullptr) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to get server object");
|
||||
return;
|
||||
}
|
||||
|
||||
status = napi_get_global(obj->env_, &global);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to get global variable");
|
||||
return;
|
||||
}
|
||||
|
||||
socket = obj->create_socket(server_obj, req);
|
||||
if (socket == nullptr) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to create socket object");
|
||||
return;
|
||||
}
|
||||
|
||||
request = obj->create_request(server_obj, socket);
|
||||
if (request == nullptr) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to create request object");
|
||||
return;
|
||||
}
|
||||
|
||||
response = obj->create_response(server_obj, socket, request, req, obj);
|
||||
if (response == nullptr) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to create response object");
|
||||
return;
|
||||
}
|
||||
|
||||
req_argv[1] = request;
|
||||
req_argv[2] = response;
|
||||
|
||||
status = obj->create_headers(req, request);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(obj->env_, NULL, "Failed to create headers");
|
||||
return;
|
||||
}
|
||||
|
||||
obj->emit(server_obj, "request", sizeof("request") - 1, 3, req_argv);
|
||||
obj->emit_post_data(request, req);
|
||||
|
||||
napi_close_handle_scope(obj->env_, scope);
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::get_server_object()
|
||||
{
|
||||
napi_value unit_obj, server_obj;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_reference_value(env_, wrapper_, &unit_obj);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_named_property(env_, unit_obj, "server", &server_obj);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return server_obj;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::emit(napi_value obj, const char *name, size_t name_len, size_t argc,
|
||||
napi_value *argv)
|
||||
{
|
||||
napi_value emitter, return_val, str;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_named_property(env_, obj, "emit", &emitter);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_create_string_latin1(env_, name, name_len, &str);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (argc != 0) {
|
||||
argv[0] = str;
|
||||
|
||||
} else {
|
||||
argc = 1;
|
||||
argv = &str;
|
||||
}
|
||||
|
||||
status = napi_call_function(env_, obj, emitter, argc, argv, &return_val);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
napi_status
|
||||
Unit::create_headers(nxt_unit_request_info_t *req, napi_value request)
|
||||
{
|
||||
uint32_t i;
|
||||
const char *p;
|
||||
napi_value headers, raw_headers, str;
|
||||
napi_status status;
|
||||
nxt_unit_field_t *f;
|
||||
nxt_unit_request_t *r;
|
||||
|
||||
r = req->request;
|
||||
|
||||
status = napi_create_object(env_, &headers);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_create_array_with_length(env_, r->fields_count * 2,
|
||||
&raw_headers);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = 0; i < r->fields_count; i++) {
|
||||
f = r->fields + i;
|
||||
|
||||
status = this->append_header(f, headers, raw_headers, i);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, request, "headers", headers);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, request, "raw_headers", raw_headers);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
p = (const char *) nxt_unit_sptr_get(&r->version);
|
||||
|
||||
status = napi_create_string_latin1(env_, p, r->version_length, &str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, request, "httpVersion", str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
p = (const char *) nxt_unit_sptr_get(&r->method);
|
||||
|
||||
status = napi_create_string_latin1(env_, p, r->method_length, &str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, request, "method", str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
p = (const char *) nxt_unit_sptr_get(&r->target);
|
||||
|
||||
status = napi_create_string_latin1(env_, p, r->target_length, &str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, request, "url", str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
|
||||
inline napi_status
|
||||
Unit::append_header(nxt_unit_field_t *f, napi_value headers,
|
||||
napi_value raw_headers, uint32_t idx)
|
||||
{
|
||||
const char *name, *value;
|
||||
napi_value str, vstr;
|
||||
napi_status status;
|
||||
|
||||
value = (const char *) nxt_unit_sptr_get(&f->value);
|
||||
|
||||
status = napi_create_string_latin1(env_, value, f->value_length, &vstr);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
name = (const char *) nxt_unit_sptr_get(&f->name);
|
||||
|
||||
status = napi_set_named_property(env_, headers, name, vstr);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_create_string_latin1(env_, name, f->name_length, &str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_element(env_, raw_headers, idx * 2, str);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = napi_set_element(env_, raw_headers, idx * 2 + 1, vstr);
|
||||
if (status != napi_ok) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::create_socket(napi_value server_obj, nxt_unit_request_info_t *req)
|
||||
{
|
||||
napi_value constructor, return_val;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_named_property(env_, server_obj, "socket",
|
||||
&constructor);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_new_instance(env_, constructor, 0, NULL, &return_val);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::create_request(napi_value server_obj, napi_value socket)
|
||||
{
|
||||
napi_value constructor, return_val;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_named_property(env_, server_obj, "request",
|
||||
&constructor);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_new_instance(env_, constructor, 1, &server_obj,
|
||||
&return_val);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, return_val, "socket", socket);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::create_response(napi_value server_obj, napi_value socket,
|
||||
napi_value request, nxt_unit_request_info_t *req,
|
||||
Unit *obj)
|
||||
{
|
||||
napi_value constructor, return_val, req_num;
|
||||
napi_status status;
|
||||
|
||||
status = napi_get_named_property(env_, server_obj, "response",
|
||||
&constructor);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_new_instance(env_, constructor, 1, &request, &return_val);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, return_val, "socket", socket);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_create_int64(env_, (int64_t) (uintptr_t) req, &req_num);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_set_named_property(env_, return_val, "_req_point", req_num);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return return_val;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Unit::emit_post_data(napi_value request, nxt_unit_request_info_t *req)
|
||||
{
|
||||
void *data;
|
||||
napi_value req_argv[2];
|
||||
napi_status status;
|
||||
|
||||
status = napi_create_buffer(env_, (size_t) req->content_length,
|
||||
&data, &req_argv[1]);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env_, NULL, "Failed to create request buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
nxt_unit_request_read(req, data, req->content_length);
|
||||
|
||||
emit(request, "data", sizeof("data") - 1, 2, req_argv);
|
||||
emit(request, "end", sizeof("end") - 1, 0, nullptr);
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::response_send_headers(napi_env env, napi_callback_info info)
|
||||
{
|
||||
int ret;
|
||||
char *ptr, *name_ptr;
|
||||
bool is_array;
|
||||
size_t argc, name_len, value_len;
|
||||
int64_t req_p;
|
||||
uint32_t status_code, header_len, keys_len, array_len;
|
||||
uint32_t keys_count, i, j;
|
||||
uint16_t hash;
|
||||
napi_value this_arg, headers, keys, name, value, array_val;
|
||||
napi_value req_num;
|
||||
napi_status status;
|
||||
nxt_unit_field_t *f;
|
||||
nxt_unit_request_info_t *req;
|
||||
napi_value argv[5];
|
||||
|
||||
argc = 5;
|
||||
|
||||
status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
|
||||
if (status != napi_ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (argc != 5) {
|
||||
napi_throw_error(env, NULL, "Wrong args count. Need three: "
|
||||
"statusCode, headers, headers count, headers length");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_value_int64(env, req_num, &req_p);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
|
||||
|
||||
status = napi_get_value_uint32(env, argv[1], &status_code);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_value_uint32(env, argv[3], &keys_count);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_value_uint32(env, argv[4], &header_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Need to reserve extra byte for C-string 0-termination. */
|
||||
header_len++;
|
||||
|
||||
headers = argv[2];
|
||||
|
||||
ret = nxt_unit_response_init(req, status_code, keys_count, header_len);
|
||||
if (ret != NXT_UNIT_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_property_names(env, headers, &keys);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_array_length(env, keys, &keys_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ptr = req->response_buf->free;
|
||||
|
||||
for (i = 0; i < keys_len; i++) {
|
||||
status = napi_get_element(env, keys, i, &name);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_property(env, headers, name, &value);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_value_string_latin1(env, name, ptr, header_len,
|
||||
&name_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
name_ptr = ptr;
|
||||
|
||||
ptr += name_len;
|
||||
header_len -= name_len;
|
||||
|
||||
hash = nxt_unit_field_hash(name_ptr, name_len);
|
||||
|
||||
status = napi_is_array(env, value, &is_array);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (is_array) {
|
||||
status = napi_get_array_length(env, value, &array_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (j = 0; j < array_len; j++) {
|
||||
status = napi_get_element(env, value, j, &array_val);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_get_value_string_latin1(env, array_val, ptr,
|
||||
header_len,
|
||||
&value_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
f = req->response->fields + req->response->fields_count;
|
||||
f->skip = 0;
|
||||
|
||||
nxt_unit_sptr_set(&f->name, name_ptr);
|
||||
|
||||
f->name_length = name_len;
|
||||
f->hash = hash;
|
||||
|
||||
nxt_unit_sptr_set(&f->value, ptr);
|
||||
f->value_length = (uint32_t) value_len;
|
||||
|
||||
ptr += value_len;
|
||||
header_len -= value_len;
|
||||
|
||||
req->response->fields_count++;
|
||||
}
|
||||
|
||||
} else {
|
||||
status = napi_get_value_string_latin1(env, value, ptr, header_len,
|
||||
&value_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
f = req->response->fields + req->response->fields_count;
|
||||
f->skip = 0;
|
||||
|
||||
nxt_unit_sptr_set(&f->name, name_ptr);
|
||||
|
||||
f->name_length = name_len;
|
||||
f->hash = hash;
|
||||
|
||||
nxt_unit_sptr_set(&f->value, ptr);
|
||||
f->value_length = (uint32_t) value_len;
|
||||
|
||||
ptr += value_len;
|
||||
header_len -= value_len;
|
||||
|
||||
req->response->fields_count++;
|
||||
}
|
||||
}
|
||||
|
||||
req->response_buf->free = ptr;
|
||||
|
||||
ret = nxt_unit_response_send(req);
|
||||
if (ret != NXT_UNIT_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return this_arg;
|
||||
|
||||
failed:
|
||||
|
||||
req->response->fields_count = 0;
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to write headers");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::response_write(napi_env env, napi_callback_info info)
|
||||
{
|
||||
int ret;
|
||||
char *ptr;
|
||||
size_t argc, have_buf_len;
|
||||
int64_t req_p;
|
||||
uint32_t buf_len;
|
||||
napi_value this_arg, req_num;
|
||||
napi_status status;
|
||||
nxt_unit_buf_t *buf;
|
||||
napi_valuetype buf_type;
|
||||
nxt_unit_request_info_t *req;
|
||||
napi_value argv[3];
|
||||
|
||||
argc = 3;
|
||||
|
||||
status = napi_get_cb_info(env, info, &argc, argv, &this_arg, NULL);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (argc != 3) {
|
||||
napi_throw_error(env, NULL, "Wrong args count. Need two: "
|
||||
"chunk, chunk length");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_named_property(env, argv[0], "_req_point", &req_num);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_value_int64(env, req_num, &req_p);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
|
||||
|
||||
status = napi_get_value_uint32(env, argv[2], &buf_len);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = napi_typeof(env, argv[1], &buf_type);
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
buf_len++;
|
||||
|
||||
buf = nxt_unit_response_buf_alloc(req, buf_len);
|
||||
if (buf == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (buf_type == napi_string) {
|
||||
/* TODO: will work only for utf8 content-type */
|
||||
|
||||
status = napi_get_value_string_utf8(env, argv[1], buf->free,
|
||||
buf_len, &have_buf_len);
|
||||
|
||||
} else {
|
||||
status = napi_get_buffer_info(env, argv[1], (void **) &ptr,
|
||||
&have_buf_len);
|
||||
|
||||
memcpy(buf->free, ptr, have_buf_len);
|
||||
}
|
||||
|
||||
if (status != napi_ok) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
buf->free += have_buf_len;
|
||||
|
||||
ret = nxt_unit_buf_send(buf);
|
||||
if (ret != NXT_UNIT_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return this_arg;
|
||||
|
||||
failed:
|
||||
|
||||
napi_throw_error(env, NULL, "Failed to write body");
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
napi_value
|
||||
Unit::response_end(napi_env env, napi_callback_info info)
|
||||
{
|
||||
size_t argc;
|
||||
int64_t req_p;
|
||||
napi_value resp, this_arg, req_num;
|
||||
napi_status status;
|
||||
nxt_unit_request_info_t *req;
|
||||
|
||||
argc = 1;
|
||||
|
||||
status = napi_get_cb_info(env, info, &argc, &resp, &this_arg, NULL);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to finalize sending body");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_named_property(env, resp, "_req_point", &req_num);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
status = napi_get_value_int64(env, req_num, &req_p);
|
||||
if (status != napi_ok) {
|
||||
napi_throw_error(env, NULL, "Failed to get request pointer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
req = (nxt_unit_request_info_t *) (uintptr_t) req_p;
|
||||
|
||||
nxt_unit_request_done(req, NXT_UNIT_OK);
|
||||
|
||||
return this_arg;
|
||||
}
|
77
src/nodejs/unit-http/unit.h
Normal file
77
src/nodejs/unit-http/unit.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
|
||||
/*
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _NXT_NODEJS_UNIT_H_INCLUDED_
|
||||
#define _NXT_NODEJS_UNIT_H_INCLUDED_
|
||||
|
||||
|
||||
#include <node_api.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <nxt_unit.h>
|
||||
#include <nxt_unit_response.h>
|
||||
#include <nxt_unit_request.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
|
||||
class Unit {
|
||||
public:
|
||||
static napi_value init(napi_env env, napi_value exports);
|
||||
|
||||
private:
|
||||
Unit(napi_env env);
|
||||
~Unit();
|
||||
|
||||
static napi_value create(napi_env env, napi_callback_info info);
|
||||
static void destroy(napi_env env, void *nativeObject, void *finalize_hint);
|
||||
|
||||
static napi_value create_server(napi_env env, napi_callback_info info);
|
||||
static napi_value listen(napi_env env, napi_callback_info info);
|
||||
static void request_handler(nxt_unit_request_info_t *req);
|
||||
|
||||
napi_value get_server_object();
|
||||
|
||||
napi_value emit(napi_value obj, const char *name, size_t name_len,
|
||||
size_t argc, napi_value *argv);
|
||||
|
||||
napi_value create_socket(napi_value server_obj,
|
||||
nxt_unit_request_info_t *req);
|
||||
|
||||
napi_value create_request(napi_value server_obj, napi_value socket);
|
||||
|
||||
napi_value create_response(napi_value server_obj, napi_value socket,
|
||||
napi_value request,
|
||||
nxt_unit_request_info_t *req, Unit *obj);
|
||||
|
||||
void emit_post_data(napi_value request, nxt_unit_request_info_t *req);
|
||||
|
||||
static napi_value response_send_headers(napi_env env,
|
||||
napi_callback_info info);
|
||||
|
||||
static napi_value response_write(napi_env env, napi_callback_info info);
|
||||
static napi_value response_end(napi_env env, napi_callback_info info);
|
||||
|
||||
napi_status create_headers(nxt_unit_request_info_t *req,
|
||||
napi_value request);
|
||||
|
||||
inline napi_status append_header(nxt_unit_field_t *f, napi_value headers,
|
||||
napi_value raw_headers, uint32_t idx);
|
||||
|
||||
static napi_ref constructor_;
|
||||
|
||||
napi_env env_;
|
||||
napi_ref wrapper_;
|
||||
nxt_unit_ctx_t *unit_ctx_;
|
||||
};
|
||||
|
||||
|
||||
#endif /* _NXT_NODEJS_H_INCLUDED_ */
|
Loading…
Reference in a new issue