Skip to content

Commit

Permalink
[#51] Add proxy support for external requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
mwbrooks committed May 5, 2014
1 parent d06dc4e commit 8bbc5fd
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 11 deletions.
6 changes: 5 additions & 1 deletion lib/middleware.js
Expand Up @@ -12,7 +12,8 @@ var autoreload = require('./middleware/autoreload'),
mstatic = require('./middleware/static'),
nocache = require('./middleware/nocache'),
path = require('path'),
plugins = require('./middleware/cordova/plugins');
plugins = require('./middleware/cordova/plugins'),
proxy = require('./middleware/proxy');

/**
* Request Listener / Connect Middleware.
Expand Down Expand Up @@ -71,6 +72,9 @@ module.exports = function(options) {
// serve static assets
app.use(mstatic(options));

// proxy cross-origin requests
app.use(proxy(options));

// serve cordova js if 404'd out from previous static server
app.use(cordova(options));

Expand Down
17 changes: 7 additions & 10 deletions lib/middleware/inject.js
Expand Up @@ -7,24 +7,21 @@ var fs = require('fs'),
path = require('path');

/**
* Middlware to inject JavaScript into the served app.
*
* - injects JavaScript to navigate to the homepage of the PhoneGap App.
* - injects JavaScript to refresh the served app.
* Middleware to inject JavaScript into the served app.
*/

module.exports = function() {
var hammerJs = path.join(__dirname, '../../res/middleware/hammer/hammer.js'),
var autoreloadScript = path.join(__dirname, '../../res/middleware/autoreload.js'),
homepageScript = path.join(__dirname, '../../res/middleware/homepage.js'),
refreshScript = path.join(__dirname, '../../res/middleware/refresh.js'),
autoreloadScript = path.join(__dirname, '../../res/middleware/autoreload.js');
proxyScript = path.join(__dirname, '../../res/middleware/proxy.js'),
refreshScript = path.join(__dirname, '../../res/middleware/refresh.js');

return inject({
snippet: [
//fs.readFileSync(hammerJs),
fs.readFileSync(autoreloadScript),
fs.readFileSync(homepageScript),
fs.readFileSync(refreshScript),
fs.readFileSync(autoreloadScript)
fs.readFileSync(proxyScript),
fs.readFileSync(refreshScript)
]
});
};
31 changes: 31 additions & 0 deletions lib/middleware/proxy.js
@@ -0,0 +1,31 @@
/*!
* Module dependencies.
*/

var request = require('request');

/**
* Cross-Origin Requests Middleware.
*
* Add support for cross-origin resource requests by proxying all of the requests
* through this route. Each request includes an encoded path that is the original
* URL.
*
* Options:
*
* - `options` {Object} is unused.
*/

module.exports = function(options) {
return function(req, res, next) {
if (req.url.indexOf('/proxy/') >= 0) {
var url = decodeURIComponent(req.url.replace('/proxy/', ''));
var proxy = request(url);
req.pipe(proxy);
proxy.pipe(res);
}
else {
next();
}
};
};
25 changes: 25 additions & 0 deletions res/middleware/proxy.js
@@ -0,0 +1,25 @@
<script type="text/javascript">
//
// Proxy
//
// Intercept XHR calls that would violate single-origin policy.
// These requests will be proxied through the server.
//
(function() {
var xhr = {};
xhr.open = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function(method, url) {
var parser = document.createElement('a');
parser.href = url;

// proxy the cross-origin request
if (!parser.hostname.match(window.location.hostname)) {
url = '/proxy/' + encodeURIComponent(url);
alert(url);
}

xhr.open.apply(this, arguments);
};
})(window);
</script>
14 changes: 14 additions & 0 deletions spec/middleware/inject.spec.js
Expand Up @@ -71,4 +71,18 @@ describe('inject middleware', function() {
});
});
});

it('should inject proxy logic', function(done) {
chdir('spec/fixture/app-with-cordova', function() {
request(phonegap())
.get('/')
.set('accept', 'text/html')
.end(function(e, res) {
expect(res.statusCode).toEqual(200);
expect(res.text).toMatch('// Proxy');
this.app.close();
done();
});
});
});
});
64 changes: 64 additions & 0 deletions spec/middleware/proxy.spec.js
@@ -0,0 +1,64 @@
/*!
* Module dependencies.
*/

var chdir = require('chdir'),
gaze = require('gaze'),
phonegap = require('../../lib'),
request = require('supertest');

/*!
* Specification: Proxy middleware.
*/

describe('proxy middleware', function() {
beforeEach(function() {
spyOn(gaze, 'Gaze').andReturn({ on: function() {} });
});

describe('single-origin request', function() {
it('should be served normally', function(done) {
chdir('spec/fixture/app-with-cordova', function() {
request(phonegap())
.get('/cordova.js')
.end(function(e, res) {
expect(res.statusCode).toEqual(200);
expect(res.text).toMatch('i am cordova');
this.app.close();
done();
});
});
});
});

//
// better to mock the requests but this is awkward with request lib.
//
describe('cross-origin request', function() {
it('should proxy http:', function() {
chdir('spec/fixture/app-with-cordova', function() {
request(phonegap())
.get('/proxy/http%3A%2F%2Fphonegap.com')
.end(function(e, res) {
expect(res.statusCode).toEqual(200);
expect(res.text).toMatch('PhoneGap');
this.app.close();
done();
});
});
});

it('should proxy https:', function() {
chdir('spec/fixture/app-with-cordova', function() {
request(phonegap())
.get('/proxy/https%3A%2F%2Fajax.googleapis.com%2Fajax%2Fservices%2Fsearch%2Fweb%3Fv%3D1.0%26q%3DAdobe%2520PhoneGap')
.end(function(e, res) {
expect(res.statusCode).toEqual(200);
expect(res.text).toMatch('GsearchResultClass');
this.app.close();
done();
});
});
});
});
});

0 comments on commit 8bbc5fd

Please sign in to comment.