From 276546433d5253da70103be85c90afe3bdb47718 Mon Sep 17 00:00:00 2001 From: Phillip Johnsen Date: Tue, 1 Sep 2015 16:19:35 +0200 Subject: [PATCH] Fix XML parsing and enable HTTPS requests. * Bugfix for parsing XML when responses has a XML Content-Type header. * Also enable HTTPS requests by inspecting the `options.port` value, using https:// whenever port equals 443. --- lib/api.js | 41 ++++++++++++++++++++++++---------------- test/query-test.js | 21 ++++++++++++++++++++ test/samples/devices.xml | 7 +++++++ test/server.js | 3 ++- 4 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 test/samples/devices.xml diff --git a/lib/api.js b/lib/api.js index 00a92da..f63398f 100644 --- a/lib/api.js +++ b/lib/api.js @@ -6,6 +6,8 @@ var Q = require('q'); var xml2js = require('xml2js'); var headers = require('plex-api-headers'); +var xmlToJSON = Q.denodeify(xml2js.parseString); + var uri = require('./uri'); var PLEX_SERVER_PORT = 32400; @@ -35,7 +37,7 @@ function PlexAPI(options, deprecatedPort) { console.warn('PlexAPI constuctor port argument is deprecated, use an options object instead.'); } - this.serverUrl = 'http://' + hostname + ':' + this.port; + this.serverUrl = hostname + ':' + this.port; this._initializeAuthenticator(); } @@ -88,7 +90,7 @@ PlexAPI.prototype.find = function find(relativeUrl, criterias) { PlexAPI.prototype._request = function _request(relativeUrl, method, parseResponse) { var self = this; var deferred = Q.defer(); - var reqUrl = generateRelativeUrl.call(this, relativeUrl); + var reqUrl = this._generateRelativeUrl(relativeUrl); var reqOpts = { url: url.parse(reqUrl), encoding: null, @@ -104,6 +106,9 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons if (err) { return deferred.reject(err); } + + var resolveValue = body; + if (response.statusCode === 401) { if (self.authenticator === undefined) { return deferred.reject(new Error('Plex Server denied request, you must provide a way to authenticate! ' + @@ -116,6 +121,7 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons }) ); } + if (response.statusCode !== 200) { return deferred.reject(new Error('Plex Server didnt respond with status code 200, response code: ' + response.statusCode)); } @@ -124,19 +130,17 @@ PlexAPI.prototype._request = function _request(relativeUrl, method, parseRespons // releasing socket back to the agent connection pool: http://nodejs.org/api/http.html#http_agent_maxsockets response.on('data', function onData() {}); - if (parseResponse) { - if (response.headers['content-type'] === 'application/json') { - return deferred.resolve(JSON.parse(body.toString('utf8'))); - } - if (response.headers['content-type'].indexOf('xml') > -1) { - return deferred.resolve(xml2js.parseString(body.toString('utf8'), { - object: true - })); - } - return deferred.resolve(body); - } else { + if (!parseResponse) { return deferred.resolve(); } + + if (response.headers['content-type'] === 'application/json') { + resolveValue = JSON.parse(body.toString('utf8')); + } else if (response.headers['content-type'].indexOf('xml') > -1) { + resolveValue = xmlToJSON(body.toString('utf8'), { attrkey: 'attributes' }); + } + + return deferred.resolve(resolveValue); }); return deferred.promise; @@ -179,6 +183,14 @@ PlexAPI.prototype._initializeAuthenticator = function _initializeAuthenticator() } }; +PlexAPI.prototype._generateRelativeUrl = function _generateRelativeUrl(relativeUrl) { + return this._serverScheme() + this.serverUrl + relativeUrl; +}; + +PlexAPI.prototype._serverScheme = function _serverScheme() { + return this.port === 443 ? 'https://' : 'http://'; +}; + function filterChildrenByCriterias(children, criterias) { var context = { criterias: criterias || {} @@ -196,8 +208,5 @@ function criteriasMatchChild(child) { }, true); } -function generateRelativeUrl(relativeUrl) { - return this.serverUrl + relativeUrl; -} module.exports = PlexAPI; diff --git a/test/query-test.js b/test/query-test.js index fff4c92..439489f 100644 --- a/test/query-test.js +++ b/test/query-test.js @@ -89,4 +89,25 @@ describe('query()', function() { }); }); }); + + describe('XML responses', function() { + it('should convert XML to a JSON object', function() { + var plexTvApi = new PlexAPI({ + hostname: 'plex.tv', + port: 443 + }); + + server.stop(); + server.start({ + schemeAndHost: 'https://plex.tv', + port: 443, + contentType: 'application/xml' + }); + + return plexTvApi.query('/devices.xml').then(function(result) { + expect(result.MediaContainer).to.be.an('object'); + expect(result.MediaContainer.attributes.publicAddress).to.equal('47.1.2.4'); + }); + }); + }); }); \ No newline at end of file diff --git a/test/samples/devices.xml b/test/samples/devices.xml new file mode 100644 index 0000000..6af05f1 --- /dev/null +++ b/test/samples/devices.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/test/server.js b/test/server.js index a37fc77..07f1ea1 100644 --- a/test/server.js +++ b/test/server.js @@ -35,11 +35,12 @@ function replaceActualPathToRoot(path) { module.exports = { start: function start(options) { options = options || {}; + options.schemeAndHost = options.schemeAndHost || 'http://localhost'; options.port = options.port || PLEX_SERVER_PORT; options.contentType = options.contentType || 'application/json'; respondWith = 'content'; - var scope = nock('http://localhost:' + options.port, { + var scope = nock(options.schemeAndHost + ':' + options.port, { reqheaders: options.reqheaders }) .defaultReplyHeaders({