Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved support for Access-Control-Allow-Origin header. #122

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ Note: The **=** sign is important here. Replacing the equal sign with a space wi

* **firewall**: An object (map) of type { allow: [ list... ], deny: [ list... ] }, where [ list... ] means an array of strings or regular expressions which are tested against the domain connected to. ONLY One of the 2 (deny or allow) shall be used depending on which array has values. The one that is non-empty shall be used. If both are empty (default), all connections are allowed. If both are non-empty, then the ALLOW list is used and ONLY connections to the domains listed in ALLOW are connected to **(default: { allow: [ ], deny: [ ] })**

* **echo_origin_in_cors_header**: Set to `true` if you want to use the value of the `Origin` request header instead of `*` in the `Access-Control-Allow-Origin` response header.

* **route_filter**: If the route attribute is set, allow connections ONLY if the route attribute matches the regex below **(default: /.\*/)**

* **pidgin_compatible**: Set to 'true' if you want to be able to use pidgin (any any other libpurple based client) with node-xmpp-bosh. If you set this to 'true', then you lose the ability to create multiple streams on a session **(default: false)**
Expand Down
5 changes: 5 additions & 0 deletions bosh.conf.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ exports.config = {
// The maximum number of active streams allowed per BOSH session
max_streams_per_session: 8,

// Headers applied to every http response
http_headers: { },

//
Expand Down Expand Up @@ -66,6 +67,10 @@ exports.config = {
deny: [ /* 'gmail.com' */ ]
},

// Set to 'true' if you want to use the value of the Origin request header
// instead of '*' in the 'Access-Control-Allow-Origin' response header.
echo_origin_in_cors_header: false,

// If the route attribute is set, allow connections ONLY if the
// route attribute matches the regex below. This can be used in
// conjunction with 'firewall' to disallow connections if an IP
Expand Down
75 changes: 75 additions & 0 deletions src/bosh-headers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use strict";

var dutil = require('./dutil.js');
var path = require('path');

var filename = path.basename(path.normalize(__filename));
var log = require('./log.js').getLogger(filename);

function add_to_headers(dest, src) {
var acah = dest['Access-Control-Allow-Headers'].split(', ');
var k;
for (k in src) {
if (src.hasOwnProperty(k)) {
dest[k] = src[k];
acah.push(k);
}
}
dest['Access-Control-Allow-Headers'] = acah.join(', ');
}

function BOSHHeaders(options) {
var _options = options;

var _echo_origin_in_cors_header = _options.echo_origin_in_cors_header || false;
log.debug('ECHO_ORIGIN_IN_CORS_HEADER: %s', _echo_origin_in_cors_header);

var _default_headers = {};
_default_headers['GET'] = {
'Content-Type': 'text/html; charset=UTF-8',
'Cache-Control': 'no-cache, no-store',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Max-Age': '14400'
};
_default_headers['POST'] = {
'Content-Type': 'text/xml; charset=UTF-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Max-Age': '14400'
};
_default_headers['OPTIONS'] = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Max-Age': '14400'
};

if (_options.http_headers) {
add_to_headers(_default_headers['GET'], _options.http_headers);
add_to_headers(_default_headers['POST'], _options.http_headers);
add_to_headers(_default_headers['OPTIONS'], _options.http_headers);
}

['GET', 'POST', 'OPTIONS'].forEach(function(method) {
var headers = _default_headers[method];
Object.keys(headers).forEach(function(header_key) {
log.debug('HTTP_RESPONSE_HEADERS:%s::%s => %s', method, header_key, headers[header_key]);
})
});

this.make_headers = function(http_method, request_headers) {
var _headers = {};
dutil.copy(_headers, _default_headers[http_method]);

if (_echo_origin_in_cors_header && request_headers['origin']) {
_headers['Access-Control-Allow-Origin'] = request_headers['origin'];
}

return _headers;
}
}

exports.BOSHHeaders = BOSHHeaders;
3 changes: 2 additions & 1 deletion src/bosh.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var log = require('./log.js').getLogger(filename);
// * max_inactivity
// * http_socket_keepalive
// * http_headers
// * echo_origin_in_cors_header
//


Expand Down Expand Up @@ -230,7 +231,7 @@ exports.createServer = function (options) {
//Called when the 'end' event for the request is fired by the HTTP request handler
function bosh_request_handler(res, node) {
if (!node) {
res.writeHead(200, bosh_options.HTTP_POST_RESPONSE_HEADERS);
res.writeHead(200, bosh_options.http_post_response_headers(res.request_headers));
res.end(helper.$terminate({ condition: 'bad-request' }).toString());
return;
}
Expand Down
13 changes: 0 additions & 13 deletions src/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,6 @@ function $terminate(attrs) {


// Begin HTTP header helpers
function add_to_headers(dest, src) {
var acah = dest['Access-Control-Allow-Headers'].split(', ');
var k;
for (k in src) {
if (src.hasOwnProperty(k)) {
dest[k] = src[k];
acah.push(k);
}
}
dest['Access-Control-Allow-Headers'] = acah.join(', ');
}

function JSONPResponseProxy(req, res) {
this.req_ = req;
this.res_ = res;
Expand Down Expand Up @@ -213,7 +201,6 @@ function is_session_creation_packet(node) {

// End misc. helpers

exports.add_to_headers = add_to_headers;
exports.JSONPResponseProxy = JSONPResponseProxy;
exports.route_parse = route_parse;
exports.save_terminate_condition_for_wait_time = save_terminate_condition_for_wait_time;
Expand Down
13 changes: 5 additions & 8 deletions src/http-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ function HTTPServer(port, host, stat_func, system_info_func,

function handle_options(req, res, u) {
if (req.method === 'OPTIONS') {
res.writeHead(200, bosh_options.HTTP_OPTIONS_RESPONSE_HEADERS);
res.writeHead(200, bosh_options.http_options_response_headers(req.headers));
res.end();
return false;
}
Expand All @@ -206,8 +206,7 @@ function HTTPServer(port, host, stat_func, system_info_func,
function handle_get_statistics(req, res, u) {
var ppos = u.pathname.search(bosh_options.path);
if (req.method === 'GET' && ppos !== -1 && !u.query.hasOwnProperty('data')) {
var _headers = { };
dutil.copy(_headers, bosh_options.HTTP_GET_RESPONSE_HEADERS);
var _headers = bosh_options.http_get_response_headers(req.headers)
_headers['Content-Type'] = 'text/html; charset=utf-8';

res.writeHead(200, _headers);
Expand All @@ -227,8 +226,7 @@ function HTTPServer(port, host, stat_func, system_info_func,
var spos = path.basename(u.pathname).search("sysinfo");

if (req.method === 'GET' && ppos !== -1 && spos === 0) {
var _headers = { };
dutil.copy(_headers, bosh_options.HTTP_GET_RESPONSE_HEADERS);
var _headers = bosh_options.http_get_response_headers(req.headers);
_headers['Content-Type'] = 'text/html; charset=utf-8';

if (bosh_options.SYSTEM_INFO_PASSWORD.length === 0) {
Expand Down Expand Up @@ -268,7 +266,7 @@ function HTTPServer(port, host, stat_func, system_info_func,
//
function handle_get_crossdomainXML(req, res, u) {
if (req.method === 'GET' && req.url === "/crossdomain.xml") {
res.writeHead(200, bosh_options.HTTP_GET_RESPONSE_HEADERS);
res.writeHead(200, bosh_options.http_get_response_headers(req.headers));
var crossdomain = '<?xml version="1.0"?>';
crossdomain += '<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">';
crossdomain += '<cross-domain-policy>';
Expand All @@ -283,8 +281,7 @@ function HTTPServer(port, host, stat_func, system_info_func,

function handle_unhandled_request(req, res, u) {
log.trace("Invalid request, method: %s path: %s", req.method, u.pathname);
var _headers = { };
dutil.copy(_headers, bosh_options.HTTP_POST_RESPONSE_HEADERS);
var _headers = bosh_options.http_post_response_headers(req.headers);
_headers['Content-Type'] = 'text/plain; charset=utf-8';
res.writeHead(404, _headers);
res.end();
Expand Down
53 changes: 9 additions & 44 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,59 +27,19 @@

var helper = require('./helper.js');
var path = require('path');
var bheaders = require('./bosh-headers.js');

var filename = path.basename(path.normalize(__filename));
var log = require('./log.js').getLogger(filename);

function BOSH_Options(opts) {
var _opts = opts;

log.debug("Node.js version: %s", process.version);

this.HTTP_GET_RESPONSE_HEADERS = {
'Content-Type': 'text/html; charset=UTF-8',
'Cache-Control': 'no-cache, no-store',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'OPTIONS, GET, POST',
'Access-Control-Max-Age': '14400'
};

this.HTTP_POST_RESPONSE_HEADERS = {
'Content-Type': 'text/xml; charset=UTF-8',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'OPTIONS, GET, POST',
'Access-Control-Max-Age': '14400'
};

this.HTTP_OPTIONS_RESPONSE_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type, x-requested-with, Set-Cookie',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Max-Age': '14400'
};

if (_opts.http_headers) {
helper.add_to_headers(this.HTTP_GET_RESPONSE_HEADERS, _opts.http_headers);
helper.add_to_headers(this.HTTP_POST_RESPONSE_HEADERS, _opts.http_headers);
helper.add_to_headers(this.HTTP_OPTIONS_RESPONSE_HEADERS, _opts.http_headers);
}

(function debug_print_HTTP_headers(header_types) {
header_types.forEach(function(header_type) {
var hobj = this[header_type];
Object.keys(hobj).forEach(function(header_key) {
log.debug("%s::%s => %s", header_type, header_key, hobj[header_key]);
});
}.bind(this));
}.bind(this))(['HTTP_GET_RESPONSE_HEADERS',
'HTTP_POST_RESPONSE_HEADERS',
'HTTP_OPTIONS_RESPONSE_HEADERS']
);
log.debug("Node.js version: %s", process.version);

this.path = _opts.path;

log.debug("path: %s", this.path);
log.debug("path: %s", this.path);

// The maximum number of bytes that the BOSH server will
// "hold" from the client.
Expand Down Expand Up @@ -116,6 +76,11 @@ function BOSH_Options(opts) {
log.debug("MAX_STREAMS_PER_SESSION: %s", this.MAX_STREAMS_PER_SESSION);
log.debug("PIDGIN_COMPATIBLE: %s", this.PIDGIN_COMPATIBLE);
log.debug("SYSTEM_INFO_PASSWORD: %s", (this.SYSTEM_INFO_PASSWORD ? "[SET]" : "[NOT SET]"));

var bosh_headers = new bheaders.BOSHHeaders(_opts);
this.http_get_response_headers = bosh_headers.make_headers.bind(undefined, 'GET');
this.http_post_response_headers = bosh_headers.make_headers.bind(undefined, 'POST');
this.http_options_response_headers = bosh_headers.make_headers.bind(undefined, 'OPTIONS');
}

exports.BOSH_Options = BOSH_Options;
4 changes: 2 additions & 2 deletions src/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Response.prototype = {
}
// According to the spec. we need to send a Content-Length header
this._res.setHeader("Content-Length", Buffer.byteLength(msg, 'utf8'));
this._res.writeHead(200, this._options.HTTP_POST_RESPONSE_HEADERS);
this._res.writeHead(200, this._options.http_post_response_headers(this._res.request_headers));
this._res.end(msg);
log.debug("%s SENT(%s): %s", this._sid, this.rid, dutil.replace_promise(dutil.trim_promise(msg), '\n', ' '));
},
Expand All @@ -105,4 +105,4 @@ Response.prototype = {
}
};

exports.Response = Response;
exports.Response = Response;