diff --git a/src/dapi/DataApiManager.js b/src/dapi/DataApiManager.js
index 0ebc862..f67960c 100644
--- a/src/dapi/DataApiManager.js
+++ b/src/dapi/DataApiManager.js
@@ -98,9 +98,10 @@ module.exports = Class( 'DataApiManager' )
'private _apis': {},
- __construct: function( api_factory )
+ __construct: function( api_factory, apis )
{
this._dataApiFactory = api_factory;
+ this.setApis( apis || {} );
},
diff --git a/src/dapi/http/HttpImpl.js b/src/dapi/http/HttpImpl.js
index a9bee06..62d0cf2 100644
--- a/src/dapi/http/HttpImpl.js
+++ b/src/dapi/http/HttpImpl.js
@@ -47,5 +47,9 @@ module.exports = Interface( 'HttpImpl',
*
* @return {HttpImpl} self
*/
- 'public requestData': [ 'url', 'method', 'data', 'callback' ]
+ 'public requestData': [ 'url', 'method', 'data', 'callback' ],
+
+ // TODO: temporary to work around class extension bug; see
+ // SpoofedNodeHttpImpl
+ 'public setOptions': [],
} );
diff --git a/src/dapi/http/NodeHttpImpl.js b/src/dapi/http/NodeHttpImpl.js
index 541b332..35e687d 100644
--- a/src/dapi/http/NodeHttpImpl.js
+++ b/src/dapi/http/NodeHttpImpl.js
@@ -98,7 +98,7 @@ module.exports = Class( 'NodeHttpImpl' )
throw Error( `No handler for ${protocol}` );
}
- this._setOptions( options, method, data );
+ this.setOptions( options, method, data );
let forbid_end = false;
@@ -152,13 +152,16 @@ module.exports = Class( 'NodeHttpImpl' )
/**
* Set request options
*
+ * TODO: public to work around a class extension trait bug; make
+ * protected once fixed
+ *
* @param {Object} options request options
* @param {string} method HTTP method
* @param {string} data request data
*
* @return {Object} request headers
*/
- 'private _setOptions'( options, method, data )
+ 'virtual public setOptions'( options, method, data )
{
const { headers = {} } = options;
diff --git a/src/dapi/http/SpoofedNodeHttpImpl.js b/src/dapi/http/SpoofedNodeHttpImpl.js
new file mode 100644
index 0000000..ac68812
--- /dev/null
+++ b/src/dapi/http/SpoofedNodeHttpImpl.js
@@ -0,0 +1,78 @@
+/**
+ * Node-based HTTP client with session spoofing
+ *
+ * Copyright (C) 2017 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+'use strict';
+
+const { Trait } = require( 'easejs' );
+const HttpImpl = require( './HttpImpl' );
+
+
+/**
+ * Spoof user session during request
+ *
+ * TODO: Implementing HttpImpl instead of overriding NodeHttpImpl to work
+ * around a class extension bug; change once fixed.
+ */
+module.exports = Trait( 'SpoofedNodeHttpImpl' )
+ .implement( HttpImpl )
+ .extend(
+{
+ /**
+ * Session to spoof
+ * @type {UserSession}
+ */
+ 'private _request': null,
+
+
+ /**
+ * Use session for spoofing requests
+ *
+ * @param {UserSession} session session to spoof
+ */
+ __mixin( session )
+ {
+ this._request = session;
+ },
+
+
+ /**
+ * Set request options to spoof session
+ *
+ * @param {Object} options request options
+ * @param {string} method HTTP method
+ * @param {string} data request data
+ *
+ * @return {Object} request headers
+ */
+ 'virtual abstract override public setOptions'( options, method, data )
+ {
+ const cookie = this._request.getSessionIdName() + '=' +
+ this._request.getSessionId();
+
+ options.headers = {
+ 'User-Agent': this._request.getUserAgent(),
+ 'X-Forwarded-For': this._request.getRemoteAddr(),
+ 'Cookie': cookie,
+ };
+
+ return this.__super( options, method, data );
+ }
+} );
diff --git a/src/dapi/http/XhrHttpImpl.js b/src/dapi/http/XhrHttpImpl.js
index 20e0f19..28eccac 100644
--- a/src/dapi/http/XhrHttpImpl.js
+++ b/src/dapi/http/XhrHttpImpl.js
@@ -279,5 +279,11 @@ module.exports = Class( 'XhrHttpImpl' )
e.status = req.status;
callback( e, req.responseText );
+ },
+
+
+ 'public setOptions'()
+ {
+ // TOOD: remove (see HttpImpl)
}
} );
diff --git a/test/dapi/http/HttpDataApiTest.js b/test/dapi/http/HttpDataApiTest.js
index 12e8d82..e402ae4 100644
--- a/test/dapi/http/HttpDataApiTest.js
+++ b/test/dapi/http/HttpDataApiTest.js
@@ -27,7 +27,11 @@ var dapi = require( '../../../' ).dapi,
dummy_url = 'http://foo',
dummy_impl = Class
.implement( dapi.http.HttpImpl )
- .extend( { requestData: function( _, __, ___, ____ ) {} } )(),
+ .extend(
+ {
+ requestData: function( _, __, ___, ____ ) {},
+ setOptions() {},
+ } )(),
dummy_sut = Sut( dummy_url, 'GET', dummy_impl );
@@ -86,7 +90,9 @@ describe( 'HttpDataApi', function()
{
this.provided = arguments;
c( this.err, this.data );
- }
+ },
+
+ setOptions() {},
} )();
diff --git a/test/dapi/http/SpoofedNodeHttpImplTest.js b/test/dapi/http/SpoofedNodeHttpImplTest.js
new file mode 100644
index 0000000..adc8039
--- /dev/null
+++ b/test/dapi/http/SpoofedNodeHttpImplTest.js
@@ -0,0 +1,84 @@
+/**
+ * Tests Node-based HTTP client with session spoofing
+ *
+ * Copyright (C) 2017 R-T Specialty, LLC.
+ *
+ * This file is part of the Liza Data Collection Framework.
+ *
+ * liza is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+'use strict';
+
+const { expect } = require( 'chai' );
+const {
+ SpoofedNodeHttpImpl: Sut,
+ NodeHttpImpl,
+} = require( '../../../' ).dapi.http;
+
+
+describe( 'SpoofNodeHttpImpl', () =>
+{
+ it( "adds session headers", done =>
+ {
+ const user_agent = 'Agent Foo';
+ const forward_for = '::1';
+ const sessname = 'FOOSESSID';
+ const sessid = '12345';
+
+ const protos = {
+ http: {
+ request( given )
+ {
+ expect( given.headers[ 'User-Agent' ] )
+ .to.equal( user_agent );
+ expect( given.headers[ 'X-Forwarded-For' ] )
+ .to.equal( forward_for );
+
+ expect( given.headers.Cookie )
+ .to.contain( sessname + '=' + sessid );
+
+ done();
+ },
+ },
+ };
+
+ const url = {
+ parse: () => ( {
+ protocol: 'http',
+ } )
+ };
+
+ const session = getStubSession( {
+ agent: user_agent,
+ forward_for: forward_for,
+ sessname: sessname,
+ sessid: sessid,
+ } );
+
+ const given = NodeHttpImpl.use( Sut( session ) )( protos, url )
+ .requestData( '', '', {}, ()=>{} );
+ } );
+} );
+
+
+function getStubSession( { agent, forward_for, sessname, sessid } )
+{
+ return {
+ getUserAgent: () => agent,
+ getRemoteAddr: () => forward_for,
+ getSessionIdName: () => sessname,
+ getSessionId: () => sessid,
+ };
+}