mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge remote-tracking branch 'origin/android_tweaks' into cordova-httpd
Conflicts: tools/commands-cordova.js
This commit is contained in:
@@ -13,8 +13,8 @@
|
||||
if (/Android/i.test(navigator.userAgent)) {
|
||||
// When Android app is emulated, it cannot connect to localhost,
|
||||
// instead it should connect to 10.0.2.2
|
||||
__meteor_runtime_config__.ROOT_URL = (__meteor_runtime_config__.ROOT_URL || '').replace(/localhost/i, '10.0.2.2');
|
||||
__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL = (__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL || '').replace(/localhost/i, '10.0.2.2');
|
||||
//__meteor_runtime_config__.ROOT_URL = (__meteor_runtime_config__.ROOT_URL || '').replace(/localhost/i, '10.0.2.2');
|
||||
//__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL = (__meteor_runtime_config__.DDP_DEFAULT_CONNECTION_URL || '').replace(/localhost/i, '10.0.2.2');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ else
|
||||
fi
|
||||
|
||||
{
|
||||
curl -O http://www.motorlogy.com/apache//ant/binaries/apache-ant-1.9.4-bin.tar.gz
|
||||
curl -O http://apache.osuosl.org/ant/binaries/apache-ant-1.9.4-bin.tar.gz
|
||||
tar xzf apache-ant-1.9.4-bin.tar.gz
|
||||
rm apache-ant-1.9.4-bin.tar.gz
|
||||
|
||||
@@ -58,9 +58,12 @@ fi
|
||||
# the platform that cordova likes
|
||||
echo y | android-sdk/tools/android update sdk -t android-19 -u
|
||||
|
||||
# system image for android 19
|
||||
# system image for android 19 - arm
|
||||
echo y | android-sdk/tools/android update sdk -t sys-img-armeabi-v7a-android-19 --all -u
|
||||
|
||||
# system image for android 19 - x86
|
||||
echo y | android-sdk/tools/android update sdk -t sys-img-x86-android-19 --all -u
|
||||
|
||||
# build tools
|
||||
echo y | android-sdk/tools/android update sdk -t "build-tools-20.0.0" -u
|
||||
|
||||
|
||||
@@ -57,7 +57,11 @@ var execFileAsyncOrThrow = function (file, args, opts, cb) {
|
||||
}
|
||||
|
||||
// XXX a hack to always tell the scripts where warehouse is
|
||||
if (opts) opts.env = _.extend({ "WAREHOUSE_DIR": tropo.root, "USE_GLOBAL_ADK": process.env.USE_GLOBAL_ADK || "", HOME: process.env.HOME }, opts.env);
|
||||
opts = opts || {};
|
||||
opts.env = _.extend({ "USE_GLOBAL_ADK": "" },
|
||||
process.env,
|
||||
opts.env || {},
|
||||
{ "WAREHOUSE_DIR": tropo.root });
|
||||
|
||||
var execFileAsync = require('./utils.js').execFileAsync;
|
||||
ensureAndroidBundle(file);
|
||||
@@ -82,7 +86,11 @@ var execFileSyncOrThrow = function (file, args, opts) {
|
||||
verboseLog('Running synchronously: ', file, args);
|
||||
|
||||
// XXX a hack to always tell the scripts where warehouse is
|
||||
if (opts) opts.env = _.extend({ "WAREHOUSE_DIR": tropo.root, "USE_GLOBAL_ADK": process.env.USE_GLOBAL_ADK || "", HOME: process.env.HOME }, opts.env);
|
||||
opts = opts || {};
|
||||
opts.env = _.extend({ "USE_GLOBAL_ADK": "" },
|
||||
process.env,
|
||||
opts.env || {},
|
||||
{ "WAREHOUSE_DIR": tropo.root });
|
||||
|
||||
var childProcess = execFileSync(file, args, opts);
|
||||
if (! childProcess.success)
|
||||
@@ -616,6 +624,44 @@ cordova.buildPlatforms = function (localPath, platforms, options) {
|
||||
buildCordova(localPath, 'build', options);
|
||||
};
|
||||
|
||||
|
||||
cordova.buildPlatformRunners = function (localPath, platforms, options) {
|
||||
var runners = [];
|
||||
_.each(platforms, function (platformName) {
|
||||
runners.push(new CordovaRunner(localPath, platformName, options));
|
||||
});
|
||||
return runners;
|
||||
};
|
||||
|
||||
|
||||
// This is a runner, that we pass to Runner (run-all.js)
|
||||
var CordovaRunner = function (localPath, platformName, options) {
|
||||
var self = this;
|
||||
|
||||
self.localPath = localPath;
|
||||
self.platformName = platformName;
|
||||
self.options = options;
|
||||
|
||||
self.title = 'Cordova (' + self.platformName + ')';
|
||||
};
|
||||
|
||||
_.extend(CordovaRunner.prototype, {
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
execCordovaOnPlatform(self.localPath,
|
||||
self.platformName,
|
||||
self.options);
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
var self = this;
|
||||
|
||||
// XXX: A no-op for now (we leave it running because it's slow!)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Start the simulator or physical device for a specific platform.
|
||||
// platformName is of the form ios/ios-device/android/android-device
|
||||
// options:
|
||||
@@ -631,9 +677,12 @@ var execCordovaOnPlatform = function (localPath, platformName, options) {
|
||||
|
||||
verboseLog('isDevice:', isDevice);
|
||||
|
||||
var args = [ 'run',
|
||||
isDevice ? '--device' : '--emulator',
|
||||
platform ];
|
||||
var args = [ 'run' ];
|
||||
if (options.verbose) {
|
||||
args.push('--verbose');
|
||||
}
|
||||
args.push(isDevice ? '--device' : '--emulator');
|
||||
args.push(platform);
|
||||
|
||||
// XXX assert we have a valid Cordova project
|
||||
if (platform === 'ios' && isDevice) {
|
||||
@@ -645,9 +694,16 @@ var execCordovaOnPlatform = function (localPath, platformName, options) {
|
||||
'platforms', 'ios', '*.xcodeproj')]);
|
||||
} else {
|
||||
verboseLog('Running emulator:', localCordova, args);
|
||||
var emulatorOptions = { verbose: options.verbose, cwd: cordovaPath };
|
||||
emulatorOptions.env = _.extend({}, process.env);
|
||||
if (options.httpProxyPort) {
|
||||
// XXX: Is this Android only?
|
||||
// This is odd; the IP address is on the host, not inside the emulator
|
||||
emulatorOptions.env['http_proxy'] = '127.0.0.1:' + options.httpProxyPort;
|
||||
}
|
||||
execFileAsyncOrThrow(
|
||||
localCordova, args,
|
||||
{ verbose: options.verbose, cwd: cordovaPath });
|
||||
emulatorOptions);
|
||||
}
|
||||
|
||||
var Log = getLoadedPackages().logging.Log;
|
||||
@@ -733,14 +789,7 @@ var execCordovaOnPlatform = function (localPath, platformName, options) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Start the simulator or physical device for a list of platforms
|
||||
// options:
|
||||
// - verbose: print all build logs
|
||||
cordova.runPlatforms = function (localPath, platforms, options) {
|
||||
_.each(platforms, function (platformName) {
|
||||
execCordovaOnPlatform(localPath, platformName, options);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// packages - list of strings
|
||||
cordova.filterPackages = function (packages) {
|
||||
@@ -843,10 +892,16 @@ main.registerCommand({
|
||||
name: "configure-android",
|
||||
options: {
|
||||
verbose: { type: Boolean, short: "v" }
|
||||
}
|
||||
},
|
||||
minArgs: 0,
|
||||
maxArgs: Infinity
|
||||
}, function (options) {
|
||||
cordova.setVerboseness(options.verbose);
|
||||
|
||||
var androidArgs = options.args || [];
|
||||
try {
|
||||
execFileSyncOrThrow(localAndroid, [], options);
|
||||
var execOptions = { pipeOutput: true, verbose: options.verbose };
|
||||
execFileSyncOrThrow(localAndroid, androidArgs, execOptions);
|
||||
} catch (err) {
|
||||
// this tool can crash for whatever reason, ignore its failures
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ main.registerCommand({
|
||||
options: {
|
||||
port: { type: String, short: "p", default: '3000' },
|
||||
'app-port': { type: String },
|
||||
'http-proxy-port': { type: String },
|
||||
production: { type: Boolean },
|
||||
'raw-logs': { type: Boolean },
|
||||
settings: { type: String },
|
||||
@@ -199,6 +200,8 @@ main.registerCommand({
|
||||
return 1;
|
||||
}
|
||||
|
||||
options.httpProxyPort = options['http-proxy-port'];
|
||||
|
||||
// If we are targeting the remote devices
|
||||
if (_.intersection(options.args, ['ios-device', 'android-device']).length) {
|
||||
cordova.verboseLog('A run on a device requested');
|
||||
@@ -213,15 +216,22 @@ main.registerCommand({
|
||||
// Always bundle for the browser by default.
|
||||
var webArchs = project.getWebArchs();
|
||||
|
||||
var runners = [];
|
||||
|
||||
// If additional args were specified, then also start a mobile build.
|
||||
if (options.args.length) {
|
||||
// will asynchronously start mobile emulators/devices
|
||||
try {
|
||||
// --clean encpasulates the behavior of once
|
||||
// --clean encapsulates the behavior of once
|
||||
if (options.clean) {
|
||||
options.once = true;
|
||||
}
|
||||
|
||||
if (!options.httpProxyPort) {
|
||||
console.log('Forcing http proxy on port 3002 for mobile');
|
||||
options.httpProxyPort = '3002';
|
||||
}
|
||||
|
||||
cordova.verboseLog('Will compile mobile builds');
|
||||
var appName = path.basename(options.appDir);
|
||||
var localPath = path.join(options.appDir, '.meteor', 'local');
|
||||
@@ -229,7 +239,7 @@ main.registerCommand({
|
||||
cordova.buildPlatforms(localPath, options.args,
|
||||
_.extend({ appName: appName, debug: ! options.production },
|
||||
options, parsedHostPort));
|
||||
cordova.runPlatforms(localPath, options.args, options);
|
||||
runners = runners.concat(cordova.buildPlatformRunners(localPath, options.args, options));
|
||||
} catch (err) {
|
||||
if (options.verbose) {
|
||||
process.stderr.write('Error while running for mobile platforms ' +
|
||||
@@ -278,6 +288,7 @@ main.registerCommand({
|
||||
return runAll.run(options.appDir, {
|
||||
proxyPort: parsedHostPort.port,
|
||||
proxyHost: parsedHostPort.host,
|
||||
httpProxyPort: options.httpProxyPort,
|
||||
appPort: appPort,
|
||||
appHost: appHost,
|
||||
settingsFile: options.settings,
|
||||
@@ -289,7 +300,8 @@ main.registerCommand({
|
||||
rootUrl: process.env.ROOT_URL,
|
||||
mongoUrl: process.env.MONGO_URL,
|
||||
oplogUrl: process.env.MONGO_OPLOG_URL,
|
||||
once: options.once
|
||||
once: options.once,
|
||||
extraRunners: runners
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1119,6 +1131,7 @@ main.registerCommand({
|
||||
maxArgs: Infinity,
|
||||
options: {
|
||||
port: { type: String, short: "p", default: "localhost:3000" },
|
||||
'http-proxy-port': { type: String },
|
||||
deploy: { type: String },
|
||||
production: { type: Boolean },
|
||||
settings: { type: String },
|
||||
@@ -1151,6 +1164,8 @@ main.registerCommand({
|
||||
return 1;
|
||||
}
|
||||
|
||||
options.httpProxyPort = options['http-proxy-port'];
|
||||
|
||||
// XXX not good to change the options this way
|
||||
_.extend(options, parsedHostPort);
|
||||
|
||||
@@ -1187,6 +1202,8 @@ main.registerCommand({
|
||||
[options['driver-package'] || 'test-in-browser'],
|
||||
'add');
|
||||
|
||||
var runners = [];
|
||||
|
||||
var mobileOptions = ['ios', 'ios-device', 'android', 'android-device'];
|
||||
var mobilePlatforms = [];
|
||||
|
||||
@@ -1196,6 +1213,11 @@ main.registerCommand({
|
||||
});
|
||||
|
||||
if (! _.isEmpty(mobilePlatforms)) {
|
||||
if (!options.httpProxyPort) {
|
||||
console.log('Forcing http proxy on port 3002 for mobile');
|
||||
options.httpProxyPort = '3002'
|
||||
}
|
||||
|
||||
var localPath = path.join(testRunnerAppDir, '.meteor', 'local');
|
||||
|
||||
var platforms =
|
||||
@@ -1211,13 +1233,14 @@ main.registerCommand({
|
||||
appName: path.basename(testRunnerAppDir),
|
||||
debug: ! options.production
|
||||
}));
|
||||
cordova.runPlatforms(localPath, mobilePlatforms, options);
|
||||
runners = runners.concat(cordova.buildPlatformRunners(localPath, mobilePlatforms, options));
|
||||
} catch (err) {
|
||||
process.stderr.write(err.message + '\n');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
options.extraRunners = runners;
|
||||
return runTestAppForPackages(testPackages, testRunnerAppDir, options);
|
||||
});
|
||||
|
||||
@@ -1394,6 +1417,7 @@ var runTestAppForPackages = function (testPackages, testRunnerAppDir, options) {
|
||||
// a switch to a different release
|
||||
appDirForVersionCheck: options.appDir,
|
||||
proxyPort: options.port,
|
||||
httpProxyPort: options.httpProxyPort,
|
||||
disableOplog: options['disable-oplog'],
|
||||
settingsFile: options.settings,
|
||||
banner: "Tests",
|
||||
@@ -1402,7 +1426,8 @@ var runTestAppForPackages = function (testPackages, testRunnerAppDir, options) {
|
||||
mongoUrl: process.env.MONGO_URL,
|
||||
oplogUrl: process.env.MONGO_OPLOG_URL,
|
||||
once: options.once,
|
||||
recordPackageUsage: false
|
||||
recordPackageUsage: false,
|
||||
extraRunners: options.extraRunners
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,9 +47,8 @@ if [ -z "$USE_GLOBAL_ADK" ] ; then
|
||||
export HOME="${ANDROID_BUNDLE}"
|
||||
export ANDROID_SDK_HOME="${ANDROID_BUNDLE}"
|
||||
else
|
||||
# to use a global ADK we don't set PATH, ANT_HOME
|
||||
# to use a global ADK we don't set PATH, ANT_HOME, ANDROID_SDK_HOME
|
||||
# relying that they are installed and available globally
|
||||
export ANDROID_SDK_HOME="${HOME}"
|
||||
true
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
BUNDLE_VERSION=0.1
|
||||
|
||||
# OS Check. Put here because here is where we download the precompiled
|
||||
@@ -92,11 +95,52 @@ command -v javac >/dev/null 2>&1 || {
|
||||
echo >&2 "To add the android platform, please install a JDK. Here are some directions: http://openjdk.java.net/install/"; exit 1;
|
||||
}
|
||||
|
||||
set_config () {
|
||||
KEY=$1
|
||||
VALUE=$2
|
||||
|
||||
CONFIG_FILE=${ANDROID_BUNDLE}/meteor_avd/config.ini
|
||||
|
||||
TEMP_FILE=`mktemp tmp.XXXXXXXXXX`
|
||||
grep -v "^${KEY}=" ${CONFIG_FILE} > ${TEMP_FILE}
|
||||
echo "${KEY}=${VALUE}" >> ${TEMP_FILE}
|
||||
mv -f ${TEMP_FILE} ${CONFIG_FILE}
|
||||
}
|
||||
|
||||
install_x86 () {
|
||||
echo "Android x86 System image not found. Found targets:"
|
||||
android list target
|
||||
echo "Downloading x86 system image..."
|
||||
echo y | android update sdk -t sys-img-x86-android-19 --all -u
|
||||
}
|
||||
|
||||
# create avd if necessary
|
||||
if [[ ! $("${ANDROID_BUNDLE}/android-sdk/tools/android" list avd | grep Name) ]] ; then
|
||||
#ABI="default/armeabi-v7a"
|
||||
ABI="default/x86"
|
||||
|
||||
(android list target | grep ABIs | grep default/x86 > /dev/null) || install_x86
|
||||
|
||||
echo "
|
||||
" | "${ANDROID_BUNDLE}/android-sdk/tools/android" create avd --target 1 --name meteor --abi default/armeabi-v7a --path ${ANDROID_BUNDLE}/meteor_avd/ 1>&2
|
||||
" | "${ANDROID_BUNDLE}/android-sdk/tools/android" create avd --target 1 --name meteor --abi ${ABI} --path ${ANDROID_BUNDLE}/meteor_avd/ 1>&2
|
||||
|
||||
# Nice keyboard support
|
||||
set_config "hw.keyboard" "yes"
|
||||
set_config "hw.mainKeys" "no"
|
||||
|
||||
# More RAM than the default
|
||||
set_config "hw.ramSize" "1024"
|
||||
set_config "vm.heapSize" "64"
|
||||
|
||||
# These are the settings for a Nexus 4, but it's a bit big for some screens
|
||||
# (and likely a bit slow without GPU & KVM/HAXM acceleration)
|
||||
#set_config "skin.dynamic" "yes"
|
||||
#set_config "hw.lcd.density" "320"
|
||||
#set_config "hw.device.name" "Nexus 4"
|
||||
#set_config "hw.device.manufacturer" "Google"
|
||||
|
||||
# XXX: hw.gpu.enabled=yes ?
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
9
tools/cordova-scripts/set_android_proxy.sh
Executable file
9
tools/cordova-scripts/set_android_proxy.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# import all the environment
|
||||
source $(dirname $0)/common_env.sh
|
||||
|
||||
adb shell sqlite3 /data/data/com.android.providers.telephony/databases/telephony.db \
|
||||
"update carriers set proxy='10.0.2.2', port='3002' where current=1"
|
||||
adb shell stop
|
||||
adb shell start
|
||||
@@ -5,6 +5,7 @@ var release = require('./release.js');
|
||||
|
||||
var runLog = require('./run-log.js');
|
||||
var Proxy = require('./run-proxy.js').Proxy;
|
||||
var HttpProxy = require('./run-httpproxy.js').HttpProxy;
|
||||
var AppRunner = require('./run-app.js').AppRunner;
|
||||
var MongoRunner = require('./run-mongo.js').MongoRunner;
|
||||
var Updater = require('./run-updater.js').Updater;
|
||||
@@ -36,6 +37,8 @@ var Runner = function (appDir, options) {
|
||||
self.rootUrl = 'http://localhost:' + listenPort + '/';
|
||||
}
|
||||
|
||||
self.extraRunners = options.extraRunners;
|
||||
|
||||
self.proxy = new Proxy({
|
||||
listenPort: listenPort,
|
||||
listenHost: options.proxyHost,
|
||||
@@ -44,6 +47,13 @@ var Runner = function (appDir, options) {
|
||||
onFailure: options.onFailure
|
||||
});
|
||||
|
||||
self.httpProxy = null;
|
||||
if (options.httpProxyPort) {
|
||||
self.httpProxy = new HttpProxy({
|
||||
listenPort: options.httpProxyPort
|
||||
})
|
||||
}
|
||||
|
||||
self.mongoRunner = null;
|
||||
var mongoUrl, oplogUrl;
|
||||
if (options.mongoUrl) {
|
||||
@@ -99,6 +109,14 @@ _.extend(Runner.prototype, {
|
||||
self.updater.start();
|
||||
}
|
||||
|
||||
// print the banner only once we've successfully bound the port
|
||||
if (! self.stopped && self.httpProxy) {
|
||||
self.httpProxy.start();
|
||||
if (! self.quiet) {
|
||||
runLog.log("=> Started http proxy.");
|
||||
}
|
||||
}
|
||||
|
||||
if (! self.stopped && self.mongoRunner) {
|
||||
var spinner = ['-', '\\', '|', '/'];
|
||||
// I looked at some Unicode indeterminate progress indicators, such as:
|
||||
@@ -136,6 +154,17 @@ _.extend(Runner.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
_.forEach(self.extraRunners, function (extraRunner) {
|
||||
if (! self.stopped) {
|
||||
var title = extraRunner.title;
|
||||
if (! self.quiet)
|
||||
runLog.logTemporary("=> Starting " + title + "...");
|
||||
extraRunner.start();
|
||||
if (! self.quiet && ! self.stopped)
|
||||
runLog.log("=> Started " + title + ".");
|
||||
}
|
||||
});
|
||||
|
||||
if (! self.stopped) {
|
||||
if (! self.quiet)
|
||||
runLog.logTemporary("=> Starting your app...");
|
||||
@@ -161,8 +190,12 @@ _.extend(Runner.prototype, {
|
||||
|
||||
self.stopped = true;
|
||||
self.proxy.stop();
|
||||
self.httpProxy && self.httpProxy.stop();
|
||||
self.updater.stop();
|
||||
self.mongoRunner && self.mongoRunner.stop();
|
||||
_.forEach(self.extraRunners, function (extraRunner) {
|
||||
extraRunner.stop();
|
||||
});
|
||||
self.appRunner.stop();
|
||||
// XXX does calling this 'finish' still make sense now that runLog is a
|
||||
// singleton?
|
||||
|
||||
280
tools/run-httpproxy.js
Normal file
280
tools/run-httpproxy.js
Normal file
@@ -0,0 +1,280 @@
|
||||
// The HTTP proxy is primarily so we can use localhost:3000 with OAuth,
|
||||
// on devices which don't run a webserver e.g. Android / iOS
|
||||
// This is a generic HTTP proxy, like a mini-Squid
|
||||
// (whereas run-proxy.js is just for our app)
|
||||
var _ = require('underscore');
|
||||
var Future = require('fibers/future');
|
||||
var runLog = require('./run-log.js');
|
||||
var url = require('url');
|
||||
|
||||
// options: listenPort, listenHost, onFailure
|
||||
var HttpProxy = function (options) {
|
||||
var self = this;
|
||||
|
||||
self.listenPort = options.listenPort;
|
||||
self.listenHost = options.listenHost;
|
||||
|
||||
self.onFailure = options.onFailure || function () {};
|
||||
|
||||
self.mode = "proxy";
|
||||
self.httpQueue = []; // keys: req, res
|
||||
self.websocketQueue = []; // keys: req, socket, head
|
||||
self.connectQueue = []; // keys: req, socket, head
|
||||
|
||||
self.proxy = null;
|
||||
self.server = null;
|
||||
};
|
||||
|
||||
_.extend(HttpProxy.prototype, {
|
||||
// Start the proxy server, block (yield) until it is ready to go
|
||||
// (actively listening on outer and proxying to inner), and then
|
||||
// return.
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
if (self.server)
|
||||
throw new Error("already running?");
|
||||
|
||||
self.started = false;
|
||||
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
var httpProxy = require('http-proxy');
|
||||
|
||||
self.proxy = httpProxy.createProxyServer({
|
||||
// agent is required to handle keep-alive, and http-proxy 1.0 is a little
|
||||
// buggy without it: https://github.com/nodejitsu/node-http-proxy/pull/488
|
||||
agent: new http.Agent({ maxSockets: 100 }),
|
||||
xfwd: false //true
|
||||
});
|
||||
|
||||
var server = self.server = http.createServer(function (req, res) {
|
||||
// Normal HTTP request
|
||||
self.httpQueue.push({ req: req, res: res });
|
||||
self._tryHandleConnections();
|
||||
});
|
||||
|
||||
self.server.on('connect', function (req, socket, head) {
|
||||
self.connectQueue.push({ req: req, socket: socket, head: head });
|
||||
self._tryHandleConnections();
|
||||
});
|
||||
|
||||
self.server.on('upgrade', function (req, socket, head) {
|
||||
// Websocket connection
|
||||
self.websocketQueue.push({ req: req, socket: socket, head: head });
|
||||
self._tryHandleConnections();
|
||||
});
|
||||
|
||||
var fut = new Future;
|
||||
self.server.on('error', function (err) {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
var port = self.listenPort;
|
||||
runLog.log(
|
||||
"HTTP proxy server can't listen on port " + port + ". \n" +
|
||||
"If something else is using port " + port + ", you can\n" +
|
||||
"specify an alternative port with --http-proxy-port <port>.");
|
||||
} else if (self.listenHost &&
|
||||
(err.code === 'ENOTFOUND' || err.code === 'EADDRNOTAVAIL')) {
|
||||
// This handles the case of "entered a DNS name that's unknown"
|
||||
// (ENOTFOUND from getaddrinfo) and "entered some random IP that we
|
||||
// can't bind to" (EADDRNOTAVAIL from listen).
|
||||
runLog.log(
|
||||
"Can't listen on host " + self.listenHost +
|
||||
" (" + err.code + " from " + err.syscall + ").");
|
||||
} else {
|
||||
runLog.log('' + err);
|
||||
}
|
||||
self.onFailure();
|
||||
// Allow start() to return.
|
||||
fut.isResolved() || fut['return']();
|
||||
});
|
||||
|
||||
// Don't crash if the app doesn't respond; instead return an error
|
||||
// immediately.
|
||||
self.proxy.on('error', function (err, req, resOrSocket) {
|
||||
if (resOrSocket instanceof http.ServerResponse) {
|
||||
resOrSocket.writeHead(503, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
resOrSocket.end('Unexpected error.');
|
||||
} else if (resOrSocket instanceof net.Socket) {
|
||||
resOrSocket.end();
|
||||
}
|
||||
});
|
||||
|
||||
self.server.listen(self.listenPort, self.listenHost || '0.0.0.0', function () {
|
||||
if (self.server) {
|
||||
self.started = true;
|
||||
} else {
|
||||
// stop() got called while we were invoking listen! Close the server (we
|
||||
// still have the var server). The rest of the cleanup shouldn't be
|
||||
// necessary.
|
||||
server.close();
|
||||
}
|
||||
fut.isResolved() || fut['return']();
|
||||
});
|
||||
|
||||
fut.wait();
|
||||
},
|
||||
|
||||
// Idempotent.
|
||||
stop: function () {
|
||||
var self = this;
|
||||
|
||||
if (! self.server)
|
||||
return;
|
||||
|
||||
if (! self.started) {
|
||||
// This probably means that we failed to listen. However, there could be a
|
||||
// race condition and we could be in the middle of starting to listen! In
|
||||
// that case, the listen callback will notice that we nulled out server
|
||||
// here.
|
||||
self.server = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// This stops listening but allows existing connections to
|
||||
// complete gracefully.
|
||||
self.server.close();
|
||||
self.server = null;
|
||||
|
||||
// It doesn't seem to be necessary to do anything special to
|
||||
// destroy an httpProxy proxyserver object.
|
||||
self.proxy = null;
|
||||
|
||||
// Drop any held connections.
|
||||
_.each(self.httpQueue, function (c) {
|
||||
c.res.statusCode = 500;
|
||||
c.res.end();
|
||||
});
|
||||
self.httpQueue = [];
|
||||
|
||||
_.each(self.websocketQueue, function (c) {
|
||||
c.socket.destroy();
|
||||
});
|
||||
self.websocketQueue = [];
|
||||
|
||||
_.each(self.connectQueue, function (c) {
|
||||
c.socket.destroy();
|
||||
});
|
||||
self.connectQueue = [];
|
||||
|
||||
self.mode = "hold";
|
||||
},
|
||||
|
||||
_tryHandleConnections: function () {
|
||||
var self = this;
|
||||
|
||||
while (self.httpQueue.length) {
|
||||
if (self.mode !== "proxy")
|
||||
break;
|
||||
|
||||
var c = self.httpQueue.shift();
|
||||
var req = c.req;
|
||||
var targetUrl = req.url;
|
||||
runLog.log("Proxy request: " + req.method + " " +req.url);
|
||||
var newUrl = req.url
|
||||
self.proxy.web(c.req, c.res, {
|
||||
target: targetUrl
|
||||
});
|
||||
}
|
||||
|
||||
while (self.websocketQueue.length) {
|
||||
if (self.mode !== "proxy")
|
||||
break;
|
||||
|
||||
var c = self.websocketQueue.shift();
|
||||
var req = c.req;
|
||||
var targetUrl = req.url;
|
||||
runLog.log("Proxy request (websocket): " + req.method + " " +req.url);
|
||||
self.proxy.ws(c.req, c.socket, c.head, {
|
||||
target: targetUrl
|
||||
});
|
||||
}
|
||||
|
||||
while (self.connectQueue.length) {
|
||||
if (self.mode !== "proxy")
|
||||
break;
|
||||
|
||||
var c = self.connectQueue.shift();
|
||||
runLog.log("Proxy request (connect): " + c.req.method + " " + c.req.url);
|
||||
proxyConnectMethod(c.req, c.socket, c.head);
|
||||
}
|
||||
},
|
||||
|
||||
// The proxy can be in one of three modes:
|
||||
// - "proxy": connections are proxied
|
||||
//
|
||||
// The initial mode is "proxy".
|
||||
setMode: function (mode) {
|
||||
var self = this;
|
||||
self.mode = mode;
|
||||
self._tryHandleConnections();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// This is what http-proxy does
|
||||
// XXX: We should submit connect support upstream
|
||||
var setupSocket = function(socket) {
|
||||
socket.setTimeout(0);
|
||||
socket.setNoDelay(true);
|
||||
|
||||
socket.setKeepAlive(true, 0);
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
|
||||
var proxyConnectMethod = function (req, socket, options, head, server, clb) {
|
||||
if (req.method !== 'CONNECT') {
|
||||
socket.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
var tokens = req.url.split(':');
|
||||
|
||||
if (tokens.length != 2) {
|
||||
runLog.log("Bad request: " + req.url);
|
||||
socket.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
var host = tokens[0];
|
||||
var port = tokens[1];
|
||||
|
||||
if (port != 443) {
|
||||
runLog.log("Blocking request to non-443 port: " + req.url);
|
||||
socket.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
setupSocket(socket);
|
||||
|
||||
// XXX: Needed?
|
||||
// if (head && head.length) socket.unshift(head);
|
||||
|
||||
var net = require('net');
|
||||
var proxySocket = net.createConnection(port, host);
|
||||
setupSocket(proxySocket);
|
||||
|
||||
socket.on('error', function (err) {
|
||||
runLog.log("Error on socket: " + err);
|
||||
proxySocket.end();
|
||||
});
|
||||
proxySocket.on('error', function (err) {
|
||||
runLog.log("Error on proxySocket: " + err);
|
||||
socket.end();
|
||||
});
|
||||
|
||||
proxySocket.on('connect', function(connect) {
|
||||
runLog.log("Connection established to " + host + ":" + port);
|
||||
socket.write("HTTP/1.0 200 Connection established\n\n");
|
||||
socket.pipe(proxySocket);
|
||||
proxySocket.pipe(socket);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
exports.HttpProxy = HttpProxy;
|
||||
Reference in New Issue
Block a user