mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1059001 - Part 3: Add encryption socket option. r=past
This commit is contained in:
parent
39b5349e47
commit
46bbcd61dd
@ -183,6 +183,7 @@ let WiFiRemoteDebugger = {
|
||||
this._listener.portOrPath = -1 /* any available port */;
|
||||
this._listener.allowConnection = RemoteDebugger.prompt;
|
||||
this._listener.discoverable = true;
|
||||
this._listener.encryption = true;
|
||||
this._listener.open();
|
||||
let port = this._listener.port;
|
||||
debug("Started WiFi debugger on " + port);
|
||||
|
@ -41,14 +41,18 @@ window.addEventListener("DOMContentLoaded", function onDOMReady() {
|
||||
|
||||
let form = document.querySelector("#connection-form form");
|
||||
form.addEventListener("submit", function() {
|
||||
window.submit();
|
||||
window.submit().catch(e => {
|
||||
Cu.reportError(e);
|
||||
// Bug 921850: catch rare exception from DebuggerClient.socketConnect
|
||||
showError("unexpected");
|
||||
});
|
||||
});
|
||||
}, true);
|
||||
|
||||
/**
|
||||
* Called when the "connect" button is clicked.
|
||||
*/
|
||||
function submit() {
|
||||
let submit = Task.async(function*() {
|
||||
// Show the "connecting" screen
|
||||
document.body.classList.add("connecting");
|
||||
|
||||
@ -64,18 +68,18 @@ function submit() {
|
||||
}
|
||||
|
||||
// Initiate the connection
|
||||
let transport;
|
||||
try {
|
||||
transport = DebuggerClient.socketConnect(host, port);
|
||||
} catch(e) {
|
||||
// Bug 921850: catch rare exception from DebuggerClient.socketConnect
|
||||
showError("unexpected");
|
||||
return;
|
||||
}
|
||||
let transport = yield DebuggerClient.socketConnect({ host, port });
|
||||
gClient = new DebuggerClient(transport);
|
||||
let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
|
||||
gConnectionTimeout = setTimeout(handleConnectionTimeout, delay);
|
||||
gClient.connect(onConnectionReady);
|
||||
let response = yield clientConnect();
|
||||
yield onConnectionReady(...response);
|
||||
});
|
||||
|
||||
function clientConnect() {
|
||||
let deferred = promise.defer();
|
||||
gClient.connect((...args) => deferred.resolve(args));
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,6 +12,7 @@ let { DebuggerClient } =
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
let { ViewHelpers } =
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm", {});
|
||||
let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
/**
|
||||
* Shortcuts for accessing various debugger preferences.
|
||||
@ -23,13 +24,13 @@ let Prefs = new ViewHelpers.Prefs("devtools.debugger", {
|
||||
|
||||
let gToolbox, gClient;
|
||||
|
||||
function connect() {
|
||||
let connect = Task.async(function*() {
|
||||
window.removeEventListener("load", connect);
|
||||
// Initiate the connection
|
||||
let transport = DebuggerClient.socketConnect(
|
||||
Prefs.chromeDebuggingHost,
|
||||
Prefs.chromeDebuggingPort
|
||||
);
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: Prefs.chromeDebuggingHost,
|
||||
port: Prefs.chromeDebuggingPort
|
||||
});
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect(() => {
|
||||
let addonID = getParameterByName("addonID");
|
||||
@ -43,7 +44,7 @@ function connect() {
|
||||
gClient.listTabs(openToolbox);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Certain options should be toggled since we can assume chrome debugging here
|
||||
function setPrefDefaults() {
|
||||
@ -56,7 +57,7 @@ window.addEventListener("load", function() {
|
||||
let cmdClose = document.getElementById("toolbox-cmd-close");
|
||||
cmdClose.addEventListener("command", onCloseCommand);
|
||||
setPrefDefaults();
|
||||
connect();
|
||||
connect().catch(Cu.reportError);
|
||||
});
|
||||
|
||||
function onCloseCommand(event) {
|
||||
|
@ -799,6 +799,8 @@ pref("devtools.remote.wifi.scan", false);
|
||||
// N.B.: This does not set whether the device can be discovered via WiFi, only
|
||||
// whether the UI control to make such a choice is shown to the user
|
||||
pref("devtools.remote.wifi.visible", false);
|
||||
// Client must complete TLS handshake within this window (ms)
|
||||
pref("devtools.remote.tls-handshake-timeout", 10000);
|
||||
|
||||
// view source
|
||||
pref("view_source.syntax_highlight", true);
|
||||
|
@ -28,8 +28,12 @@ function connect(onDone) {
|
||||
let observer = {
|
||||
observe: function (subject, topic, data) {
|
||||
Services.obs.removeObserver(observer, "debugger-server-started");
|
||||
let transport = DebuggerClient.socketConnect("127.0.0.1", 6000);
|
||||
startClient(transport, onDone);
|
||||
DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: 6000
|
||||
}).then(transport => {
|
||||
startClient(transport, onDone);
|
||||
}, e => dump("Connection failed: " + e + "\n"));
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "debugger-server-started", false);
|
||||
|
@ -9,10 +9,13 @@
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
const {setTimeout, clearTimeout} = require('sdk/timers');
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
DevToolsUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
/**
|
||||
* Connection Manager.
|
||||
@ -48,7 +51,8 @@ Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
* . port Port
|
||||
* . logs Current logs. "newlog" event notifies new available logs
|
||||
* . store Reference to a local data store (see below)
|
||||
* . keepConnecting Should the connection keep trying connecting
|
||||
* . keepConnecting Should the connection keep trying to connect?
|
||||
* . encryption Should the connection be encrypted?
|
||||
* . status Connection status:
|
||||
* Connection.Status.CONNECTED
|
||||
* Connection.Status.DISCONNECTED
|
||||
@ -113,6 +117,7 @@ function Connection(host, port) {
|
||||
this._onConnected = this._onConnected.bind(this);
|
||||
this._onTimeout = this._onTimeout.bind(this);
|
||||
this.keepConnecting = false;
|
||||
this.encryption = false;
|
||||
}
|
||||
|
||||
Connection.Status = {
|
||||
@ -222,30 +227,38 @@ Connection.prototype = {
|
||||
this._setStatus(Connection.Status.DESTROYED);
|
||||
},
|
||||
|
||||
_clientConnect: function () {
|
||||
let transport;
|
||||
_getTransport: Task.async(function*() {
|
||||
if (this._customTransport) {
|
||||
transport = this._customTransport;
|
||||
} else {
|
||||
if (!this.host) {
|
||||
transport = DebuggerServer.connectPipe();
|
||||
} else {
|
||||
try {
|
||||
transport = DebuggerClient.socketConnect(this.host, this.port);
|
||||
} catch (e) {
|
||||
// In some cases, especially on Mac, the openOutputStream call in
|
||||
// DebuggerClient.socketConnect may throw NS_ERROR_NOT_INITIALIZED.
|
||||
// It occurs when we connect agressively to the simulator,
|
||||
// and keep trying to open a socket to the server being started in
|
||||
// the simulator.
|
||||
this._onDisconnected();
|
||||
return;
|
||||
}
|
||||
}
|
||||
return this._customTransport;
|
||||
}
|
||||
this._client = new DebuggerClient(transport);
|
||||
this._client.addOneTimeListener("closed", this._onDisconnected);
|
||||
this._client.connect(this._onConnected);
|
||||
if (!this.host) {
|
||||
return DebuggerServer.connectPipe();
|
||||
}
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
encryption: this.encryption
|
||||
});
|
||||
return transport;
|
||||
}),
|
||||
|
||||
_clientConnect: function () {
|
||||
this._getTransport().then(transport => {
|
||||
if (!transport) {
|
||||
return;
|
||||
}
|
||||
this._client = new DebuggerClient(transport);
|
||||
this._client.addOneTimeListener("closed", this._onDisconnected);
|
||||
this._client.connect(this._onConnected);
|
||||
}, e => {
|
||||
console.error(e);
|
||||
// In some cases, especially on Mac, the openOutputStream call in
|
||||
// DebuggerClient.socketConnect may throw NS_ERROR_NOT_INITIALIZED.
|
||||
// It occurs when we connect agressively to the simulator,
|
||||
// and keep trying to open a socket to the server being started in
|
||||
// the simulator.
|
||||
this._onDisconnected();
|
||||
});
|
||||
},
|
||||
|
||||
get status() {
|
||||
|
@ -372,9 +372,9 @@ DebuggerClient.Argument.prototype.getArgument = function (aParams) {
|
||||
};
|
||||
|
||||
// Expose this to save callers the trouble of importing DebuggerSocket
|
||||
DebuggerClient.socketConnect = function(host, port) {
|
||||
DebuggerClient.socketConnect = function(options) {
|
||||
// Defined here instead of just copying the function to allow lazy-load
|
||||
return DebuggerSocket.connect(host, port);
|
||||
return DebuggerSocket.connect(options);
|
||||
};
|
||||
|
||||
DebuggerClient.prototype = {
|
||||
|
@ -19,6 +19,7 @@
|
||||
var Cu = require('chrome').Cu;
|
||||
|
||||
var DebuggerClient = Cu.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
|
||||
var { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
var Promise = require('../util/promise').Promise;
|
||||
var Connection = require('./connectors').Connection;
|
||||
@ -61,7 +62,7 @@ function RdpConnection(url) {
|
||||
/**
|
||||
* Asynchronous construction
|
||||
*/
|
||||
RdpConnection.create = function(url) {
|
||||
RdpConnection.create = Task.async(function*(url) {
|
||||
this.host = url;
|
||||
this.port = undefined; // TODO: Split out the port number
|
||||
|
||||
@ -70,9 +71,13 @@ RdpConnection.create = function(url) {
|
||||
|
||||
this._emit = this._emit.bind(this);
|
||||
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: this.host,
|
||||
port: this.port
|
||||
});
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
this.transport = DebuggerClient.socketConnect(this.host, this.port);
|
||||
this.client = new DebuggerClient(this.transport);
|
||||
this.client = new DebuggerClient(transport);
|
||||
this.client.connect(function() {
|
||||
this.client.listTabs(function(response) {
|
||||
this.actor = response.gcliActor;
|
||||
@ -80,7 +85,7 @@ RdpConnection.create = function(url) {
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
};
|
||||
});
|
||||
|
||||
RdpConnection.prototype = Object.create(Connection.prototype);
|
||||
|
||||
|
66
toolkit/devtools/security/cert.js
Normal file
66
toolkit/devtools/security/cert.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let { Ci, Cc } = require("chrome");
|
||||
let promise = require("promise");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
DevToolsUtils.defineLazyGetter(this, "localCertService", () => {
|
||||
// Ensure PSM is initialized to support TLS sockets
|
||||
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
||||
return Cc["@mozilla.org/security/local-cert-service;1"]
|
||||
.getService(Ci.nsILocalCertService);
|
||||
});
|
||||
|
||||
const localCertName = "devtools";
|
||||
|
||||
exports.local = {
|
||||
|
||||
/**
|
||||
* Get or create a new self-signed X.509 cert to represent this device for
|
||||
* DevTools purposes over a secure transport, like TLS.
|
||||
*
|
||||
* The cert is stored permanently in the profile's key store after first use,
|
||||
* and is valid for 1 year. If an expired or otherwise invalid cert is found,
|
||||
* it is removed and a new one is made.
|
||||
*
|
||||
* @return promise
|
||||
*/
|
||||
getOrCreate() {
|
||||
let deferred = promise.defer();
|
||||
localCertService.getOrCreateCert(localCertName, {
|
||||
handleCert: function(cert, rv) {
|
||||
if (rv) {
|
||||
deferred.reject(rv);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(cert);
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the DevTools self-signed X.509 cert for this device.
|
||||
*
|
||||
* @return promise
|
||||
*/
|
||||
remove() {
|
||||
let deferred = promise.defer();
|
||||
localCertService.removeCert(localCertName, {
|
||||
handleCert: function(rv) {
|
||||
if (rv) {
|
||||
deferred.reject(rv);
|
||||
return;
|
||||
}
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
};
|
@ -21,5 +21,6 @@ FAIL_ON_WARNINGS = True
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
EXTRA_JS_MODULES.devtools.security += [
|
||||
'cert.js',
|
||||
'socket.js',
|
||||
]
|
||||
|
@ -7,27 +7,24 @@
|
||||
"use strict";
|
||||
|
||||
let { Ci, Cc, CC, Cr } = require("chrome");
|
||||
|
||||
// Ensure PSM is initialized to support TLS sockets
|
||||
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
||||
|
||||
let Services = require("Services");
|
||||
let promise = require("promise");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dumpn } = DevToolsUtils;
|
||||
let { dumpn, dumpv } = DevToolsUtils;
|
||||
loader.lazyRequireGetter(this, "DebuggerTransport",
|
||||
"devtools/toolkit/transport/transport", true);
|
||||
loader.lazyRequireGetter(this, "DebuggerServer",
|
||||
"devtools/server/main", true);
|
||||
loader.lazyRequireGetter(this, "discovery",
|
||||
"devtools/toolkit/discovery/discovery");
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "ServerSocket", () => {
|
||||
return CC("@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"initSpecialConnection");
|
||||
});
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "UnixDomainServerSocket", () => {
|
||||
return CC("@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"initWithFilename");
|
||||
});
|
||||
loader.lazyRequireGetter(this, "cert",
|
||||
"devtools/toolkit/security/cert");
|
||||
loader.lazyRequireGetter(this, "setTimeout", "Timer", true);
|
||||
loader.lazyRequireGetter(this, "clearTimeout", "Timer", true);
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
|
||||
return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
|
||||
@ -38,18 +35,111 @@ DevToolsUtils.defineLazyGetter(this, "socketTransportService", () => {
|
||||
.getService(Ci.nsISocketTransportService);
|
||||
});
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "certOverrideService", () => {
|
||||
return Cc["@mozilla.org/security/certoverride;1"]
|
||||
.getService(Ci.nsICertOverrideService);
|
||||
});
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "nssErrorsService", () => {
|
||||
return Cc["@mozilla.org/nss_errors_service;1"]
|
||||
.getService(Ci.nsINSSErrorsService);
|
||||
});
|
||||
|
||||
DevToolsUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
|
||||
|
||||
let DebuggerSocket = {};
|
||||
|
||||
/**
|
||||
* Connects to a debugger server socket and returns a DebuggerTransport.
|
||||
* Connects to a debugger server socket.
|
||||
*
|
||||
* @param host string
|
||||
* The host name or IP address of the debugger server.
|
||||
* @param port number
|
||||
* The port number of the debugger server.
|
||||
* @param encryption boolean (optional)
|
||||
* Whether the server requires encryption. Defaults to false.
|
||||
* @return promise
|
||||
* Resolved to a DebuggerTransport instance.
|
||||
*/
|
||||
function socketConnect(host, port) {
|
||||
let s = socketTransportService.createTransport(null, 0, host, port, null);
|
||||
DebuggerSocket.connect = Task.async(function*({ host, port, encryption }) {
|
||||
let attempt = yield _attemptTransport({ host, port, encryption });
|
||||
if (attempt.transport) {
|
||||
return attempt.transport; // Success
|
||||
}
|
||||
|
||||
// If the server cert failed validation, store a temporary override and make
|
||||
// a second attempt.
|
||||
if (encryption && attempt.certError) {
|
||||
_storeCertOverride(attempt.s, host, port);
|
||||
} else {
|
||||
throw new Error("Connection failed");
|
||||
}
|
||||
|
||||
attempt = yield _attemptTransport({ host, port, encryption });
|
||||
if (attempt.transport) {
|
||||
return attempt.transport; // Success
|
||||
}
|
||||
|
||||
throw new Error("Connection failed even after cert override");
|
||||
});
|
||||
|
||||
/**
|
||||
* Try to connect and create a DevTools transport.
|
||||
*
|
||||
* @return transport DebuggerTransport
|
||||
* A possible DevTools transport (if connection succeeded and streams
|
||||
* are actually alive and working)
|
||||
* @return certError boolean
|
||||
* Flag noting if cert trouble caused the streams to fail
|
||||
* @return s nsISocketTransport
|
||||
* Underlying socket transport, in case more details are needed.
|
||||
*/
|
||||
let _attemptTransport = Task.async(function*({ host, port, encryption }){
|
||||
// _attemptConnect only opens the streams. Any failures at that stage
|
||||
// aborts the connection process immedidately.
|
||||
let { s, input, output } = _attemptConnect({ host, port, encryption });
|
||||
|
||||
// Check if the input stream is alive. If encryption is enabled, we need to
|
||||
// watch out for cert errors by testing the input stream.
|
||||
let { alive, certError } = yield _isInputAlive(input);
|
||||
dumpv("Server cert accepted? " + !certError);
|
||||
|
||||
let transport;
|
||||
if (alive) {
|
||||
transport = new DebuggerTransport(input, output);
|
||||
} else {
|
||||
// Something went wrong, close the streams.
|
||||
input.close();
|
||||
output.close();
|
||||
}
|
||||
|
||||
return { transport, certError, s };
|
||||
});
|
||||
|
||||
/**
|
||||
* Try to connect to a remote server socket.
|
||||
*
|
||||
* If successsful, the socket transport and its opened streams are returned.
|
||||
* Typically, this will only fail if the host / port is unreachable. Other
|
||||
* problems, such as security errors, will allow this stage to succeed, but then
|
||||
* fail later when the streams are actually used.
|
||||
* @return s nsISocketTransport
|
||||
* Underlying socket transport, in case more details are needed.
|
||||
* @return input nsIAsyncInputStream
|
||||
* The socket's input stream.
|
||||
* @return output nsIAsyncOutputStream
|
||||
* The socket's output stream.
|
||||
*/
|
||||
function _attemptConnect({ host, port, encryption }) {
|
||||
let s;
|
||||
if (encryption) {
|
||||
s = socketTransportService.createTransport(["ssl"], 1, host, port, null);
|
||||
} else {
|
||||
s = socketTransportService.createTransport(null, 0, host, port, null);
|
||||
}
|
||||
// By default the CONNECT socket timeout is very long, 65535 seconds,
|
||||
// so that if we race to be in CONNECT state while the server socket is still
|
||||
// initializing, the connection is stuck in connecting state for 18.20 hours!
|
||||
@ -58,15 +148,61 @@ function socketConnect(host, port) {
|
||||
// openOutputStream may throw NS_ERROR_NOT_INITIALIZED if we hit some race
|
||||
// where the nsISocketTransport gets shutdown in between its instantiation and
|
||||
// the call to this method.
|
||||
let transport;
|
||||
let input;
|
||||
let output;
|
||||
try {
|
||||
transport = new DebuggerTransport(s.openInputStream(0, 0, 0),
|
||||
s.openOutputStream(0, 0, 0));
|
||||
input = s.openInputStream(0, 0, 0);
|
||||
output = s.openOutputStream(0, 0, 0);
|
||||
} catch(e) {
|
||||
DevToolsUtils.reportException("socketConnect", e);
|
||||
DevToolsUtils.reportException("_attemptConnect", e);
|
||||
throw e;
|
||||
}
|
||||
return transport;
|
||||
|
||||
return { s, input, output };
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the input stream is alive. For an encrypted connection, it may not
|
||||
* be if the client refuses the server's cert. A cert error is expected on
|
||||
* first connection to a new host because the cert is self-signed.
|
||||
*/
|
||||
function _isInputAlive(input) {
|
||||
let deferred = promise.defer();
|
||||
input.asyncWait({
|
||||
onInputStreamReady(stream) {
|
||||
try {
|
||||
stream.available();
|
||||
deferred.resolve({ alive: true });
|
||||
} catch (e) {
|
||||
try {
|
||||
// getErrorClass may throw if you pass a non-NSS error
|
||||
let errorClass = nssErrorsService.getErrorClass(e.result);
|
||||
if (errorClass === Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
|
||||
deferred.resolve({ certError: true });
|
||||
} else {
|
||||
deferred.reject(e);
|
||||
}
|
||||
} catch (nssErr) {
|
||||
deferred.reject(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 0, 0, Services.tm.currentThread);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* To allow the connection to proceed with self-signed cert, we store a cert
|
||||
* override. This implies that we take on the burden of authentication for
|
||||
* these connections.
|
||||
*/
|
||||
function _storeCertOverride(s, host, port) {
|
||||
let cert = s.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
|
||||
.SSLStatus.serverCert;
|
||||
let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
|
||||
Ci.nsICertOverrideService.ERROR_MISMATCH;
|
||||
certOverrideService.rememberValidityOverride(host, port, cert, overrideBits,
|
||||
true /* temporary */);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,6 +269,11 @@ SocketListener.prototype = {
|
||||
*/
|
||||
discoverable: false,
|
||||
|
||||
/**
|
||||
* Controls whether this listener's transport uses encryption.
|
||||
*/
|
||||
encryption: false,
|
||||
|
||||
/**
|
||||
* Validate that all options have been set to a supported configuration.
|
||||
*/
|
||||
@ -158,31 +299,53 @@ SocketListener.prototype = {
|
||||
flags |= Ci.nsIServerSocket.LoopbackOnly;
|
||||
}
|
||||
|
||||
try {
|
||||
let self = this;
|
||||
return Task.spawn(function*() {
|
||||
let backlog = 4;
|
||||
let port = Number(this.portOrPath);
|
||||
if (port) {
|
||||
this._socket = new ServerSocket(port, flags, backlog);
|
||||
self._socket = self._createSocketInstance();
|
||||
if (self.isPortBased) {
|
||||
let port = Number(self.portOrPath);
|
||||
self._socket.initSpecialConnection(port, flags, backlog);
|
||||
} else {
|
||||
let file = nsFile(this.portOrPath);
|
||||
if (file.exists())
|
||||
let file = nsFile(self.portOrPath);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
this._socket = new UnixDomainServerSocket(file, parseInt("666", 8),
|
||||
backlog);
|
||||
}
|
||||
self._socket.initWithFilename(file, parseInt("666", 8), backlog);
|
||||
}
|
||||
this._socket.asyncListen(this);
|
||||
} catch (e) {
|
||||
yield self._setAdditionalSocketOptions();
|
||||
self._socket.asyncListen(self);
|
||||
dumpn("Socket listening on: " + (self.port || self.portOrPath));
|
||||
}).then(() => {
|
||||
if (this.discoverable && this.port) {
|
||||
discovery.addService("devtools", { port: this.port });
|
||||
}
|
||||
}).catch(e => {
|
||||
dumpn("Could not start debugging listener on '" + this.portOrPath +
|
||||
"': " + e);
|
||||
this.close();
|
||||
throw Cr.NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (this.discoverable && this.port) {
|
||||
discovery.addService("devtools", { port: this.port });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_createSocketInstance: function() {
|
||||
if (this.encryption) {
|
||||
return Cc["@mozilla.org/network/tls-server-socket;1"]
|
||||
.createInstance(Ci.nsITLSServerSocket);
|
||||
}
|
||||
return Cc["@mozilla.org/network/server-socket;1"]
|
||||
.createInstance(Ci.nsIServerSocket);
|
||||
},
|
||||
|
||||
_setAdditionalSocketOptions: Task.async(function*() {
|
||||
if (this.encryption) {
|
||||
this._socket.serverCert = yield cert.local.getOrCreate();
|
||||
this._socket.setSessionCache(false);
|
||||
this._socket.setSessionTickets(false);
|
||||
let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
|
||||
this._socket.setRequestClientCertificate(requestCert);
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Closes the SocketListener. Notifies the server to remove the listener from
|
||||
* the set of active SocketListeners.
|
||||
@ -198,12 +361,19 @@ SocketListener.prototype = {
|
||||
DebuggerServer._removeListener(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets whether this listener uses a port number vs. a path.
|
||||
*/
|
||||
get isPortBased() {
|
||||
return !!Number(this.portOrPath);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the port that a TCP socket listener is listening on, or null if this
|
||||
* is not a TCP socket (so there is no port).
|
||||
*/
|
||||
get port() {
|
||||
if (!this._socket) {
|
||||
if (!this.isPortBased || !this._socket) {
|
||||
return null;
|
||||
}
|
||||
return this._socket.port;
|
||||
@ -213,6 +383,9 @@ SocketListener.prototype = {
|
||||
|
||||
onSocketAccepted:
|
||||
DevToolsUtils.makeInfallible(function(socket, socketTransport) {
|
||||
if (this.encryption) {
|
||||
new SecurityObserver(socketTransport);
|
||||
}
|
||||
if (Services.prefs.getBoolPref("devtools.debugger.prompt-connection") &&
|
||||
!this.allowConnection()) {
|
||||
return;
|
||||
@ -232,13 +405,63 @@ SocketListener.prototype = {
|
||||
|
||||
};
|
||||
|
||||
// TODO: These high-level entry points will branch based on TLS vs. bare TCP as
|
||||
// part of bug 1059001.
|
||||
exports.DebuggerSocket = {
|
||||
createListener() {
|
||||
return new SocketListener();
|
||||
// Client must complete TLS handshake within this window (ms)
|
||||
loader.lazyGetter(this, "HANDSHAKE_TIMEOUT", () => {
|
||||
return Services.prefs.getIntPref("devtools.remote.tls-handshake-timeout");
|
||||
});
|
||||
|
||||
function SecurityObserver(socketTransport) {
|
||||
this.socketTransport = socketTransport;
|
||||
let connectionInfo = socketTransport.securityInfo
|
||||
.QueryInterface(Ci.nsITLSServerConnectionInfo);
|
||||
connectionInfo.setSecurityObserver(this);
|
||||
this._handshakeTimeout = setTimeout(this._onHandshakeTimeout.bind(this),
|
||||
HANDSHAKE_TIMEOUT);
|
||||
}
|
||||
|
||||
SecurityObserver.prototype = {
|
||||
|
||||
_onHandshakeTimeout() {
|
||||
dumpv("Client failed to complete handshake");
|
||||
this.destroy(Cr.NS_ERROR_NET_TIMEOUT);
|
||||
},
|
||||
connect(host, port) {
|
||||
return socketConnect(host, port);
|
||||
|
||||
// nsITLSServerSecurityObserver implementation
|
||||
onHandshakeDone(socket, clientStatus) {
|
||||
clearTimeout(this._handshakeTimeout);
|
||||
dumpv("TLS version: " + clientStatus.tlsVersionUsed.toString(16));
|
||||
dumpv("TLS cipher: " + clientStatus.cipherName);
|
||||
dumpv("TLS key length: " + clientStatus.keyLength);
|
||||
dumpv("TLS MAC length: " + clientStatus.macLength);
|
||||
/*
|
||||
* TODO: These rules should be really be set on the TLS socket directly, but
|
||||
* this would need more platform work to expose it via XPCOM.
|
||||
*
|
||||
* Server *will* send hello packet when any rules below are not met, but the
|
||||
* socket then closes after that.
|
||||
*
|
||||
* Enforcing cipher suites here would be a bad idea, as we want TLS
|
||||
* cipher negotiation to work correctly. The server already allows only
|
||||
* Gecko's normal set of cipher suites.
|
||||
*/
|
||||
if (clientStatus.tlsVersionUsed != Ci.nsITLSClientStatus.TLS_VERSION_1_2) {
|
||||
this.destroy(Cr.NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
},
|
||||
|
||||
destroy(result) {
|
||||
clearTimeout(this._handshakeTimeout);
|
||||
let connectionInfo = this.socketTransport.securityInfo
|
||||
.QueryInterface(Ci.nsITLSServerConnectionInfo);
|
||||
connectionInfo.setSecurityObserver(null);
|
||||
this.socketTransport.close(result);
|
||||
this.socketTransport = null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
DebuggerSocket.createListener = function() {
|
||||
return new SocketListener();
|
||||
};
|
||||
|
||||
exports.DebuggerSocket = DebuggerSocket;
|
||||
|
107
toolkit/devtools/security/tests/unit/head_dbg.js
Normal file
107
toolkit/devtools/security/tests/unit/head_dbg.js
Normal file
@ -0,0 +1,107 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
const CC = Components.Constructor;
|
||||
|
||||
const { devtools } =
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const { Promise: promise } =
|
||||
Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
const Services = devtools.require("Services");
|
||||
const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
|
||||
const xpcInspector = devtools.require("xpcInspector");
|
||||
|
||||
// We do not want to log packets by default, because in some tests,
|
||||
// we can be sending large amounts of data. The test harness has
|
||||
// trouble dealing with logging all the data, and we end up with
|
||||
// intermittent time outs (e.g. bug 775924).
|
||||
// Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
// Services.prefs.setBoolPref("devtools.debugger.log.verbose", true);
|
||||
// Enable remote debugging for the relevant tests.
|
||||
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
|
||||
// Fast timeout for TLS tests
|
||||
Services.prefs.setIntPref("devtools.remote.tls-handshake-timeout", 1000);
|
||||
|
||||
function tryImport(url) {
|
||||
try {
|
||||
Cu.import(url);
|
||||
} catch (e) {
|
||||
dump("Error importing " + url + "\n");
|
||||
dump(DevToolsUtils.safeErrorString(e) + "\n");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
tryImport("resource://gre/modules/devtools/dbg-server.jsm");
|
||||
tryImport("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
|
||||
// Convert an nsIScriptError 'aFlags' value into an appropriate string.
|
||||
function scriptErrorFlagsToKind(aFlags) {
|
||||
var kind;
|
||||
if (aFlags & Ci.nsIScriptError.warningFlag)
|
||||
kind = "warning";
|
||||
if (aFlags & Ci.nsIScriptError.exceptionFlag)
|
||||
kind = "exception";
|
||||
else
|
||||
kind = "error";
|
||||
|
||||
if (aFlags & Ci.nsIScriptError.strictFlag)
|
||||
kind = "strict " + kind;
|
||||
|
||||
return kind;
|
||||
}
|
||||
|
||||
// Register a console listener, so console messages don't just disappear
|
||||
// into the ether.
|
||||
let errorCount = 0;
|
||||
let listener = {
|
||||
observe: function (aMessage) {
|
||||
errorCount++;
|
||||
try {
|
||||
// If we've been given an nsIScriptError, then we can print out
|
||||
// something nicely formatted, for tools like Emacs to pick up.
|
||||
var scriptError = aMessage.QueryInterface(Ci.nsIScriptError);
|
||||
dump(aMessage.sourceName + ":" + aMessage.lineNumber + ": " +
|
||||
scriptErrorFlagsToKind(aMessage.flags) + ": " +
|
||||
aMessage.errorMessage + "\n");
|
||||
var string = aMessage.errorMessage;
|
||||
} catch (x) {
|
||||
// Be a little paranoid with message, as the whole goal here is to lose
|
||||
// no information.
|
||||
try {
|
||||
var string = "" + aMessage.message;
|
||||
} catch (x) {
|
||||
var string = "<error converting error message to string>";
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we exit all nested event loops so that the test can finish.
|
||||
while (xpcInspector.eventLoopNestLevel > 0) {
|
||||
xpcInspector.exitNestedEventLoop();
|
||||
}
|
||||
|
||||
// Print in most cases, but ignore the "strict" messages
|
||||
if (!(aMessage.flags & Ci.nsIScriptError.strictFlag)) {
|
||||
do_print("head_dbg.js got console message: " + string + "\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let consoleService = Cc["@mozilla.org/consoleservice;1"]
|
||||
.getService(Ci.nsIConsoleService);
|
||||
consoleService.registerListener(listener);
|
||||
|
||||
/**
|
||||
* Initialize the testing debugger server.
|
||||
*/
|
||||
function initTestDebuggerServer() {
|
||||
DebuggerServer.registerModule("xpcshell-test/testactors");
|
||||
DebuggerServer.init();
|
||||
}
|
@ -3,10 +3,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
|
||||
|
||||
const { Promise: promise } =
|
||||
Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const certService = Cc["@mozilla.org/security/local-cert-service;1"]
|
||||
.getService(Ci.nsILocalCertService);
|
||||
|
||||
@ -49,7 +45,7 @@ function removeCert(nickname) {
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
// No master password, so prompt required here
|
||||
// No master password, so no prompt required here
|
||||
ok(!certService.loginPromptRequired);
|
||||
|
||||
let certA = yield getOrCreateCert(gNickname);
|
||||
|
98
toolkit/devtools/security/tests/unit/test_encryption.js
Normal file
98
toolkit/devtools/security/tests/unit/test_encryption.js
Normal file
@ -0,0 +1,98 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test basic functionality of DevTools client and server TLS encryption mode
|
||||
function run_test() {
|
||||
// Need profile dir to store the key / cert
|
||||
do_get_profile();
|
||||
// Ensure PSM is initialized
|
||||
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function connectClient(client) {
|
||||
let deferred = promise.defer();
|
||||
client.connect(() => {
|
||||
client.listTabs(deferred.resolve);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
initTestDebuggerServer();
|
||||
});
|
||||
|
||||
// Client w/ encryption connects successfully to server w/ encryption
|
||||
add_task(function*() {
|
||||
equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
|
||||
|
||||
let listener = DebuggerServer.createListener();
|
||||
ok(listener, "Socket listener created");
|
||||
listener.portOrPath = -1 /* any available port */;
|
||||
listener.allowConnection = () => true;
|
||||
listener.encryption = true;
|
||||
yield listener.open();
|
||||
equal(DebuggerServer.listeningSockets, 1, "1 listening socket");
|
||||
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: listener.port,
|
||||
encryption: true
|
||||
});
|
||||
ok(transport, "Client transport created");
|
||||
|
||||
let client = new DebuggerClient(transport);
|
||||
let onUnexpectedClose = () => {
|
||||
do_throw("Closed unexpectedly");
|
||||
};
|
||||
client.addListener("closed", onUnexpectedClose);
|
||||
yield connectClient(client);
|
||||
|
||||
// Send a message the server will echo back
|
||||
let message = "secrets";
|
||||
let reply = yield client.request({
|
||||
to: "root",
|
||||
type: "echo",
|
||||
message
|
||||
});
|
||||
equal(reply.message, message, "Encrypted echo matches");
|
||||
|
||||
client.removeListener("closed", onUnexpectedClose);
|
||||
transport.close();
|
||||
listener.close();
|
||||
equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
|
||||
});
|
||||
|
||||
// Client w/o encryption fails to connect to server w/ encryption
|
||||
add_task(function*() {
|
||||
equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
|
||||
|
||||
let listener = DebuggerServer.createListener();
|
||||
ok(listener, "Socket listener created");
|
||||
listener.portOrPath = -1 /* any available port */;
|
||||
listener.allowConnection = () => true;
|
||||
listener.encryption = true;
|
||||
yield listener.open();
|
||||
equal(DebuggerServer.listeningSockets, 1, "1 listening socket");
|
||||
|
||||
try {
|
||||
yield DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: listener.port
|
||||
// encryption: false is the default
|
||||
});
|
||||
} catch(e) {
|
||||
ok(true, "Client failed to connect as expected");
|
||||
listener.close();
|
||||
equal(DebuggerServer.listeningSockets, 0, "0 listening sockets");
|
||||
return;
|
||||
}
|
||||
|
||||
do_throw("Connection unexpectedly succeeded");
|
||||
});
|
||||
|
||||
add_task(function*() {
|
||||
DebuggerServer.destroy();
|
||||
});
|
131
toolkit/devtools/security/tests/unit/testactors.js
Normal file
131
toolkit/devtools/security/tests/unit/testactors.js
Normal file
@ -0,0 +1,131 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { ActorPool, appendExtraActors, createExtraActors } =
|
||||
require("devtools/server/actors/common");
|
||||
const { RootActor } = require("devtools/server/actors/root");
|
||||
const { ThreadActor } = require("devtools/server/actors/script");
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const promise = require("promise");
|
||||
|
||||
var gTestGlobals = [];
|
||||
DebuggerServer.addTestGlobal = function(aGlobal) {
|
||||
gTestGlobals.push(aGlobal);
|
||||
};
|
||||
|
||||
// A mock tab list, for use by tests. This simply presents each global in
|
||||
// gTestGlobals as a tab, and the list is fixed: it never calls its
|
||||
// onListChanged handler.
|
||||
//
|
||||
// As implemented now, we consult gTestGlobals when we're constructed, not
|
||||
// when we're iterated over, so tests have to add their globals before the
|
||||
// root actor is created.
|
||||
function TestTabList(aConnection) {
|
||||
this.conn = aConnection;
|
||||
|
||||
// An array of actors for each global added with
|
||||
// DebuggerServer.addTestGlobal.
|
||||
this._tabActors = [];
|
||||
|
||||
// A pool mapping those actors' names to the actors.
|
||||
this._tabActorPool = new ActorPool(aConnection);
|
||||
|
||||
for (let global of gTestGlobals) {
|
||||
let actor = new TestTabActor(aConnection, global);
|
||||
actor.selected = false;
|
||||
this._tabActors.push(actor);
|
||||
this._tabActorPool.addActor(actor);
|
||||
}
|
||||
if (this._tabActors.length > 0) {
|
||||
this._tabActors[0].selected = true;
|
||||
}
|
||||
|
||||
aConnection.addActorPool(this._tabActorPool);
|
||||
}
|
||||
|
||||
TestTabList.prototype = {
|
||||
constructor: TestTabList,
|
||||
getList: function () {
|
||||
return promise.resolve([tabActor for (tabActor of this._tabActors)]);
|
||||
}
|
||||
};
|
||||
|
||||
function createRootActor(aConnection) {
|
||||
let root = new RootActor(aConnection, {
|
||||
tabList: new TestTabList(aConnection),
|
||||
globalActorFactories: DebuggerServer.globalActorFactories
|
||||
});
|
||||
root.applicationType = "xpcshell-tests";
|
||||
return root;
|
||||
}
|
||||
|
||||
function TestTabActor(aConnection, aGlobal) {
|
||||
this.conn = aConnection;
|
||||
this._global = aGlobal;
|
||||
this._threadActor = new ThreadActor(this, this._global);
|
||||
this.conn.addActor(this._threadActor);
|
||||
this._attached = false;
|
||||
this._extraActors = {};
|
||||
}
|
||||
|
||||
TestTabActor.prototype = {
|
||||
constructor: TestTabActor,
|
||||
actorPrefix: "TestTabActor",
|
||||
|
||||
get window() {
|
||||
return { wrappedJSObject: this._global };
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._global.__name;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
let response = { actor: this.actorID, title: this._global.__name };
|
||||
|
||||
// Walk over tab actors added by extensions and add them to a new ActorPool.
|
||||
let actorPool = new ActorPool(this.conn);
|
||||
this._createExtraActors(DebuggerServer.tabActorFactories, actorPool);
|
||||
if (!actorPool.isEmpty()) {
|
||||
this._tabActorPool = actorPool;
|
||||
this.conn.addActorPool(this._tabActorPool);
|
||||
}
|
||||
|
||||
this._appendExtraActors(response);
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
onAttach: function(aRequest) {
|
||||
this._attached = true;
|
||||
|
||||
let response = { type: "tabAttached", threadActor: this._threadActor.actorID };
|
||||
this._appendExtraActors(response);
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
onDetach: function(aRequest) {
|
||||
if (!this._attached) {
|
||||
return { "error":"wrongState" };
|
||||
}
|
||||
return { type: "detached" };
|
||||
},
|
||||
|
||||
/* Support for DebuggerServer.addTabActor. */
|
||||
_createExtraActors: createExtraActors,
|
||||
_appendExtraActors: appendExtraActors
|
||||
};
|
||||
|
||||
TestTabActor.prototype.requestTypes = {
|
||||
"attach": TestTabActor.prototype.onAttach,
|
||||
"detach": TestTabActor.prototype.onDetach
|
||||
};
|
||||
|
||||
exports.register = function(handle) {
|
||||
handle.setRootActor(createRootActor);
|
||||
};
|
||||
|
||||
exports.unregister = function(handle) {
|
||||
handle.setRootActor(null);
|
||||
};
|
@ -1,6 +1,10 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
head = head_dbg.js
|
||||
tail =
|
||||
skip-if = toolkit == 'android'
|
||||
|
||||
support-files=
|
||||
testactors.js
|
||||
|
||||
[test_cert.js]
|
||||
[test_encryption.js]
|
||||
|
@ -12,6 +12,7 @@ const { devtools } =
|
||||
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const { Promise: promise } =
|
||||
Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
const Services = devtools.require("Services");
|
||||
const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
|
||||
@ -258,20 +259,20 @@ function writeTestTempFile(aFileName, aContent) {
|
||||
|
||||
/*** Transport Factories ***/
|
||||
|
||||
function socket_transport() {
|
||||
let socket_transport = Task.async(function*() {
|
||||
if (!DebuggerServer.listeningSockets) {
|
||||
let listener = DebuggerServer.createListener();
|
||||
listener.portOrPath = -1 /* any available port */;
|
||||
listener.allowConnection = () => true;
|
||||
listener.open();
|
||||
yield listener.open();
|
||||
}
|
||||
let port = DebuggerServer._listeners[0].port;
|
||||
do_print("Debugger server port is " + port);
|
||||
return DebuggerClient.socketConnect("127.0.0.1", port);
|
||||
}
|
||||
return DebuggerClient.socketConnect({ host: "127.0.0.1", port });
|
||||
});
|
||||
|
||||
function local_transport() {
|
||||
return DebuggerServer.connectPipe();
|
||||
return promise.resolve(DebuggerServer.connectPipe());
|
||||
}
|
||||
|
||||
/*** Sample Data ***/
|
||||
|
@ -51,9 +51,9 @@ function add_test_bulk_actor() {
|
||||
|
||||
/*** Tests ***/
|
||||
|
||||
function test_string_error(transportFactory, onReady) {
|
||||
let test_string_error = Task.async(function*(transportFactory, onReady) {
|
||||
let deferred = promise.defer();
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
let client = new DebuggerClient(transport);
|
||||
client.connect((app, traits) => {
|
||||
@ -67,7 +67,7 @@ function test_string_error(transportFactory, onReady) {
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
});
|
||||
|
||||
/*** Reply Types ***/
|
||||
|
||||
|
@ -134,7 +134,7 @@ let replyHandlers = {
|
||||
|
||||
/*** Tests ***/
|
||||
|
||||
function test_bulk_request_cs(transportFactory, actorType, replyType) {
|
||||
let test_bulk_request_cs = Task.async(function*(transportFactory, actorType, replyType) {
|
||||
// Ensure test files are not present from a failed run
|
||||
cleanup_files();
|
||||
writeTestTempFile("bulk-input", really_long());
|
||||
@ -143,7 +143,7 @@ function test_bulk_request_cs(transportFactory, actorType, replyType) {
|
||||
let serverDeferred = promise.defer();
|
||||
let bulkCopyDeferred = promise.defer();
|
||||
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
let client = new DebuggerClient(transport);
|
||||
client.connect((app, traits) => {
|
||||
@ -186,9 +186,9 @@ function test_bulk_request_cs(transportFactory, actorType, replyType) {
|
||||
bulkCopyDeferred.promise,
|
||||
serverDeferred.promise
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
function test_json_request_cs(transportFactory, actorType, replyType) {
|
||||
let test_json_request_cs = Task.async(function*(transportFactory, actorType, replyType) {
|
||||
// Ensure test files are not present from a failed run
|
||||
cleanup_files();
|
||||
writeTestTempFile("bulk-input", really_long());
|
||||
@ -196,7 +196,7 @@ function test_json_request_cs(transportFactory, actorType, replyType) {
|
||||
let clientDeferred = promise.defer();
|
||||
let serverDeferred = promise.defer();
|
||||
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
let client = new DebuggerClient(transport);
|
||||
client.connect((app, traits) => {
|
||||
@ -227,7 +227,7 @@ function test_json_request_cs(transportFactory, actorType, replyType) {
|
||||
clientDeferred.promise,
|
||||
serverDeferred.promise
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
/*** Test Utils ***/
|
||||
|
||||
|
@ -12,14 +12,14 @@ function run_test()
|
||||
do_print("Starting test at " + new Date().toTimeString());
|
||||
initTestDebuggerServer();
|
||||
|
||||
add_test(test_socket_conn);
|
||||
add_test(test_socket_shutdown);
|
||||
add_task(test_socket_conn);
|
||||
add_task(test_socket_shutdown);
|
||||
add_test(test_pipe_conn);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function test_socket_conn()
|
||||
function* test_socket_conn()
|
||||
{
|
||||
do_check_eq(DebuggerServer.listeningSockets, 0);
|
||||
let listener = DebuggerServer.createListener();
|
||||
@ -39,7 +39,11 @@ function test_socket_conn()
|
||||
|
||||
do_print("Starting long and unicode tests at " + new Date().toTimeString());
|
||||
let unicodeString = "(╯°□°)╯︵ ┻━┻";
|
||||
let transport = DebuggerClient.socketConnect("127.0.0.1", gPort);
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: gPort
|
||||
});
|
||||
let closedDeferred = promise.defer();
|
||||
transport.hooks = {
|
||||
onPacket: function(aPacket) {
|
||||
this.onPacket = function(aPacket) {
|
||||
@ -55,13 +59,14 @@ function test_socket_conn()
|
||||
do_check_eq(aPacket.from, "root");
|
||||
},
|
||||
onClosed: function(aStatus) {
|
||||
run_next_test();
|
||||
closedDeferred.resolve();
|
||||
},
|
||||
};
|
||||
transport.ready();
|
||||
return closedDeferred.promise;
|
||||
}
|
||||
|
||||
function test_socket_shutdown()
|
||||
function* test_socket_shutdown()
|
||||
{
|
||||
do_check_eq(DebuggerServer.listeningSockets, 2);
|
||||
gExtraListener.close();
|
||||
@ -73,25 +78,21 @@ function test_socket_shutdown()
|
||||
do_check_eq(DebuggerServer.listeningSockets, 0);
|
||||
|
||||
do_print("Connecting to a server socket at " + new Date().toTimeString());
|
||||
let transport = DebuggerClient.socketConnect("127.0.0.1", gPort);
|
||||
transport.hooks = {
|
||||
onPacket: function(aPacket) {
|
||||
// Shouldn't reach this, should never connect.
|
||||
do_check_true(false);
|
||||
},
|
||||
try {
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: gPort
|
||||
});
|
||||
} catch(e if e.result == Cr.NS_ERROR_CONNECTION_REFUSED ||
|
||||
e.result == Cr.NS_ERROR_NET_TIMEOUT) {
|
||||
// The connection should be refused here, but on slow or overloaded
|
||||
// machines it may just time out.
|
||||
do_check_true(true);
|
||||
return;
|
||||
}
|
||||
|
||||
onClosed: function(aStatus) {
|
||||
do_print("test_socket_shutdown onClosed called at " + new Date().toTimeString());
|
||||
// The connection should be refused here, but on slow or overloaded
|
||||
// machines it may just time out.
|
||||
let expected = [ Cr.NS_ERROR_CONNECTION_REFUSED, Cr.NS_ERROR_NET_TIMEOUT ];
|
||||
do_check_neq(expected.indexOf(aStatus), -1);
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
|
||||
do_print("Initializing input stream at " + new Date().toTimeString());
|
||||
transport.ready();
|
||||
// Shouldn't reach this, should never connect.
|
||||
do_check_true(false);
|
||||
}
|
||||
|
||||
function test_pipe_conn()
|
||||
|
@ -17,10 +17,10 @@ function run_test() {
|
||||
do_print("Starting test at " + new Date().toTimeString());
|
||||
initTestDebuggerServer();
|
||||
|
||||
add_test(test_socket_conn_drops_after_invalid_header);
|
||||
add_test(test_socket_conn_drops_after_invalid_header_2);
|
||||
add_test(test_socket_conn_drops_after_too_large_length);
|
||||
add_test(test_socket_conn_drops_after_too_long_header);
|
||||
add_task(test_socket_conn_drops_after_invalid_header);
|
||||
add_task(test_socket_conn_drops_after_invalid_header_2);
|
||||
add_task(test_socket_conn_drops_after_too_large_length);
|
||||
add_task(test_socket_conn_drops_after_too_long_header);
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
@ -46,13 +46,17 @@ function test_socket_conn_drops_after_too_long_header() {
|
||||
return test_helper(rawPacket + ':');
|
||||
}
|
||||
|
||||
function test_helper(payload) {
|
||||
let test_helper = Task.async(function*(payload) {
|
||||
let listener = DebuggerServer.createListener();
|
||||
listener.portOrPath = -1;
|
||||
listener.allowConnection = () => true;
|
||||
listener.open();
|
||||
|
||||
let transport = DebuggerClient.socketConnect("127.0.0.1", listener.port);
|
||||
let transport = yield DebuggerClient.socketConnect({
|
||||
host: "127.0.0.1",
|
||||
port: listener.port
|
||||
});
|
||||
let closedDeferred = promise.defer();
|
||||
transport.hooks = {
|
||||
onPacket: function(aPacket) {
|
||||
this.onPacket = function(aPacket) {
|
||||
@ -66,8 +70,9 @@ function test_helper(payload) {
|
||||
},
|
||||
onClosed: function(aStatus) {
|
||||
do_check_true(true);
|
||||
run_next_test();
|
||||
closedDeferred.resolve();
|
||||
},
|
||||
};
|
||||
transport.ready();
|
||||
}
|
||||
return closedDeferred.promise;
|
||||
});
|
||||
|
@ -26,9 +26,9 @@ function run_test() {
|
||||
|
||||
/*** Tests ***/
|
||||
|
||||
function test_bulk_send_error(transportFactory) {
|
||||
let test_bulk_send_error = Task.async(function*(transportFactory) {
|
||||
let deferred = promise.defer();
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
let client = new DebuggerClient(transport);
|
||||
client.connect((app, traits) => {
|
||||
@ -45,4 +45,4 @@ function test_bulk_send_error(transportFactory) {
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ function run_test() {
|
||||
|
||||
/*** Tests ***/
|
||||
|
||||
function test_transport(transportFactory) {
|
||||
let test_transport = Task.async(function*(transportFactory) {
|
||||
let clientDeferred = promise.defer();
|
||||
let serverDeferred = promise.defer();
|
||||
|
||||
@ -36,7 +36,7 @@ function test_transport(transportFactory) {
|
||||
|
||||
do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
|
||||
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
// Sending from client to server
|
||||
function write_data({copyFrom}) {
|
||||
@ -133,7 +133,7 @@ function test_transport(transportFactory) {
|
||||
transport.ready();
|
||||
|
||||
return promise.all([clientDeferred.promise, serverDeferred.promise]);
|
||||
}
|
||||
});
|
||||
|
||||
/*** Test Utils ***/
|
||||
|
||||
|
@ -23,7 +23,7 @@ function run_test() {
|
||||
/**
|
||||
* This tests a one-way bulk transfer at the transport layer.
|
||||
*/
|
||||
function test_bulk_transfer_transport(transportFactory) {
|
||||
let test_bulk_transfer_transport = Task.async(function*(transportFactory) {
|
||||
do_print("Starting bulk transfer test at " + new Date().toTimeString());
|
||||
|
||||
let clientDeferred = promise.defer();
|
||||
@ -36,7 +36,7 @@ function test_bulk_transfer_transport(transportFactory) {
|
||||
|
||||
do_check_eq(Object.keys(DebuggerServer._connections).length, 0);
|
||||
|
||||
let transport = transportFactory();
|
||||
let transport = yield transportFactory();
|
||||
|
||||
// Sending from client to server
|
||||
function write_data({copyFrom}) {
|
||||
@ -104,7 +104,7 @@ function test_bulk_transfer_transport(transportFactory) {
|
||||
transport.ready();
|
||||
|
||||
return promise.all([clientDeferred.promise, serverDeferred.promise]);
|
||||
}
|
||||
});
|
||||
|
||||
/*** Test Utils ***/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user