From 212ed70227f1d907e10073d66adcfa493b704f7a Mon Sep 17 00:00:00 2001 From: michelelazzeri Date: Tue, 16 May 2017 16:02:47 +0200 Subject: [PATCH 1/9] allowing apac throw proxy --- lib/operation-helper.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/operation-helper.js b/lib/operation-helper.js index a41f966..022cba3 100644 --- a/lib/operation-helper.js +++ b/lib/operation-helper.js @@ -35,6 +35,10 @@ class OperationHelper { this.baseUri = params.baseUri || OperationHelper.defaultBaseUri this.xml2jsOptions = Object.assign({}, defaultXml2JsOptions, params.xml2jsOptions) this.throttler = new Throttler(params.maxRequestsPerSecond) + this.proxy_username = params.proxy_username + this.proxy_password = params.proxy_password + this.proxy_hostname = params.proxy_hostname + this.proxy_port = params.proxy_port || 8080 // set version if (typeof(params.version) === 'string') OperationHelper.version = params.version @@ -91,6 +95,21 @@ class OperationHelper { path: uri, method: 'GET' } + if (this.proxy_hostname) { + options = { + hostname: this.proxy_hostname, + port: this.proxy_port, + path: 'http://' + host + uri, + method: 'GET', + headers: { + Host: host + } + } + if (this.proxy_username) { + var auth = 'Basic ' + new Buffer(this.proxy_username + ':' + this.proxy_password).toString('base64') + options.headers["Proxy-Authorization"] = auth + } + } if (scheme) options['scheme'] = scheme; From a9e5d9b82bcd5e011b91c4025504f2b0323bb48d Mon Sep 17 00:00:00 2001 From: michelelazzeri Date: Tue, 16 May 2017 16:34:32 +0200 Subject: [PATCH 2/9] uniform parameter names in camelCase --- lib/operation-helper.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/operation-helper.js b/lib/operation-helper.js index 022cba3..fd5b481 100644 --- a/lib/operation-helper.js +++ b/lib/operation-helper.js @@ -35,10 +35,10 @@ class OperationHelper { this.baseUri = params.baseUri || OperationHelper.defaultBaseUri this.xml2jsOptions = Object.assign({}, defaultXml2JsOptions, params.xml2jsOptions) this.throttler = new Throttler(params.maxRequestsPerSecond) - this.proxy_username = params.proxy_username - this.proxy_password = params.proxy_password - this.proxy_hostname = params.proxy_hostname - this.proxy_port = params.proxy_port || 8080 + this.proxyUsername = params.proxyUsername + this.proxyPassword = params.proxyPassword + this.proxyHostname = params.proxyHostname + this.proxyPort = params.proxyPort || 8080 // set version if (typeof(params.version) === 'string') OperationHelper.version = params.version @@ -95,18 +95,18 @@ class OperationHelper { path: uri, method: 'GET' } - if (this.proxy_hostname) { + if (this.proxyHostname) { options = { - hostname: this.proxy_hostname, - port: this.proxy_port, + hostname: this.proxyHostname, + port: this.proxyPort, path: 'http://' + host + uri, method: 'GET', headers: { Host: host } } - if (this.proxy_username) { - var auth = 'Basic ' + new Buffer(this.proxy_username + ':' + this.proxy_password).toString('base64') + if (this.proxyUsername) { + var auth = 'Basic ' + new Buffer(this.proxyUsername + ':' + this.proxyPassword).toString('base64') options.headers["Proxy-Authorization"] = auth } } From 6de5d7cda1ba25ca6d785010183b6a8efe793c9b Mon Sep 17 00:00:00 2001 From: michelelazzeri Date: Tue, 16 May 2017 16:35:35 +0200 Subject: [PATCH 3/9] add proxy documentation --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 1c119b3..60fb431 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,22 @@ ES|Spain|webservices.amazon.es UK|United Kingdom|webservices.amazon.co.uk US|United States|webservices.amazon.com +## Proxy + +To allow node-apac throw proxy, set the proxy configuration + +```javascript +var opHelper = new OperationHelper({ + awsId: '[YOUR AWS ID HERE]', + awsSecret: '[YOUR AWS SECRET HERE]', + assocId: '[YOUR ASSOCIATE TAG HERE]', + proxyHostname: '[YOUR PROXY HOSTNAME HERE]', + proxyPort: '[YOUR PROXY PORT NAME HERE]', # default 8080 + proxyUsername: '[YOUR PROXY USERNAME NAME HERE]' + proxyPassword: '[YOUR PROXY PASSWORD NAME HERE]' +}); +``` + ## Contributing Feel free to submit a pull request. If you'd like, you may discuss the change with me first by submitting an issue. From a1da60368027be915916abd73a761848b859c560 Mon Sep 17 00:00:00 2001 From: Michele Lazzeri Date: Tue, 16 May 2017 16:41:11 +0200 Subject: [PATCH 4/9] Update README.md typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 60fb431..f1b6a05 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,9 @@ var opHelper = new OperationHelper({ awsSecret: '[YOUR AWS SECRET HERE]', assocId: '[YOUR ASSOCIATE TAG HERE]', proxyHostname: '[YOUR PROXY HOSTNAME HERE]', - proxyPort: '[YOUR PROXY PORT NAME HERE]', # default 8080 - proxyUsername: '[YOUR PROXY USERNAME NAME HERE]' - proxyPassword: '[YOUR PROXY PASSWORD NAME HERE]' + proxyPort: '[YOUR PROXY PORT HERE]', # default 8080 + proxyUsername: '[YOUR PROXY USERNAME HERE]' + proxyPassword: '[YOUR PROXY PASSWORD HERE]' }); ``` From 2512e30efad083c01001226bfa53a13da368e6e1 Mon Sep 17 00:00:00 2001 From: Michele Lazzeri Date: Tue, 16 May 2017 16:45:57 +0200 Subject: [PATCH 5/9] Update README.md typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1b6a05..ffc8fad 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ US|United States|webservices.amazon.com ## Proxy -To allow node-apac throw proxy, set the proxy configuration +To allow node-apac through proxy, set the proxy configuration ```javascript var opHelper = new OperationHelper({ From 6bd6175ee48094ffe29f9ee2ba8ffe4520401f09 Mon Sep 17 00:00:00 2001 From: michelelazzeri Date: Wed, 24 Jan 2018 14:43:50 +0100 Subject: [PATCH 6/9] Starting from 23 January 2018 at 17:30 UTC, PA-API refuse requests without "Accept" header. Added, "Accept" : "*/*" to HTTP request to fix the problem. --- lib/operation-helper.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/operation-helper.js b/lib/operation-helper.js index fd5b481..5df40a1 100644 --- a/lib/operation-helper.js +++ b/lib/operation-helper.js @@ -93,7 +93,10 @@ class OperationHelper { var options = { hostname: host, path: uri, - method: 'GET' + method: 'GET', + headers: { + Accept: '*/*' + } } if (this.proxyHostname) { options = { @@ -102,7 +105,8 @@ class OperationHelper { path: 'http://' + host + uri, method: 'GET', headers: { - Host: host + Host: host, + Accept: '*/*' } } if (this.proxyUsername) { From 3cf46ad4d7e4d1a78a5cb39a1c2f2166f5f831f2 Mon Sep 17 00:00:00 2001 From: michelelazzeri Date: Wed, 24 Jan 2018 17:05:01 +0100 Subject: [PATCH 7/9] fix test. Add header Accept: */* --- lib/operation-helper.specs.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/operation-helper.specs.js b/lib/operation-helper.specs.js index 56524b1..0cc55a6 100644 --- a/lib/operation-helper.specs.js +++ b/lib/operation-helper.specs.js @@ -193,7 +193,10 @@ describe('OperationHelper', function () { hostname: locale.DEFAULT_ENDPOINT, method: 'GET', path: 'testUri', - scheme: 'http' + scheme: 'http', + headers: { + Accept: '*/*' + } }) }) From c460b61f80a584322ee20d715ba13b8cbf0f6df1 Mon Sep 17 00:00:00 2001 From: mlazzeri Date: Wed, 20 Mar 2019 11:54:56 +0100 Subject: [PATCH 8/9] fix proxy for https client requests to apac --- lib/operation-helper.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/operation-helper.js b/lib/operation-helper.js index 17eb403..bba30e4 100644 --- a/lib/operation-helper.js +++ b/lib/operation-helper.js @@ -5,6 +5,7 @@ const Throttler = require('./throttler') const locale = require('./locale') const https = require('https') +const http = require('http') const xml2js = require('xml2js') const defaultXml2JsOptions = { @@ -34,10 +35,10 @@ class OperationHelper { this.baseUri = params.baseUri || OperationHelper.defaultBaseUri this.xml2jsOptions = Object.assign({}, defaultXml2JsOptions, params.xml2jsOptions) this.throttler = new Throttler(params.maxRequestsPerSecond) - this.proxyUsername = params.proxyUsername - this.proxyPassword = params.proxyPassword - this.proxyHostname = params.proxyHostname + if (typeof(params.proxyHostname) === 'string') this.proxyHostname = params.proxyHostname this.proxyPort = params.proxyPort || 8080 + if (typeof(params.proxyUsername) === 'string') this.proxyUsername = params.proxyUsername + if (typeof(params.proxyPassword) === 'string') this.proxyPassword = params.proxyPassword // set version if (typeof(params.version) === 'string') OperationHelper.version = params.version @@ -96,19 +97,15 @@ class OperationHelper { hostname: host, path: uri, method: 'GET', - headers: { - Accept: '*/*' - } } if (this.proxyHostname) { options = { hostname: this.proxyHostname, port: this.proxyPort, - path: 'http://' + host + uri, + path: 'https://' + host + uri, method: 'GET', headers: { - Host: host, - Accept: '*/*' + Host: host } } if (this.proxyUsername) { @@ -120,7 +117,7 @@ class OperationHelper { var responseBody = '' const promise = new Promise((resolve, reject) => { - var request = https.request(options, function (response) { + var request = (this.proxyHostname? http: https).request(options, function (response) { response.setEncoding('utf8') response.on('data', function (chunk) { From c9033bc80714422270df611cddbc963fe242e6e4 Mon Sep 17 00:00:00 2001 From: mlazzeri Date: Thu, 21 Mar 2019 10:43:54 +0100 Subject: [PATCH 9/9] add tests for proxy configuration --- lib/operation-helper.specs.js | 118 ++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/lib/operation-helper.specs.js b/lib/operation-helper.specs.js index 9753b5e..f35eb24 100644 --- a/lib/operation-helper.specs.js +++ b/lib/operation-helper.specs.js @@ -1,6 +1,7 @@ "use strict" const https = require('https') +const http = require('http') const EventEmitter = require('events') const xml2js = require('xml2js') const proxyquire = require('proxyquire') @@ -243,6 +244,123 @@ describe('OperationHelper', function () { }) }) + describe('#execute with proxy', () => { + let requestMock, responseMock, result, outputResponseBody + const responseBody = 'xml' + const xml2jsOptions = {foo: 'bar'} + const expectedXml2jsOptions = Object.assign({explicitArray: false}, xml2jsOptions) + const proxyHostname ='localhost' + const proxyPort = 8080 + const proxyUsername = 'username' + const proxyPassword = 'password' + const testURI = '/onca/xml'; + + context('happy path proxy', () => { + let opHelper + + beforeEach(() => { + opHelper = new OperationHelper({ + awsId: 'testAwsId', + awsSecret: 'testAwsSecret', + assocId: 'testAssocId', + proxyHostname: proxyHostname, + proxyPort: proxyPort, + proxyUsername: proxyUsername, + proxyPassword: proxyPassword, + xml2jsOptions + }) + + responseMock = new EventEmitter() + responseMock.setEncoding = sinon.spy() + + requestMock = new EventEmitter() + requestMock.end = () => { + responseMock.emit('data', responseBody.substr(0, 5)) + responseMock.emit('data', responseBody.substr(5)) + responseMock.emit('end') + } + + sinon.stub(http, 'request').returns(requestMock).callsArgWith(1, responseMock) + sinon.stub(opHelper, 'generateUri').returns(testURI) + sinon.spy(xml2js, 'parseString') + }) + + afterEach(() => { + http.request.restore() + xml2js.parseString.restore() + }) + + const doAssertions = () => { + it('should create an http request to proxy with the correct options', () => { + expect(http.request.callCount).to.equal(1) + expect(http.request.firstCall.args[0]).to.eql({ + hostname: proxyHostname, + port: proxyPort, + method: 'GET', + headers: { + Host: locale.DEFAULT_ENDPOINT, + "Proxy-Authorization": "Basic " + new Buffer(proxyUsername + ':' + proxyPassword).toString('base64') + }, + path: 'https://' + locale.DEFAULT_ENDPOINT + testURI, + }) + }) + + it('should set the response encoding to utf8 (via proxy)', () => { + expect(responseMock.setEncoding.calledWith('utf8')) + }) + + it('should provide the raw response body', () => { + expect(outputResponseBody).to.equal(responseBody) + }) + + it('should pass the xml2jsOptions to xml2js', () => { + expect(xml2js.parseString.firstCall.args[1]).to.eql(expectedXml2jsOptions) + }) + + it('should parse XML and return result as object', () => { + expect(result).to.eql({ + it: { + is: { + some: 'xml' + } + } + }) + }) + } + + context('(traditional callback)', () => { + beforeEach((done) => { + opHelper.execute('ItemSearch', { + 'SearchIndex': 'Books', + 'Keywords': 'harry potter', + 'ResponseGroup': 'ItemAttributes,Offers' + }, function (err, _results, _rawResponseBody) { + result = _results + outputResponseBody = _rawResponseBody + done() + }) + }) + + doAssertions() + }) + + context('(promise)', () => { + beforeEach(() => { + return opHelper.execute('ItemSearch', { + 'SearchIndex': 'Books', + 'Keywords': 'harry potter', + 'ResponseGroup': 'ItemAttributes,Offers' + }).then((response) => { + result = response.result + outputResponseBody = response.responseBody + }) + }) + + doAssertions() + }) + }) + }) + context('when the request has an error', () => { const error = new Error('testErrorMessage') let thrownError, opHelper