michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: */ michael@0: "use strict"; michael@0: michael@0: const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components; michael@0: michael@0: let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); michael@0: let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); michael@0: let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); michael@0: let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); michael@0: let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {}); michael@0: let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm"); michael@0: michael@0: let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc); michael@0: michael@0: const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"] michael@0: .getService(Ci.nsIDebug2).isDebugBuild; michael@0: michael@0: const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE; michael@0: const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; michael@0: michael@0: // Sort in numerical order michael@0: const SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5; // -8187 michael@0: const SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9; michael@0: const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11; michael@0: const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12; // -8180 michael@0: const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13; michael@0: const SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18; michael@0: const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20; // -8172 michael@0: const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21; // -8171 michael@0: const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30; // -8162 michael@0: const SEC_ERROR_EXTENSION_VALUE_INVALID = SEC_ERROR_BASE + 34; // -8158 michael@0: const SEC_ERROR_EXTENSION_NOT_FOUND = SEC_ERROR_BASE + 35; // -8157 michael@0: const SEC_ERROR_CA_CERT_INVALID = SEC_ERROR_BASE + 36; michael@0: const SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION = SEC_ERROR_BASE + 41; michael@0: const SEC_ERROR_INADEQUATE_KEY_USAGE = SEC_ERROR_BASE + 90; // -8102 michael@0: const SEC_ERROR_INADEQUATE_CERT_TYPE = SEC_ERROR_BASE + 91; // -8101 michael@0: const SEC_ERROR_CERT_NOT_IN_NAME_SPACE = SEC_ERROR_BASE + 112; // -8080 michael@0: const SEC_ERROR_CERT_BAD_ACCESS_LOCATION = SEC_ERROR_BASE + 117; // -8075 michael@0: const SEC_ERROR_OCSP_MALFORMED_REQUEST = SEC_ERROR_BASE + 120; michael@0: const SEC_ERROR_OCSP_SERVER_ERROR = SEC_ERROR_BASE + 121; // -8071 michael@0: const SEC_ERROR_OCSP_TRY_SERVER_LATER = SEC_ERROR_BASE + 122; michael@0: const SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = SEC_ERROR_BASE + 123; michael@0: const SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = SEC_ERROR_BASE + 124; michael@0: const SEC_ERROR_OCSP_UNKNOWN_CERT = SEC_ERROR_BASE + 126; // -8066 michael@0: const SEC_ERROR_OCSP_MALFORMED_RESPONSE = SEC_ERROR_BASE + 129; michael@0: const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = SEC_ERROR_BASE + 130; michael@0: const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132; michael@0: const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144; michael@0: const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032 michael@0: const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157; michael@0: const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176; michael@0: const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178; michael@0: michael@0: const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12; michael@0: michael@0: // Supported Certificate Usages michael@0: const certificateUsageSSLClient = 0x0001; michael@0: const certificateUsageSSLServer = 0x0002; michael@0: const certificateUsageSSLCA = 0x0008; michael@0: const certificateUsageEmailSigner = 0x0010; michael@0: const certificateUsageEmailRecipient = 0x0020; michael@0: const certificateUsageObjectSigner = 0x0040; michael@0: const certificateUsageVerifyCA = 0x0100; michael@0: const certificateUsageStatusResponder = 0x0400; michael@0: michael@0: const NO_FLAGS = 0; michael@0: michael@0: function readFile(file) { michael@0: let fstream = Cc["@mozilla.org/network/file-input-stream;1"] michael@0: .createInstance(Ci.nsIFileInputStream); michael@0: fstream.init(file, -1, 0, 0); michael@0: let data = NetUtil.readInputStreamToString(fstream, fstream.available()); michael@0: fstream.close(); michael@0: return data; michael@0: } michael@0: michael@0: function addCertFromFile(certdb, filename, trustString) { michael@0: let certFile = do_get_file(filename, false); michael@0: let der = readFile(certFile); michael@0: certdb.addCert(der, trustString, null); michael@0: } michael@0: michael@0: function constructCertFromFile(filename) { michael@0: let certFile = do_get_file(filename, false); michael@0: let certDER = readFile(certFile); michael@0: let certdb = Cc["@mozilla.org/security/x509certdb;1"] michael@0: .getService(Ci.nsIX509CertDB); michael@0: return certdb.constructX509(certDER, certDER.length); michael@0: } michael@0: michael@0: function setCertTrust(cert, trustString) { michael@0: let certdb = Cc["@mozilla.org/security/x509certdb;1"] michael@0: .getService(Ci.nsIX509CertDB); michael@0: certdb.setCertTrustFromString(cert, trustString); michael@0: } michael@0: michael@0: function getXPCOMStatusFromNSS(statusNSS) { michael@0: let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"] michael@0: .getService(Ci.nsINSSErrorsService); michael@0: return nssErrorsService.getXPCOMFromNSSError(statusNSS); michael@0: } michael@0: michael@0: function checkCertErrorGeneric(certdb, cert, expectedError, usage) { michael@0: let hasEVPolicy = {}; michael@0: let verifiedChain = {}; michael@0: let error = certdb.verifyCertNow(cert, usage, NO_FLAGS, verifiedChain, michael@0: hasEVPolicy); michael@0: // expected error == -1 is a special marker for any error is OK michael@0: if (expectedError != -1 ) { michael@0: do_check_eq(error, expectedError); michael@0: } else { michael@0: do_check_neq (error, 0); michael@0: } michael@0: } michael@0: michael@0: function _getLibraryFunctionWithNoArguments(functionName, libraryName) { michael@0: // Open the NSS library. copied from services/crypto/modules/WeaveCrypto.js michael@0: let path = ctypes.libraryName(libraryName); michael@0: michael@0: // XXX really want to be able to pass specific dlopen flags here. michael@0: let nsslib; michael@0: try { michael@0: nsslib = ctypes.open(path); michael@0: } catch(e) { michael@0: // In case opening the library without a full path fails, michael@0: // try again with a full path. michael@0: let file = Services.dirsvc.get("GreD", Ci.nsILocalFile); michael@0: file.append(path); michael@0: nsslib = ctypes.open(file.path); michael@0: } michael@0: michael@0: let SECStatus = ctypes.int; michael@0: let func = nsslib.declare(functionName, ctypes.default_abi, SECStatus); michael@0: return func; michael@0: } michael@0: michael@0: function clearOCSPCache() { michael@0: let certdb = Cc["@mozilla.org/security/x509certdb;1"] michael@0: .getService(Ci.nsIX509CertDB); michael@0: certdb.clearOCSPCache(); michael@0: } michael@0: michael@0: function clearSessionCache() { michael@0: let SSL_ClearSessionCache = null; michael@0: try { michael@0: SSL_ClearSessionCache = michael@0: _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "ssl3"); michael@0: } catch (e) { michael@0: // On Windows, this is actually in the nss3 library. michael@0: SSL_ClearSessionCache = michael@0: _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "nss3"); michael@0: } michael@0: if (!SSL_ClearSessionCache || SSL_ClearSessionCache() != 0) { michael@0: throw "Failed to clear SSL session cache"; michael@0: } michael@0: } michael@0: michael@0: // Set up a TLS testing environment that has a TLS server running and michael@0: // ready to accept connections. This async function starts the server and michael@0: // waits for the server to indicate that it is ready. michael@0: // michael@0: // Each test should have its own subdomain of example.com, for example michael@0: // my-first-connection-test.example.com. The server can use the server michael@0: // name (passed through the SNI TLS extension) to determine what behavior michael@0: // the server side of the text should exhibit. See TLSServer.h for more michael@0: // information on how to write the server side of tests. michael@0: // michael@0: // Create a new source file for your new server executable in michael@0: // security/manager/ssl/tests/unit/tlsserver/cmd similar to the other ones in michael@0: // that directory, and add a reference to it to the sources variable in that michael@0: // directory's moz.build. michael@0: // michael@0: // Modify TEST_HARNESS_BINS in michael@0: // testing/mochitest/Makefile.in and NO_PKG_FILES in michael@0: // toolkit/mozapps/installer/packager.mk to make sure the new executable michael@0: // gets included in the packages used for shipping the tests to the test michael@0: // runners in our build/test farm. (Things will work fine locally without michael@0: // these changes but will break on TBPL.) michael@0: // michael@0: // Your test script should look something like this: michael@0: /* michael@0: michael@0: // -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: // This Source Code Form is subject to the terms of the Mozilla Public michael@0: // License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: // file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: "use strict"; michael@0: michael@0: // michael@0: michael@0: function run_test() { michael@0: do_get_profile(); michael@0: add_tls_server_setup(""); michael@0: michael@0: add_connection_test(".example.com", michael@0: getXPCOMStatusFromNSS(SEC_ERROR_xxx), michael@0: function() { ... }, michael@0: function(aTransportSecurityInfo) { ... }); michael@0: [...] michael@0: add_connection_test(".example.com", Cr.NS_OK); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: */ michael@0: function add_tls_server_setup(serverBinName) { michael@0: add_test(function() { michael@0: _setupTLSServerTest(serverBinName); michael@0: }); michael@0: } michael@0: michael@0: // Add a TLS connection test case. aHost is the hostname to pass in the SNI TLS michael@0: // extension; this should unambiguously identifiy which test is being run. michael@0: // aExpectedResult is the expected nsresult of the connection. michael@0: // aBeforeConnect is a callback function that takes no arguments that will be michael@0: // called before the connection is attempted. michael@0: // aWithSecurityInfo is a callback function that takes an michael@0: // nsITransportSecurityInfo, which is called after the TLS handshake succeeds. michael@0: function add_connection_test(aHost, aExpectedResult, michael@0: aBeforeConnect, aWithSecurityInfo) { michael@0: const REMOTE_PORT = 8443; michael@0: michael@0: function Connection(aHost) { michael@0: this.host = aHost; michael@0: let threadManager = Cc["@mozilla.org/thread-manager;1"] michael@0: .getService(Ci.nsIThreadManager); michael@0: this.thread = threadManager.currentThread; michael@0: this.defer = Promise.defer(); michael@0: let sts = Cc["@mozilla.org/network/socket-transport-service;1"] michael@0: .getService(Ci.nsISocketTransportService); michael@0: this.transport = sts.createTransport(["ssl"], 1, aHost, REMOTE_PORT, null); michael@0: this.transport.setEventSink(this, this.thread); michael@0: this.inputStream = null; michael@0: this.outputStream = null; michael@0: this.connected = false; michael@0: } michael@0: michael@0: Connection.prototype = { michael@0: // nsITransportEventSink michael@0: onTransportStatus: function(aTransport, aStatus, aProgress, aProgressMax) { michael@0: if (!this.connected && aStatus == Ci.nsISocketTransport.STATUS_CONNECTED_TO) { michael@0: this.connected = true; michael@0: this.outputStream.asyncWait(this, 0, 0, this.thread); michael@0: } michael@0: }, michael@0: michael@0: // nsIInputStreamCallback michael@0: onInputStreamReady: function(aStream) { michael@0: try { michael@0: // this will throw if the stream has been closed by an error michael@0: let str = NetUtil.readInputStreamToString(aStream, aStream.available()); michael@0: do_check_eq(str, "0"); michael@0: this.inputStream.close(); michael@0: this.outputStream.close(); michael@0: this.result = Cr.NS_OK; michael@0: } catch (e) { michael@0: this.result = e.result; michael@0: } michael@0: this.defer.resolve(this); michael@0: }, michael@0: michael@0: // nsIOutputStreamCallback michael@0: onOutputStreamReady: function(aStream) { michael@0: let sslSocketControl = this.transport.securityInfo michael@0: .QueryInterface(Ci.nsISSLSocketControl); michael@0: sslSocketControl.proxyStartSSL(); michael@0: this.outputStream.write("0", 1); michael@0: let inStream = this.transport.openInputStream(0, 0, 0) michael@0: .QueryInterface(Ci.nsIAsyncInputStream); michael@0: this.inputStream = inStream; michael@0: this.inputStream.asyncWait(this, 0, 0, this.thread); michael@0: }, michael@0: michael@0: go: function() { michael@0: this.outputStream = this.transport.openOutputStream(0, 0, 0) michael@0: .QueryInterface(Ci.nsIAsyncOutputStream); michael@0: return this.defer.promise; michael@0: } michael@0: }; michael@0: michael@0: /* Returns a promise to connect to aHost that resolves to the result of that michael@0: * connection */ michael@0: function connectTo(aHost) { michael@0: Services.prefs.setCharPref("network.dns.localDomains", aHost); michael@0: let connection = new Connection(aHost); michael@0: return connection.go(); michael@0: } michael@0: michael@0: add_test(function() { michael@0: if (aBeforeConnect) { michael@0: aBeforeConnect(); michael@0: } michael@0: connectTo(aHost).then(function(conn) { michael@0: do_check_eq(conn.result, aExpectedResult); michael@0: if (aWithSecurityInfo) { michael@0: aWithSecurityInfo(conn.transport.securityInfo michael@0: .QueryInterface(Ci.nsITransportSecurityInfo)); michael@0: } michael@0: run_next_test(); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: function _getBinaryUtil(binaryUtilName) { michael@0: let directoryService = Cc["@mozilla.org/file/directory_service;1"] michael@0: .getService(Ci.nsIProperties); michael@0: michael@0: let utilBin = directoryService.get("CurProcD", Ci.nsILocalFile); michael@0: utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : "")); michael@0: // If we're testing locally, the above works. If not, the server executable michael@0: // is in another location. michael@0: if (!utilBin.exists()) { michael@0: utilBin = directoryService.get("CurWorkD", Ci.nsILocalFile); michael@0: while (utilBin.path.indexOf("xpcshell") != -1) { michael@0: utilBin = utilBin.parent; michael@0: } michael@0: utilBin.append("bin"); michael@0: utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : "")); michael@0: } michael@0: do_check_true(utilBin.exists()); michael@0: return utilBin; michael@0: } michael@0: michael@0: // Do not call this directly; use add_tls_server_setup michael@0: function _setupTLSServerTest(serverBinName) michael@0: { michael@0: let certdb = Cc["@mozilla.org/security/x509certdb;1"] michael@0: .getService(Ci.nsIX509CertDB); michael@0: // The trusted CA that is typically used for "good" certificates. michael@0: addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u"); michael@0: michael@0: const CALLBACK_PORT = 8444; michael@0: michael@0: let directoryService = Cc["@mozilla.org/file/directory_service;1"] michael@0: .getService(Ci.nsIProperties); michael@0: let envSvc = Cc["@mozilla.org/process/environment;1"] michael@0: .getService(Ci.nsIEnvironment); michael@0: let greDir = directoryService.get("GreD", Ci.nsIFile); michael@0: envSvc.set("DYLD_LIBRARY_PATH", greDir.path); michael@0: envSvc.set("LD_LIBRARY_PATH", greDir.path); michael@0: envSvc.set("MOZ_TLS_SERVER_DEBUG_LEVEL", "3"); michael@0: envSvc.set("MOZ_TLS_SERVER_CALLBACK_PORT", CALLBACK_PORT); michael@0: michael@0: let httpServer = new HttpServer(); michael@0: httpServer.registerPathHandler("/", michael@0: function handleServerCallback(aRequest, aResponse) { michael@0: aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); michael@0: aResponse.setHeader("Content-Type", "text/plain"); michael@0: let responseBody = "OK!"; michael@0: aResponse.bodyOutputStream.write(responseBody, responseBody.length); michael@0: do_execute_soon(function() { michael@0: httpServer.stop(run_next_test); michael@0: }); michael@0: }); michael@0: httpServer.start(CALLBACK_PORT); michael@0: michael@0: let serverBin = _getBinaryUtil(serverBinName); michael@0: let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); michael@0: process.init(serverBin); michael@0: let certDir = directoryService.get("CurWorkD", Ci.nsILocalFile); michael@0: certDir.append("tlsserver"); michael@0: do_check_true(certDir.exists()); michael@0: process.run(false, [certDir.path], 1); michael@0: michael@0: do_register_cleanup(function() { michael@0: process.kill(); michael@0: }); michael@0: } michael@0: michael@0: // Returns an Array of OCSP responses for a given ocspRespArray and a location michael@0: // for a nssDB where the certs and public keys are prepopulated. michael@0: // ocspRespArray is an array of arrays like: michael@0: // [ [typeOfResponse, certnick, extracertnick]...] michael@0: function generateOCSPResponses(ocspRespArray, nssDBlocation) michael@0: { michael@0: let utilBinName = "GenerateOCSPResponse"; michael@0: let ocspGenBin = _getBinaryUtil(utilBinName); michael@0: let retArray = new Array(); michael@0: michael@0: for (let i = 0; i < ocspRespArray.length; i++) { michael@0: let argArray = new Array(); michael@0: let ocspFilepre = do_get_file(i.toString() + ".ocsp", true); michael@0: let filename = ocspFilepre.path; michael@0: argArray.push(nssDBlocation); michael@0: argArray.push(ocspRespArray[i][0]); // ocsRespType; michael@0: argArray.push(ocspRespArray[i][1]); // nick; michael@0: argArray.push(ocspRespArray[i][2]); // extranickname michael@0: argArray.push(filename); michael@0: do_print("arg_array ="+argArray); michael@0: michael@0: let process = Cc["@mozilla.org/process/util;1"] michael@0: .createInstance(Ci.nsIProcess); michael@0: process.init(ocspGenBin); michael@0: process.run(true, argArray, 5); michael@0: do_check_eq(0, process.exitValue); michael@0: let ocspFile = do_get_file(i.toString() + ".ocsp", false); michael@0: retArray.push(readFile(ocspFile)); michael@0: ocspFile.remove(false); michael@0: } michael@0: return retArray; michael@0: } michael@0: michael@0: // Starts and returns an http responder that will cause a test failure if it is michael@0: // queried. The server identities are given by a non-empty array michael@0: // serverIdentities. michael@0: function getFailingHttpServer(serverPort, serverIdentities) { michael@0: let httpServer = new HttpServer(); michael@0: httpServer.registerPrefixHandler("/", function(request, response) { michael@0: do_check_true(false); michael@0: }); michael@0: httpServer.identity.setPrimary("http", serverIdentities.shift(), serverPort); michael@0: serverIdentities.forEach(function(identity) { michael@0: httpServer.identity.add("http", identity, serverPort); michael@0: }); michael@0: httpServer.start(serverPort); michael@0: return httpServer; michael@0: } michael@0: michael@0: // Starts an http OCSP responder that serves good OCSP responses and michael@0: // returns an object with a method stop that should be called to stop michael@0: // the http server. michael@0: // NB: Because generating OCSP responses inside the HTTP request michael@0: // handler can cause timeouts, the expected responses are pre-generated michael@0: // all at once before starting the server. This means that their producedAt michael@0: // times will all be the same. If a test depends on this not being the case, michael@0: // perhaps calling startOCSPResponder twice (at different times) will be michael@0: // necessary. michael@0: // michael@0: // serverPort is the port of the http OCSP responder michael@0: // identity is the http hostname that will answer the OCSP requests michael@0: // invalidIdentities is an array of identities that if used an michael@0: // will cause a test failure michael@0: // nssDBlocaion is the location of the NSS database from where the OCSP michael@0: // responses will be generated (assumes appropiate keys are present) michael@0: // expectedCertNames is an array of nicks of the certs to be responsed michael@0: // expectedBasePaths is an optional array that is used to indicate michael@0: // what is the expected base path of the OCSP request. michael@0: function startOCSPResponder(serverPort, identity, invalidIdentities, michael@0: nssDBLocation, expectedCertNames, michael@0: expectedBasePaths, expectedMethods, michael@0: expectedResponseTypes) { michael@0: let ocspResponseGenerationArgs = expectedCertNames.map( michael@0: function(expectedNick) { michael@0: let responseType = "good"; michael@0: if (expectedResponseTypes && expectedResponseTypes.length >= 1) { michael@0: responseType = expectedResponseTypes.shift(); michael@0: } michael@0: return [responseType, expectedNick, "unused"]; michael@0: } michael@0: ); michael@0: let ocspResponses = generateOCSPResponses(ocspResponseGenerationArgs, michael@0: nssDBLocation); michael@0: let httpServer = new HttpServer(); michael@0: httpServer.registerPrefixHandler("/", michael@0: function handleServerCallback(aRequest, aResponse) { michael@0: invalidIdentities.forEach(function(identity) { michael@0: do_check_neq(aRequest.host, identity) michael@0: }); michael@0: do_print("got request for: " + aRequest.path); michael@0: let basePath = aRequest.path.slice(1).split("/")[0]; michael@0: if (expectedBasePaths.length >= 1) { michael@0: do_check_eq(basePath, expectedBasePaths.shift()); michael@0: } michael@0: do_check_true(expectedCertNames.length >= 1); michael@0: if (expectedMethods && expectedMethods.length >= 1) { michael@0: do_check_eq(aRequest.method, expectedMethods.shift()); michael@0: } michael@0: aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); michael@0: aResponse.setHeader("Content-Type", "application/ocsp-response"); michael@0: aResponse.write(ocspResponses.shift()); michael@0: }); michael@0: httpServer.identity.setPrimary("http", identity, serverPort); michael@0: invalidIdentities.forEach(function(identity) { michael@0: httpServer.identity.add("http", identity, serverPort); michael@0: }); michael@0: httpServer.start(serverPort); michael@0: return { michael@0: stop: function(callback) { michael@0: // make sure we consumed each expected response michael@0: do_check_eq(ocspResponses.length, 0); michael@0: if (expectedBasePaths) { michael@0: do_check_eq(expectedBasePaths.length, 0); michael@0: } michael@0: if (expectedResponseTypes) { michael@0: do_check_eq(expectedResponseTypes.length, 0); michael@0: } michael@0: httpServer.stop(callback); michael@0: } michael@0: }; michael@0: }