Skip to content

Commit

Permalink
pass down HTTP agent
Browse files Browse the repository at this point in the history
  • Loading branch information
BertKleewein committed Apr 4, 2018
1 parent 92300ac commit 4dda10b
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 8 deletions.
2 changes: 2 additions & 0 deletions common/transport/http/devdoc/http_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ The buildRequest method receives all the information necessary to make an HTTP r

**SRS_NODE_HTTP_16_001: [** If `x509Options` is specified, the certificate, key and passphrase in the structure shall be used to authenticate the connection. **]**

**SRS_NODE_HTTP_18_001: [** If the `options` object passed into `setOptions` has a value in `http.agent`, that value shall be passed into the `request` function as `httpOptions.agent` **]**

### toMessage(response, body)
The `toMessage` function converts an HTTP response to an IoT Hub Message.

Expand Down
4 changes: 4 additions & 0 deletions common/transport/http/devdoc/rest_api_client_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,7 @@ The `translateError` method translates HTTP errors into Azure IoT Hub errors, ef
**SRS_NODE_IOTHUB_REST_API_CLIENT_16_034: [** The `updateSharedAccessSignature` method shall throw a `ReferenceError` if the `sharedAccessSignature` argument is falsy. **]**

**SRS_NODE_IOTHUB_REST_API_CLIENT_16_028: [** The `updateSharedAccessSignature` method shall update the `sharedAccessSignature` configuration parameter that is used in the `Authorization` header of all HTTP requests. **]**

### setOptions(options: any, callback?: (err?: Error) => void): void;

**SRS_NODE_IOTHUB_REST_API_CLIENT_18_003: [** `setOptions` shall call `this._http.setOptions` passing the same parameters **]**
3 changes: 2 additions & 1 deletion common/transport/http/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"istanbul": "^0.4.4",
"jshint": "^2.9.2",
"mocha": "^3.0.1",
"sinon": "^4.0.2",
"tslint": "^5.1.0",
"typescript": "2.2.2",
"@types/node": "^7.0.12"
Expand All @@ -29,7 +30,7 @@
"alltest": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter spec test/_*_test*.js",
"ci": "npm -s run lint && npm -s run build && npm -s run alltest-min && npm -s run check-cover",
"test": "npm -s run lint && npm -s run build && npm -s run unittest",
"check-cover": "istanbul check-coverage --statements 80 --branches 77 --functions 62 --lines 80"
"check-cover": "istanbul check-coverage --statements 86 --branches 80 --functions 76 --lines 86"
},
"engines": {
"node": ">= 0.10"
Expand Down
16 changes: 15 additions & 1 deletion common/transport/http/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ClientRequest, IncomingMessage } from 'http';
import { request, RequestOptions } from 'https';
import { Message, X509 } from 'azure-iot-common';
import dbg = require('debug');
const debug = dbg('azure-iot-common.Http');
const debug = dbg('azure-iot-http-base.Http');

/**
* @private
Expand All @@ -26,6 +26,8 @@ export type HttpCallback = (err: Error, body?: string, response?: IncomingMessag
* You generally want to use these higher-level objects (such as [azure-iot-device-http.Http]{@link module:azure-iot-device-http.Http}) rather than this one.
*/
export class Http {
private _options: any;

/**
* @method module:azure-iot-http-base.Http.buildRequest
* @description Builds an HTTP request object using the parameters supplied by the caller.
Expand Down Expand Up @@ -65,6 +67,11 @@ export class Http {
headers: httpHeaders
};

/*Codes_SRS_NODE_HTTP_18_001: [ If the `options` object passed into `setOptions` has a value in `http.agent`, that value shall be passed into the `request` function as `httpOptions.agent` ]*/
if (this._options && this._options.http && this._options.http.agent) {
httpOptions.agent = this._options.http.agent;
}

/*Codes_SRS_NODE_HTTP_16_001: [If `x509Options` is specified, the certificate, key and passphrase in the structure shall be used to authenticate the connection.]*/
if (x509Options) {
httpOptions.cert = (x509Options as X509).cert;
Expand Down Expand Up @@ -142,6 +149,13 @@ export class Http {
return msg;
}

/**
* @private
*/
setOptions(options: any): void {
this._options = options;
}

/**
* @method module:azure-iot-http-base.Http#parseErrorBody
* @description Parses the body of an error response and returns it as an object.
Expand Down
14 changes: 14 additions & 0 deletions common/transport/http/src/rest_api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { anHourFromNow, errors, SharedAccessSignature, X509 } from 'azure-iot-co
import { Http as HttpBase } from './http';
import * as uuid from 'uuid';
import { ClientRequest } from 'http';
import dbg = require('debug');
const debug = dbg('azure-iot-http-base.RestApiClient');



/**
Expand Down Expand Up @@ -118,6 +121,7 @@ export class RestApiClient {
}

const requestCallback = (err, responseBody, response) => {
debug(method + ' call to ' + path + ' returned ' + (err ? err : 'success'));
if (err) {
if (response) {
/*Codes_SRS_NODE_IOTHUB_REST_API_CLIENT_16_010: [If the HTTP request fails with an error code >= 300 the `executeApiCall` method shall translate the HTTP error into a transport-agnostic error using the `translateError` method and call the `done` callback with the resulting error as the only argument.]*/
Expand Down Expand Up @@ -147,7 +151,9 @@ export class RestApiClient {
request.setTimeout(timeout as number);
}

debug('sending ' + method + ' call to ' + path);
if (requestBodyString) {
debug('with body ' + requestBodyString);
request.write(requestBodyString);
}

Expand All @@ -170,6 +176,14 @@ export class RestApiClient {
this._config.sharedAccessSignature = sharedAccessSignature;
}

/**
* @private
*/
setOptions(options: any): void {
/*Codes_SRS_NODE_IOTHUB_REST_API_CLIENT_18_003: [ `setOptions` shall call `this._http.setOptions` passing the same parameters ]*/
this._http.setOptions(options);
}

/**
* @method module:azure-iothub.RestApiClient.translateError
* @description Translates an HTTP error into a transport-agnostic error.
Expand Down
29 changes: 29 additions & 0 deletions common/transport/http/test/_http_test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
'use strict';

var Http = require('../lib/http.js').Http;
var https = require('https');
var assert = require('chai').assert;
var sinon = require('sinon');
var EventEmitter = require('events');

describe('Http', function() {
describe('#parseErrorBody', function(){
Expand All @@ -28,6 +31,32 @@ describe('Http', function() {
});
});


describe('#buildRequest', function() {
var fakeOptions = {
http: {
agent: '__FAKE_AGENT__'
}
};
var fakePath = '__FAKE_PATH__';
var fakeHeaders = { 'fake' : true };
var fakeHost = '__FAKE_HOST__';
it ('uses the right agent value', function(callback) {
after(function() {
https.request.restore();
});
sinon.stub(https,'request').returns(new EventEmitter());
var http = new Http();
http.setOptions(fakeOptions);
http.buildRequest('GET', fakePath, fakeHeaders, fakeHost, function() {});
assert(https.request.called);
var httpOptions = https.request.firstCall.args[0];
assert.strictEqual(httpOptions.agent, fakeOptions.http.agent);
callback();
});

});

describe('#toMessage', function() {
/*Tests_SRS_NODE_HTTP_13_001: [ If the HTTP response has a header with the prefix iothub-app- then a new property with the header name and value as the key and value shall be added to the message. ]*/
it('adds HTTP headers with prefix iothub-app- as message properties', function() {
Expand Down
19 changes: 16 additions & 3 deletions common/transport/http/test/_rest_api_client_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
'use strict';

var assert = require('chai').assert;
var sinon = require('sinon');
var errors = require('azure-iot-common').errors;
var HttpBase = require('../lib/http.js').Http;
var PackageJson = require('../package.json');
var RestApiClient = require('../lib/rest_api_client.js').RestApiClient;

var fakeConfig = { host: 'host', sharedAccessSignature: 'sas' };
Expand All @@ -31,7 +31,7 @@ describe('RestApiClient', function() {
}, ReferenceError);
});
});

/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_16_002: [The `RestApiClient` constructor shall throw an `ArgumentError` if config is missing a `host` property.]*/
['host'].forEach(function(badPropName) {
[undefined, null, ''].forEach(function(badPropValue) {
Expand Down Expand Up @@ -118,7 +118,7 @@ describe('RestApiClient', function() {
var myFakeConfig = JSON.parse(JSON.stringify(fakeConfig));
myFakeConfig.x509 = { 'fake' : 'yes' };
var fakeHttpHelper = {
buildRequest: function(method, path, headers, host, x509, requestCallback) {
buildRequest: function(method, path, headers, host, x509) {
assert.strictEqual(myFakeConfig.x509, x509);
testCallback();
}
Expand Down Expand Up @@ -506,4 +506,17 @@ describe('RestApiClient', function() {
});
});
});

describe('#setOptions', function() {
/*Tests_SRS_NODE_IOTHUB_REST_API_CLIENT_18_003: [ `setOptions` shall call `this._http.setOptions` passing the same parameters ]*/
it ('passes the options down', function(callback) {
var fakeHttpRequestBuilder = { setOptions: sinon.stub() };
var fakeOptions = '__FAKE_OPTIONS__';
var client = new RestApiClient({host: 'host', sharedAccessSignature: 'sas'}, fakeAgent, fakeHttpRequestBuilder);
client.setOptions(fakeOptions);
assert(fakeHttpRequestBuilder.setOptions.calledWith(fakeOptions));
callback();
});

});
});
2 changes: 1 addition & 1 deletion device/transport/http/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"alltest": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter spec test/_*_test*.js",
"ci": "npm -s run lint && npm -s run build && npm -s run alltest-min && npm -s run check-cover",
"test": "npm -s run lint && npm -s run build && npm -s run unittest",
"check-cover": "istanbul check-coverage --statements 93 --branches 84 --lines 94 --functions 90"
"check-cover": "istanbul check-coverage --statements 92 --branches 82 --lines 93 --functions 91"
},
"engines": {
"node": ">= 0.10"
Expand Down
2 changes: 2 additions & 0 deletions device/transport/http/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ export class Http extends EventEmitter implements Client.Transport {
}
};

this._http.setOptions(options);

// setOptions used to exist both on Http and HttpReceiver with different options class. In order not to break backward compatibility we have
// to check what properties this options object has to figure out what to do with it.
if (options.hasOwnProperty('http') && options.http.hasOwnProperty('receivePolicy')) {
Expand Down
11 changes: 9 additions & 2 deletions device/transport/http/test/_http_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ FakeHttp.prototype.setMessageCount = function (messageCount) {
this.messageCount = messageCount;
};

FakeHttp.prototype.setOptions = function(options, callback) {
if (callback) callback();
};

describe('Http', function () {
var fakeAuthenticationProvider = null;
var transport = null;
Expand Down Expand Up @@ -571,9 +575,12 @@ describe('HttpReceiver', function () {
buildRequest: function (method, path, httpHeaders, host, sslOptions, done) {
return {
end: function () {
done(fakeError, { fake: 'response' }, 'fakeResponseBody')
done(fakeError, { fake: 'response' }, 'fakeResponseBody');
}.bind(this)
};
},
setOptions: function(options, done) {
if (done) done();
}
};
var http = new Http(fakeAuthenticationProvider, fakeHttp);
Expand Down Expand Up @@ -675,7 +682,7 @@ describe('HttpReceiver', function () {
assert.isTrue(http.disableC2D.calledOnce);
assert.isTrue(http.enableC2D.calledOnce);
testCallback();
})
});
});
});

Expand Down

0 comments on commit 4dda10b

Please sign in to comment.