diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/net/test | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/net/test')
36 files changed, 5687 insertions, 0 deletions
diff --git a/chromium/net/test/OWNERS b/chromium/net/test/OWNERS new file mode 100644 index 00000000000..5b08e349893 --- /dev/null +++ b/chromium/net/test/OWNERS @@ -0,0 +1,5 @@ +# General reviewer, except sync-specific bits. +phajdan.jr@chromium.org + +# For changes to local_sync_test_server.{h|cc}. +akalin@chromium.org diff --git a/chromium/net/test/android/OWNERS b/chromium/net/test/android/OWNERS new file mode 100644 index 00000000000..4bcb60f934c --- /dev/null +++ b/chromium/net/test/android/OWNERS @@ -0,0 +1,3 @@ +digit@chromium.org +pliard@chromium.org +yfriedman@chromium.org diff --git a/chromium/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java b/chromium/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java new file mode 100644 index 00000000000..9e60a43fa41 --- /dev/null +++ b/chromium/net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java @@ -0,0 +1,557 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.net.test.util; + +import android.util.Base64; +import android.util.Log; +import android.util.Pair; + +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.HttpVersion; +import org.apache.http.RequestLine; +import org.apache.http.StatusLine; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.DefaultHttpServerConnection; +import org.apache.http.impl.cookie.DateUtils; +import org.apache.http.message.BasicHttpResponse; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.HttpParams; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.X509TrustManager; + +/** + * Simple http test server for testing. + * + * This server runs in a thread in the current process, so it is convenient + * for loopback testing without the need to setup tcp forwarding to the + * host computer. + * + * Based heavily on the CTSWebServer in Android. + */ +public class TestWebServer { + private static final String TAG = "TestWebServer"; + private static final int SERVER_PORT = 4444; + private static final int SSL_SERVER_PORT = 4445; + + public static final String SHUTDOWN_PREFIX = "/shutdown"; + + private static TestWebServer sInstance; + private static Hashtable<Integer, String> sReasons; + + private final ServerThread mServerThread; + private String mServerUri; + private final boolean mSsl; + + private static class Response { + final byte[] mResponseData; + final List<Pair<String, String>> mResponseHeaders; + final boolean mIsRedirect; + + Response(byte[] resposneData, List<Pair<String, String>> responseHeaders, + boolean isRedirect) { + mIsRedirect = isRedirect; + mResponseData = resposneData; + mResponseHeaders = responseHeaders == null ? + new ArrayList<Pair<String, String>>() : responseHeaders; + } + } + + // The Maps below are modified on both the client thread and the internal server thread, so + // need to use a lock when accessing them. + private final Object mLock = new Object(); + private final Map<String, Response> mResponseMap = new HashMap<String, Response>(); + private final Map<String, Integer> mResponseCountMap = new HashMap<String, Integer>(); + private final Map<String, HttpRequest> mLastRequestMap = new HashMap<String, HttpRequest>(); + + /** + * Create and start a local HTTP server instance. + * @param ssl True if the server should be using secure sockets. + * @throws Exception + */ + public TestWebServer(boolean ssl) throws Exception { + if (sInstance != null) { + // attempt to start a new instance while one is still running + // shut down the old instance first + sInstance.shutdown(); + } + setStaticInstance(this); + mSsl = ssl; + if (mSsl) { + mServerUri = "https://localhost:" + SSL_SERVER_PORT; + } else { + mServerUri = "http://localhost:" + SERVER_PORT; + } + mServerThread = new ServerThread(this, mSsl); + mServerThread.start(); + } + + private static void setStaticInstance(TestWebServer instance) { + sInstance = instance; + } + + /** + * Terminate the http server. + */ + public void shutdown() { + try { + // Avoid a deadlock between two threads where one is trying to call + // close() and the other one is calling accept() by sending a GET + // request for shutdown and having the server's one thread + // sequentially call accept() and close(). + URL url = new URL(mServerUri + SHUTDOWN_PREFIX); + URLConnection connection = openConnection(url); + connection.connect(); + + // Read the input from the stream to send the request. + InputStream is = connection.getInputStream(); + is.close(); + + // Block until the server thread is done shutting down. + mServerThread.join(); + + } catch (MalformedURLException e) { + throw new IllegalStateException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } catch (KeyManagementException e) { + throw new IllegalStateException(e); + } + + setStaticInstance(null); + } + + private final static int RESPONSE_STATUS_NORMAL = 0; + private final static int RESPONSE_STATUS_MOVED_TEMPORARILY = 1; + + private String setResponseInternal( + String requestPath, byte[] responseData, + List<Pair<String, String>> responseHeaders, + int status) { + final boolean isRedirect = (status == RESPONSE_STATUS_MOVED_TEMPORARILY); + + synchronized (mLock) { + mResponseMap.put(requestPath, new Response(responseData, responseHeaders, isRedirect)); + mResponseCountMap.put(requestPath, Integer.valueOf(0)); + mLastRequestMap.put(requestPath, null); + } + return getResponseUrl(requestPath); + } + + /** + * Gets the URL on the server under which a particular request path will be accessible. + * + * This only gets the URL, you still need to set the response if you intend to access it. + * + * @param requestPath The path to respond to. + * @return The full URL including the requestPath. + */ + public String getResponseUrl(String requestPath) { + return mServerUri + requestPath; + } + + /** + * Sets a response to be returned when a particular request path is passed + * in (with the option to specify additional headers). + * + * @param requestPath The path to respond to. + * @param responseString The response body that will be returned. + * @param responseHeaders Any additional headers that should be returned along with the + * response (null is acceptable). + * @return The full URL including the path that should be requested to get the expected + * response. + */ + public String setResponse( + String requestPath, String responseString, + List<Pair<String, String>> responseHeaders) { + return setResponseInternal(requestPath, responseString.getBytes(), responseHeaders, + RESPONSE_STATUS_NORMAL); + } + + /** + * Sets a redirect. + * + * @param requestPath The path to respond to. + * @param targetPath The path to redirect to. + * @return The full URL including the path that should be requested to get the expected + * response. + */ + public String setRedirect( + String requestPath, String targetPath) { + List<Pair<String, String>> responseHeaders = new ArrayList<Pair<String, String>>(); + responseHeaders.add(Pair.create("Location", targetPath)); + + return setResponseInternal(requestPath, targetPath.getBytes(), responseHeaders, + RESPONSE_STATUS_MOVED_TEMPORARILY); + } + + /** + * Sets a base64 encoded response to be returned when a particular request path is passed + * in (with the option to specify additional headers). + * + * @param requestPath The path to respond to. + * @param base64EncodedResponse The response body that is base64 encoded. The actual server + * response will the decoded binary form. + * @param responseHeaders Any additional headers that should be returned along with the + * response (null is acceptable). + * @return The full URL including the path that should be requested to get the expected + * response. + */ + public String setResponseBase64( + String requestPath, String base64EncodedResponse, + List<Pair<String, String>> responseHeaders) { + return setResponseInternal(requestPath, + Base64.decode(base64EncodedResponse, Base64.DEFAULT), + responseHeaders, + RESPONSE_STATUS_NORMAL); + } + + /** + * Get the number of requests was made at this path since it was last set. + */ + public int getRequestCount(String requestPath) { + Integer count = null; + synchronized (mLock) { + count = mResponseCountMap.get(requestPath); + } + if (count == null) throw new IllegalArgumentException("Path not set: " + requestPath); + return count.intValue(); + } + + /** + * Returns the last HttpRequest at this path. Can return null if it is never requested. + */ + public HttpRequest getLastRequest(String requestPath) { + synchronized (mLock) { + if (!mLastRequestMap.containsKey(requestPath)) + throw new IllegalArgumentException("Path not set: " + requestPath); + return mLastRequestMap.get(requestPath); + } + } + + public String getBaseUrl() { + return mServerUri + "/"; + } + + private URLConnection openConnection(URL url) + throws IOException, NoSuchAlgorithmException, KeyManagementException { + if (mSsl) { + // Install hostname verifiers and trust managers that don't do + // anything in order to get around the client not trusting + // the test server due to a lack of certificates. + + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setHostnameVerifier(new TestHostnameVerifier()); + + SSLContext context = SSLContext.getInstance("TLS"); + TestTrustManager trustManager = new TestTrustManager(); + context.init(null, new TestTrustManager[] {trustManager}, null); + connection.setSSLSocketFactory(context.getSocketFactory()); + + return connection; + } else { + return url.openConnection(); + } + } + + /** + * {@link X509TrustManager} that trusts everybody. This is used so that + * the client calling {@link TestWebServer#shutdown()} can issue a request + * for shutdown by blindly trusting the {@link TestWebServer}'s + * credentials. + */ + private static class TestTrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + // Trust the TestWebServer... + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + // Trust the TestWebServer... + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + } + + /** + * {@link HostnameVerifier} that verifies everybody. This permits + * the client to trust the web server and call + * {@link TestWebServer#shutdown()}. + */ + private static class TestHostnameVerifier implements HostnameVerifier { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + } + + private void servedResponseFor(String path, HttpRequest request) { + synchronized (mLock) { + mResponseCountMap.put(path, Integer.valueOf( + mResponseCountMap.get(path).intValue() + 1)); + mLastRequestMap.put(path, request); + } + } + + /** + * Generate a response to the given request. + * @throws InterruptedException + */ + private HttpResponse getResponse(HttpRequest request) throws InterruptedException { + RequestLine requestLine = request.getRequestLine(); + HttpResponse httpResponse = null; + Log.i(TAG, requestLine.getMethod() + ": " + requestLine.getUri()); + String uriString = requestLine.getUri(); + URI uri = URI.create(uriString); + String path = uri.getPath(); + + Response response = null; + synchronized (mLock) { + response = mResponseMap.get(path); + } + if (path.equals(SHUTDOWN_PREFIX)) { + httpResponse = createResponse(HttpStatus.SC_OK); + } else if (response == null) { + httpResponse = createResponse(HttpStatus.SC_NOT_FOUND); + } else if (response.mIsRedirect) { + httpResponse = createResponse(HttpStatus.SC_MOVED_TEMPORARILY); + for (Pair<String, String> header : response.mResponseHeaders) { + httpResponse.addHeader(header.first, header.second); + } + servedResponseFor(path, request); + } else { + httpResponse = createResponse(HttpStatus.SC_OK); + httpResponse.setEntity(createEntity(response.mResponseData)); + for (Pair<String, String> header : response.mResponseHeaders) { + httpResponse.addHeader(header.first, header.second); + } + servedResponseFor(path, request); + } + StatusLine sl = httpResponse.getStatusLine(); + Log.i(TAG, sl.getStatusCode() + "(" + sl.getReasonPhrase() + ")"); + setDateHeaders(httpResponse); + return httpResponse; + } + + private void setDateHeaders(HttpResponse response) { + response.addHeader("Date", DateUtils.formatDate(new Date(), DateUtils.PATTERN_RFC1123)); + } + + /** + * Create an empty response with the given status. + */ + private HttpResponse createResponse(int status) { + HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, null); + String reason = null; + + // This synchronized silences findbugs. + synchronized (TestWebServer.class) { + if (sReasons == null) { + sReasons = new Hashtable<Integer, String>(); + sReasons.put(HttpStatus.SC_UNAUTHORIZED, "Unauthorized"); + sReasons.put(HttpStatus.SC_NOT_FOUND, "Not Found"); + sReasons.put(HttpStatus.SC_FORBIDDEN, "Forbidden"); + sReasons.put(HttpStatus.SC_MOVED_TEMPORARILY, "Moved Temporarily"); + } + // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is + // Locale-dependent. + reason = sReasons.get(status); + } + + if (reason != null) { + StringBuffer buf = new StringBuffer("<html><head><title>"); + buf.append(reason); + buf.append("</title></head><body>"); + buf.append(reason); + buf.append("</body></html>"); + response.setEntity(createEntity(buf.toString().getBytes())); + } + return response; + } + + /** + * Create a string entity for the given content. + */ + private ByteArrayEntity createEntity(byte[] data) { + ByteArrayEntity entity = new ByteArrayEntity(data); + entity.setContentType("text/html"); + return entity; + } + + private static class ServerThread extends Thread { + private TestWebServer mServer; + private ServerSocket mSocket; + private boolean mIsSsl; + private boolean mIsCancelled; + private SSLContext mSslContext; + + /** + * Defines the keystore contents for the server, BKS version. Holds just a + * single self-generated key. The subject name is "Test Server". + */ + private static final String SERVER_KEYS_BKS = + "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41" + + "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" + + "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" + + "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw" + + "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" + + "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl" + + "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy" + + "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV" + + "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG" + + "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU" + + "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV" + + "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx" + + "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR" + + "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN" + + "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs" + + "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck" + + "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM" + + "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI" + + "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f" + + "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx" + + "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt" + + "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw" + + "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" + + "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw="; + + private static final String PASSWORD = "android"; + + /** + * Loads a keystore from a base64-encoded String. Returns the KeyManager[] + * for the result. + */ + private KeyManager[] getKeyManagers() throws Exception { + byte[] bytes = Base64.decode(SERVER_KEYS_BKS, Base64.DEFAULT); + InputStream inputStream = new ByteArrayInputStream(bytes); + + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(inputStream, PASSWORD.toCharArray()); + inputStream.close(); + + String algorithm = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm); + keyManagerFactory.init(keyStore, PASSWORD.toCharArray()); + + return keyManagerFactory.getKeyManagers(); + } + + + public ServerThread(TestWebServer server, boolean ssl) throws Exception { + super("ServerThread"); + mServer = server; + mIsSsl = ssl; + int retry = 3; + while (true) { + try { + if (mIsSsl) { + mSslContext = SSLContext.getInstance("TLS"); + mSslContext.init(getKeyManagers(), null, null); + mSocket = mSslContext.getServerSocketFactory().createServerSocket( + SSL_SERVER_PORT); + } else { + mSocket = new ServerSocket(SERVER_PORT); + } + return; + } catch (IOException e) { + Log.w(TAG, e); + if (--retry == 0) { + throw e; + } + // sleep in case server socket is still being closed + Thread.sleep(1000); + } + } + } + + @Override + public void run() { + HttpParams params = new BasicHttpParams(); + params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0); + while (!mIsCancelled) { + try { + Socket socket = mSocket.accept(); + DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + conn.bind(socket, params); + + // Determine whether we need to shutdown early before + // parsing the response since conn.close() will crash + // for SSL requests due to UnsupportedOperationException. + HttpRequest request = conn.receiveRequestHeader(); + if (isShutdownRequest(request)) { + mIsCancelled = true; + } + + HttpResponse response = mServer.getResponse(request); + conn.sendResponseHeader(response); + conn.sendResponseEntity(response); + conn.close(); + + } catch (IOException e) { + // normal during shutdown, ignore + Log.w(TAG, e); + } catch (HttpException e) { + Log.w(TAG, e); + } catch (InterruptedException e) { + Log.w(TAG, e); + } catch (UnsupportedOperationException e) { + // DefaultHttpServerConnection's close() throws an + // UnsupportedOperationException. + Log.w(TAG, e); + } + } + try { + mSocket.close(); + } catch (IOException ignored) { + // safe to ignore + } + } + + private boolean isShutdownRequest(HttpRequest request) { + RequestLine requestLine = request.getRequestLine(); + String uriString = requestLine.getUri(); + URI uri = URI.create(uriString); + String path = uri.getPath(); + return path.equals(SHUTDOWN_PREFIX); + } + } +} diff --git a/chromium/net/test/cert_test_util.cc b/chromium/net/test/cert_test_util.cc new file mode 100644 index 00000000000..085a4594c76 --- /dev/null +++ b/chromium/net/test/cert_test_util.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/cert_test_util.h" + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "net/cert/ev_root_ca_metadata.h" +#include "net/cert/x509_certificate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +CertificateList CreateCertificateListFromFile( + const base::FilePath& certs_dir, + const std::string& cert_file, + int format) { + base::FilePath cert_path = certs_dir.AppendASCII(cert_file); + std::string cert_data; + if (!file_util::ReadFileToString(cert_path, &cert_data)) + return CertificateList(); + return X509Certificate::CreateCertificateListFromBytes(cert_data.data(), + cert_data.size(), + format); +} + +scoped_refptr<X509Certificate> ImportCertFromFile( + const base::FilePath& certs_dir, + const std::string& cert_file) { + base::FilePath cert_path = certs_dir.AppendASCII(cert_file); + std::string cert_data; + if (!file_util::ReadFileToString(cert_path, &cert_data)) + return NULL; + + CertificateList certs_in_file = + X509Certificate::CreateCertificateListFromBytes( + cert_data.data(), cert_data.size(), X509Certificate::FORMAT_AUTO); + if (certs_in_file.empty()) + return NULL; + return certs_in_file[0]; +} + +ScopedTestEVPolicy::ScopedTestEVPolicy(EVRootCAMetadata* ev_root_ca_metadata, + const SHA1HashValue& fingerprint, + const char* policy) + : fingerprint_(fingerprint), + ev_root_ca_metadata_(ev_root_ca_metadata) { + EXPECT_TRUE(ev_root_ca_metadata->AddEVCA(fingerprint, policy)); +} + +ScopedTestEVPolicy::~ScopedTestEVPolicy() { + EXPECT_TRUE(ev_root_ca_metadata_->RemoveEVCA(fingerprint_)); +} + +} // namespace net diff --git a/chromium/net/test/cert_test_util.h b/chromium/net/test/cert_test_util.h new file mode 100644 index 00000000000..d4aa4d7d325 --- /dev/null +++ b/chromium/net/test/cert_test_util.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_CERT_TEST_UTIL_H_ +#define NET_TEST_CERT_TEST_UTIL_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "net/cert/x509_cert_types.h" +#include "net/cert/x509_certificate.h" + +namespace base { +class FilePath; +} + +namespace net { + +class EVRootCAMetadata; + +CertificateList CreateCertificateListFromFile(const base::FilePath& certs_dir, + const std::string& cert_file, + int format); + +// Imports a certificate file in the directory net::GetTestCertsDirectory() +// returns. +// |certs_dir| represents the test certificates directory. |cert_file| is the +// name of the certificate file. If cert_file contains multiple certificates, +// the first certificate found will be returned. +scoped_refptr<X509Certificate> ImportCertFromFile(const base::FilePath& certs_dir, + const std::string& cert_file); + +// ScopedTestEVPolicy causes certificates marked with |policy|, issued from a +// root with the given fingerprint, to be treated as EV. |policy| is expressed +// as a string of dotted numbers: i.e. "1.2.3.4". +// This should only be used in unittests as adding a CA twice causes a CHECK +// failure. +class ScopedTestEVPolicy { + public: + ScopedTestEVPolicy(EVRootCAMetadata* ev_root_ca_metadata, + const SHA1HashValue& fingerprint, + const char* policy); + ~ScopedTestEVPolicy(); + + private: + SHA1HashValue fingerprint_; + EVRootCAMetadata* const ev_root_ca_metadata_; +}; + +} // namespace net + +#endif // NET_TEST_CERT_TEST_UTIL_H_ diff --git a/chromium/net/test/embedded_test_server/OWNERS b/chromium/net/test/embedded_test_server/OWNERS new file mode 100644 index 00000000000..e0abbce9eac --- /dev/null +++ b/chromium/net/test/embedded_test_server/OWNERS @@ -0,0 +1,2 @@ +mtomasz@chromium.org +satorux@chromium.org diff --git a/chromium/net/test/embedded_test_server/embedded_test_server.cc b/chromium/net/test/embedded_test_server/embedded_test_server.cc new file mode 100644 index 00000000000..9175d6ca894 --- /dev/null +++ b/chromium/net/test/embedded_test_server/embedded_test_server.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/embedded_test_server.h" + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_restrictions.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/test/embedded_test_server/http_connection.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "net/tools/fetch/http_listen_socket.h" + +namespace net { +namespace test_server { + +namespace { + +class CustomHttpResponse : public HttpResponse { + public: + CustomHttpResponse(const std::string& headers, const std::string& contents) + : headers_(headers), contents_(contents) { + } + + virtual std::string ToResponseString() const OVERRIDE { + return headers_ + "\r\n" + contents_; + } + + private: + std::string headers_; + std::string contents_; + + DISALLOW_COPY_AND_ASSIGN(CustomHttpResponse); +}; + +// Handles |request| by serving a file from under |server_root|. +scoped_ptr<HttpResponse> HandleFileRequest( + const base::FilePath& server_root, + const HttpRequest& request) { + // This is a test-only server. Ignore I/O thread restrictions. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + // Trim the first byte ('/'). + std::string request_path(request.relative_url.substr(1)); + + // Remove the query string if present. + size_t query_pos = request_path.find('?'); + if (query_pos != std::string::npos) + request_path = request_path.substr(0, query_pos); + + base::FilePath file_path(server_root.AppendASCII(request_path)); + std::string file_contents; + if (!file_util::ReadFileToString(file_path, &file_contents)) + return scoped_ptr<HttpResponse>(); + + base::FilePath headers_path( + file_path.AddExtension(FILE_PATH_LITERAL("mock-http-headers"))); + + if (base::PathExists(headers_path)) { + std::string headers_contents; + if (!file_util::ReadFileToString(headers_path, &headers_contents)) + return scoped_ptr<HttpResponse>(); + + scoped_ptr<CustomHttpResponse> http_response( + new CustomHttpResponse(headers_contents, file_contents)); + return http_response.PassAs<HttpResponse>(); + } + + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); + http_response->set_code(HTTP_OK); + http_response->set_content(file_contents); + return http_response.PassAs<HttpResponse>(); +} + +} // namespace + +HttpListenSocket::HttpListenSocket(const SocketDescriptor socket_descriptor, + StreamListenSocket::Delegate* delegate) + : TCPListenSocket(socket_descriptor, delegate) { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void HttpListenSocket::Listen() { + DCHECK(thread_checker_.CalledOnValidThread()); + TCPListenSocket::Listen(); +} + +HttpListenSocket::~HttpListenSocket() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +EmbeddedTestServer::EmbeddedTestServer( + const scoped_refptr<base::SingleThreadTaskRunner>& io_thread) + : io_thread_(io_thread), + port_(-1), + weak_factory_(this) { + DCHECK(io_thread_.get()); + DCHECK(thread_checker_.CalledOnValidThread()); +} + +EmbeddedTestServer::~EmbeddedTestServer() { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (Started() && !ShutdownAndWaitUntilComplete()) { + LOG(ERROR) << "EmbeddedTestServer failed to shut down."; + } +} + +bool EmbeddedTestServer::InitializeAndWaitUntilReady() { + DCHECK(thread_checker_.CalledOnValidThread()); + + base::RunLoop run_loop; + if (!io_thread_->PostTaskAndReply( + FROM_HERE, + base::Bind(&EmbeddedTestServer::InitializeOnIOThread, + base::Unretained(this)), + run_loop.QuitClosure())) { + return false; + } + run_loop.Run(); + + return Started() && base_url_.is_valid(); +} + +bool EmbeddedTestServer::ShutdownAndWaitUntilComplete() { + DCHECK(thread_checker_.CalledOnValidThread()); + + base::RunLoop run_loop; + if (!io_thread_->PostTaskAndReply( + FROM_HERE, + base::Bind(&EmbeddedTestServer::ShutdownOnIOThread, + base::Unretained(this)), + run_loop.QuitClosure())) { + return false; + } + run_loop.Run(); + + return true; +} + +void EmbeddedTestServer::InitializeOnIOThread() { + DCHECK(io_thread_->BelongsToCurrentThread()); + DCHECK(!Started()); + + SocketDescriptor socket_descriptor = + TCPListenSocket::CreateAndBindAnyPort("127.0.0.1", &port_); + if (socket_descriptor == TCPListenSocket::kInvalidSocket) + return; + + listen_socket_ = new HttpListenSocket(socket_descriptor, this); + listen_socket_->Listen(); + + IPEndPoint address; + int result = listen_socket_->GetLocalAddress(&address); + if (result == OK) { + base_url_ = GURL(std::string("http://") + address.ToString()); + } else { + LOG(ERROR) << "GetLocalAddress failed: " << ErrorToString(result); + } +} + +void EmbeddedTestServer::ShutdownOnIOThread() { + DCHECK(io_thread_->BelongsToCurrentThread()); + + listen_socket_ = NULL; // Release the listen socket. + STLDeleteContainerPairSecondPointers(connections_.begin(), + connections_.end()); + connections_.clear(); +} + +void EmbeddedTestServer::HandleRequest(HttpConnection* connection, + scoped_ptr<HttpRequest> request) { + DCHECK(io_thread_->BelongsToCurrentThread()); + + bool request_handled = false; + + for (size_t i = 0; i < request_handlers_.size(); ++i) { + scoped_ptr<HttpResponse> response = + request_handlers_[i].Run(*request.get()); + if (response.get()) { + connection->SendResponse(response.Pass()); + request_handled = true; + break; + } + } + + if (!request_handled) { + LOG(WARNING) << "Request not handled. Returning 404: " + << request->relative_url; + scoped_ptr<BasicHttpResponse> not_found_response(new BasicHttpResponse); + not_found_response->set_code(HTTP_NOT_FOUND); + connection->SendResponse( + not_found_response.PassAs<HttpResponse>()); + } + + // Drop the connection, since we do not support multiple requests per + // connection. + connections_.erase(connection->socket_.get()); + delete connection; +} + +GURL EmbeddedTestServer::GetURL(const std::string& relative_url) const { + DCHECK(StartsWithASCII(relative_url, "/", true /* case_sensitive */)) + << relative_url; + return base_url_.Resolve(relative_url); +} + +void EmbeddedTestServer::ServeFilesFromDirectory( + const base::FilePath& directory) { + RegisterRequestHandler(base::Bind(&HandleFileRequest, directory)); +} + +void EmbeddedTestServer::RegisterRequestHandler( + const HandleRequestCallback& callback) { + request_handlers_.push_back(callback); +} + +void EmbeddedTestServer::DidAccept(StreamListenSocket* server, + StreamListenSocket* connection) { + DCHECK(io_thread_->BelongsToCurrentThread()); + + HttpConnection* http_connection = new HttpConnection( + connection, + base::Bind(&EmbeddedTestServer::HandleRequest, + weak_factory_.GetWeakPtr())); + connections_[connection] = http_connection; +} + +void EmbeddedTestServer::DidRead(StreamListenSocket* connection, + const char* data, + int length) { + DCHECK(io_thread_->BelongsToCurrentThread()); + + HttpConnection* http_connection = FindConnection(connection); + if (http_connection == NULL) { + LOG(WARNING) << "Unknown connection."; + return; + } + http_connection->ReceiveData(std::string(data, length)); +} + +void EmbeddedTestServer::DidClose(StreamListenSocket* connection) { + DCHECK(io_thread_->BelongsToCurrentThread()); + + HttpConnection* http_connection = FindConnection(connection); + if (http_connection == NULL) { + LOG(WARNING) << "Unknown connection."; + return; + } + delete http_connection; + connections_.erase(connection); +} + +HttpConnection* EmbeddedTestServer::FindConnection( + StreamListenSocket* socket) { + DCHECK(io_thread_->BelongsToCurrentThread()); + + std::map<StreamListenSocket*, HttpConnection*>::iterator it = + connections_.find(socket); + if (it == connections_.end()) { + return NULL; + } + return it->second; +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/embedded_test_server.h b/chromium/net/test/embedded_test_server/embedded_test_server.h new file mode 100644 index 00000000000..879c4a947f9 --- /dev/null +++ b/chromium/net/test/embedded_test_server/embedded_test_server.h @@ -0,0 +1,172 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_ +#define NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "net/socket/tcp_listen_socket.h" +#include "url/gurl.h" + +namespace base { +class FilePath; +} + +namespace net { +namespace test_server { + +class HttpConnection; +class HttpResponse; +struct HttpRequest; + +// This class is required to be able to have composition instead of inheritance, +class HttpListenSocket : public TCPListenSocket { + public: + HttpListenSocket(const SocketDescriptor socket_descriptor, + StreamListenSocket::Delegate* delegate); + virtual void Listen(); + + private: + virtual ~HttpListenSocket(); + + base::ThreadChecker thread_checker_; +}; + +// Class providing an HTTP server for testing purpose. This is a basic server +// providing only an essential subset of HTTP/1.1 protocol. Especially, +// it assumes that the request syntax is correct. It *does not* support +// a Chunked Transfer Encoding. +// +// The common use case is below: +// +// base::Thread io_thread_; +// scoped_ptr<EmbeddedTestServer> test_server_; +// +// void SetUp() { +// base::Thread::Options thread_options; +// thread_options.message_loop_type = MessageLoop::TYPE_IO; +// ASSERT_TRUE(io_thread_.StartWithOptions(thread_options)); +// +// test_server_.reset( +// new EmbeddedTestServer(io_thread_.message_loop_proxy())); +// ASSERT_TRUE(test_server_.InitializeAndWaitUntilReady()); +// test_server_->RegisterRequestHandler( +// base::Bind(&FooTest::HandleRequest, base::Unretained(this))); +// } +// +// scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) { +// GURL absolute_url = test_server_->GetURL(request.relative_url); +// if (absolute_url.path() != "/test") +// return scoped_ptr<HttpResponse>(); +// +// scoped_ptr<HttpResponse> http_response(new HttpResponse()); +// http_response->set_code(test_server::SUCCESS); +// http_response->set_content("hello"); +// http_response->set_content_type("text/plain"); +// return http_response.Pass(); +// } +// +class EmbeddedTestServer : public StreamListenSocket::Delegate { + public: + typedef base::Callback<scoped_ptr<HttpResponse>( + const HttpRequest& request)> HandleRequestCallback; + + // Creates a http test server. |io_thread| is a task runner + // with IO message loop, used as a backend thread. + // InitializeAndWaitUntilReady() must be called to start the server. + explicit EmbeddedTestServer( + const scoped_refptr<base::SingleThreadTaskRunner>& io_thread); + virtual ~EmbeddedTestServer(); + + // Initializes and waits until the server is ready to accept requests. + bool InitializeAndWaitUntilReady() WARN_UNUSED_RESULT; + + // Shuts down the http server and waits until the shutdown is complete. + bool ShutdownAndWaitUntilComplete() WARN_UNUSED_RESULT; + + // Checks if the server is started. + bool Started() const { + return listen_socket_.get() != NULL; + } + + // Returns the base URL to the server, which looks like + // http://127.0.0.1:<port>/, where <port> is the actual port number used by + // the server. + const GURL& base_url() const { return base_url_; } + + // Returns a URL to the server based on the given relative URL, which + // should start with '/'. For example: GetURL("/path?query=foo") => + // http://127.0.0.1:<port>/path?query=foo. + GURL GetURL(const std::string& relative_url) const; + + // Returns the port number used by the server. + int port() const { return port_; } + + // Registers request handler which serves files from |directory|. + // For instance, a request to "/foo.html" is served by "foo.html" under + // |directory|. Files under sub directories are also handled in the same way + // (i.e. "/foo/bar.html" is served by "foo/bar.html" under |directory|). + void ServeFilesFromDirectory(const base::FilePath& directory); + + // The most general purpose method. Any request processing can be added using + // this method. Takes ownership of the object. The |callback| is called + // on UI thread. + void RegisterRequestHandler(const HandleRequestCallback& callback); + + private: + // Initializes and starts the server. If initialization succeeds, Starts() + // will return true. + void InitializeOnIOThread(); + + // Shuts down the server. + void ShutdownOnIOThread(); + + // Handles a request when it is parsed. It passes the request to registed + // request handlers and sends a http response. + void HandleRequest(HttpConnection* connection, + scoped_ptr<HttpRequest> request); + + // StreamListenSocket::Delegate overrides: + virtual void DidAccept(StreamListenSocket* server, + StreamListenSocket* connection) OVERRIDE; + virtual void DidRead(StreamListenSocket* connection, + const char* data, + int length) OVERRIDE; + virtual void DidClose(StreamListenSocket* connection) OVERRIDE; + + HttpConnection* FindConnection(StreamListenSocket* socket); + + scoped_refptr<base::SingleThreadTaskRunner> io_thread_; + + scoped_refptr<HttpListenSocket> listen_socket_; + int port_; + GURL base_url_; + + // Owns the HttpConnection objects. + std::map<StreamListenSocket*, HttpConnection*> connections_; + + // Vector of registered request handlers. + std::vector<HandleRequestCallback> request_handlers_; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory<EmbeddedTestServer> weak_factory_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(EmbeddedTestServer); +}; + +} // namespace test_servers +} // namespace net + +#endif // NET_TEST_EMBEDDED_TEST_SERVER_EMBEDDED_TEST_SERVER_H_ diff --git a/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc b/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc new file mode 100644 index 00000000000..35d0fd414e1 --- /dev/null +++ b/chromium/net/test/embedded_test_server/embedded_test_server_unittest.cc @@ -0,0 +1,244 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/embedded_test_server.h" + +#include "base/strings/stringprintf.h" +#include "base/threading/thread.h" +#include "net/http/http_response_headers.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_fetcher_delegate.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test_server { + +namespace { + +// Gets the content from the given URLFetcher. +std::string GetContentFromFetcher(const URLFetcher& fetcher) { + std::string result; + const bool success = fetcher.GetResponseAsString(&result); + EXPECT_TRUE(success); + return result; +} + +// Gets the content type from the given URLFetcher. +std::string GetContentTypeFromFetcher(const URLFetcher& fetcher) { + const HttpResponseHeaders* headers = fetcher.GetResponseHeaders(); + if (headers) { + std::string content_type; + if (headers->GetMimeType(&content_type)) + return content_type; + } + return std::string(); +} + +} // namespace + +class EmbeddedTestServerTest : public testing::Test, + public URLFetcherDelegate { + public: + EmbeddedTestServerTest() + : num_responses_received_(0), + num_responses_expected_(0), + io_thread_("io_thread") { + } + + virtual void SetUp() OVERRIDE { + base::Thread::Options thread_options; + thread_options.message_loop_type = base::MessageLoop::TYPE_IO; + ASSERT_TRUE(io_thread_.StartWithOptions(thread_options)); + + request_context_getter_ = new TestURLRequestContextGetter( + io_thread_.message_loop_proxy()); + + server_.reset(new EmbeddedTestServer(io_thread_.message_loop_proxy())); + ASSERT_TRUE(server_->InitializeAndWaitUntilReady()); + } + + virtual void TearDown() OVERRIDE { + ASSERT_TRUE(server_->ShutdownAndWaitUntilComplete()); + } + + // URLFetcherDelegate override. + virtual void OnURLFetchComplete(const URLFetcher* source) OVERRIDE { + ++num_responses_received_; + if (num_responses_received_ == num_responses_expected_) + base::MessageLoop::current()->Quit(); + } + + // Waits until the specified number of responses are received. + void WaitForResponses(int num_responses) { + num_responses_received_ = 0; + num_responses_expected_ = num_responses; + // Will be terminated in OnURLFetchComplete(). + base::MessageLoop::current()->Run(); + } + + // Handles |request| sent to |path| and returns the response per |content|, + // |content type|, and |code|. Saves the request URL for verification. + scoped_ptr<HttpResponse> HandleRequest(const std::string& path, + const std::string& content, + const std::string& content_type, + HttpStatusCode code, + const HttpRequest& request) { + request_relative_url_ = request.relative_url; + + GURL absolute_url = server_->GetURL(request.relative_url); + if (absolute_url.path() == path) { + scoped_ptr<BasicHttpResponse> http_response(new BasicHttpResponse); + http_response->set_code(code); + http_response->set_content(content); + http_response->set_content_type(content_type); + return http_response.PassAs<HttpResponse>(); + } + + return scoped_ptr<HttpResponse>(); + } + + protected: + int num_responses_received_; + int num_responses_expected_; + std::string request_relative_url_; + base::Thread io_thread_; + scoped_refptr<TestURLRequestContextGetter> request_context_getter_; + scoped_ptr<EmbeddedTestServer> server_; +}; + +TEST_F(EmbeddedTestServerTest, GetBaseURL) { + EXPECT_EQ(base::StringPrintf("http://127.0.0.1:%d/", server_->port()), + server_->base_url().spec()); +} + +TEST_F(EmbeddedTestServerTest, GetURL) { + EXPECT_EQ(base::StringPrintf("http://127.0.0.1:%d/path?query=foo", + server_->port()), + server_->GetURL("/path?query=foo").spec()); +} + +TEST_F(EmbeddedTestServerTest, RegisterRequestHandler) { + server_->RegisterRequestHandler( + base::Bind(&EmbeddedTestServerTest::HandleRequest, + base::Unretained(this), + "/test", + "<b>Worked!</b>", + "text/html", + HTTP_OK)); + + scoped_ptr<URLFetcher> fetcher( + URLFetcher::Create(server_->GetURL("/test?q=foo"), + URLFetcher::GET, + this)); + fetcher->SetRequestContext(request_context_getter_.get()); + fetcher->Start(); + WaitForResponses(1); + + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status()); + EXPECT_EQ(HTTP_OK, fetcher->GetResponseCode()); + EXPECT_EQ("<b>Worked!</b>", GetContentFromFetcher(*fetcher)); + EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher)); + + EXPECT_EQ("/test?q=foo", request_relative_url_); +} + +TEST_F(EmbeddedTestServerTest, ServeFilesFromDirectory) { + base::FilePath src_dir; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)); + server_->ServeFilesFromDirectory( + src_dir.AppendASCII("net").AppendASCII("data")); + + scoped_ptr<URLFetcher> fetcher( + URLFetcher::Create(server_->GetURL("/test.html"), + URLFetcher::GET, + this)); + fetcher->SetRequestContext(request_context_getter_.get()); + fetcher->Start(); + WaitForResponses(1); + + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status()); + EXPECT_EQ(HTTP_OK, fetcher->GetResponseCode()); + EXPECT_EQ("<p>Hello World!</p>", GetContentFromFetcher(*fetcher)); + EXPECT_EQ("", GetContentTypeFromFetcher(*fetcher)); +} + +TEST_F(EmbeddedTestServerTest, DefaultNotFoundResponse) { + scoped_ptr<URLFetcher> fetcher( + URLFetcher::Create(server_->GetURL("/non-existent"), + URLFetcher::GET, + this)); + fetcher->SetRequestContext(request_context_getter_.get()); + + fetcher->Start(); + WaitForResponses(1); + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher->GetStatus().status()); + EXPECT_EQ(HTTP_NOT_FOUND, fetcher->GetResponseCode()); +} + +TEST_F(EmbeddedTestServerTest, ConcurrentFetches) { + server_->RegisterRequestHandler( + base::Bind(&EmbeddedTestServerTest::HandleRequest, + base::Unretained(this), + "/test1", + "Raspberry chocolate", + "text/html", + HTTP_OK)); + server_->RegisterRequestHandler( + base::Bind(&EmbeddedTestServerTest::HandleRequest, + base::Unretained(this), + "/test2", + "Vanilla chocolate", + "text/html", + HTTP_OK)); + server_->RegisterRequestHandler( + base::Bind(&EmbeddedTestServerTest::HandleRequest, + base::Unretained(this), + "/test3", + "No chocolates", + "text/plain", + HTTP_NOT_FOUND)); + + scoped_ptr<URLFetcher> fetcher1 = scoped_ptr<URLFetcher>( + URLFetcher::Create(server_->GetURL("/test1"), + URLFetcher::GET, + this)); + fetcher1->SetRequestContext(request_context_getter_.get()); + scoped_ptr<URLFetcher> fetcher2 = scoped_ptr<URLFetcher>( + URLFetcher::Create(server_->GetURL("/test2"), + URLFetcher::GET, + this)); + fetcher2->SetRequestContext(request_context_getter_.get()); + scoped_ptr<URLFetcher> fetcher3 = scoped_ptr<URLFetcher>( + URLFetcher::Create(server_->GetURL("/test3"), + URLFetcher::GET, + this)); + fetcher3->SetRequestContext(request_context_getter_.get()); + + // Fetch the three URLs concurrently. + fetcher1->Start(); + fetcher2->Start(); + fetcher3->Start(); + WaitForResponses(3); + + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher1->GetStatus().status()); + EXPECT_EQ(HTTP_OK, fetcher1->GetResponseCode()); + EXPECT_EQ("Raspberry chocolate", GetContentFromFetcher(*fetcher1)); + EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher1)); + + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher2->GetStatus().status()); + EXPECT_EQ(HTTP_OK, fetcher2->GetResponseCode()); + EXPECT_EQ("Vanilla chocolate", GetContentFromFetcher(*fetcher2)); + EXPECT_EQ("text/html", GetContentTypeFromFetcher(*fetcher2)); + + EXPECT_EQ(URLRequestStatus::SUCCESS, fetcher3->GetStatus().status()); + EXPECT_EQ(HTTP_NOT_FOUND, fetcher3->GetResponseCode()); + EXPECT_EQ("No chocolates", GetContentFromFetcher(*fetcher3)); + EXPECT_EQ("text/plain", GetContentTypeFromFetcher(*fetcher3)); +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/http_connection.cc b/chromium/net/test/embedded_test_server/http_connection.cc new file mode 100644 index 00000000000..8b5317e320b --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_connection.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/http_connection.h" + +#include "net/socket/stream_listen_socket.h" +#include "net/test/embedded_test_server/http_response.h" + +namespace net { +namespace test_server { + +HttpConnection::HttpConnection(StreamListenSocket* socket, + const HandleRequestCallback& callback) + : socket_(socket), + callback_(callback) { +} + +HttpConnection::~HttpConnection() { +} + +void HttpConnection::SendResponse(scoped_ptr<HttpResponse> response) const { + const std::string response_string = response->ToResponseString(); + socket_->Send(response_string.c_str(), response_string.length()); +} + +void HttpConnection::ReceiveData(const base::StringPiece& data) { + request_parser_.ProcessChunk(data); + if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) { + callback_.Run(this, request_parser_.GetRequest()); + } +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/http_connection.h b/chromium/net/test/embedded_test_server/http_connection.h new file mode 100644 index 00000000000..da9353404c6 --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_connection.h @@ -0,0 +1,58 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_EMBEDDED_TEST_SERVER_HTTP_CONNECTION_H_ +#define NET_TEST_EMBEDDED_TEST_SERVER_HTTP_CONNECTION_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string_piece.h" +#include "net/test/embedded_test_server/http_request.h" + +namespace net { + +class StreamListenSocket; + +namespace test_server { + +class HttpConnection; +class HttpResponse; + +// Calblack called when a request is parsed. Response should be sent +// using HttpConnection::SendResponse() on the |connection| argument. +typedef base::Callback<void(HttpConnection* connection, + scoped_ptr<HttpRequest> request)> + HandleRequestCallback; + +// Wraps the connection socket. Accepts incoming data and sends responses. +// If a valid request is parsed, then |callback_| is invoked. +class HttpConnection { + public: + HttpConnection(StreamListenSocket* socket, + const HandleRequestCallback& callback); + ~HttpConnection(); + + // Sends the HTTP response to the client. + void SendResponse(scoped_ptr<HttpResponse> response) const; + + private: + friend class EmbeddedTestServer; + + // Accepts raw chunk of data from the client. Internally, passes it to the + // HttpRequestParser class. If a request is parsed, then |callback_| is + // called. + void ReceiveData(const base::StringPiece& data); + + scoped_refptr<StreamListenSocket> socket_; + const HandleRequestCallback callback_; + HttpRequestParser request_parser_; + + DISALLOW_COPY_AND_ASSIGN(HttpConnection); +}; + +} // namespace test_server +} // namespace net + +#endif // NET_TEST_EMBEDDED_TEST_SERVER_HTTP_CONNECTION_H_ diff --git a/chromium/net/test/embedded_test_server/http_request.cc b/chromium/net/test/embedded_test_server/http_request.cc new file mode 100644 index 00000000000..8ac76d35923 --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_request.cc @@ -0,0 +1,202 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/http_request.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" + +namespace net { +namespace test_server { + +namespace { + +size_t kRequestSizeLimit = 64 * 1024 * 1024; // 64 mb. + +// Helper function used to trim tokens in http request headers. +std::string Trim(const std::string& value) { + std::string result; + TrimString(value, " \t", &result); + return result; +} + +} // namespace + +HttpRequest::HttpRequest() : method(METHOD_UNKNOWN), + has_content(false) { +} + +HttpRequest::~HttpRequest() { +} + +HttpRequestParser::HttpRequestParser() + : http_request_(new HttpRequest()), + buffer_position_(0), + state_(STATE_HEADERS), + declared_content_length_(0) { +} + +HttpRequestParser::~HttpRequestParser() { +} + +void HttpRequestParser::ProcessChunk(const base::StringPiece& data) { + data.AppendToString(&buffer_); + DCHECK_LE(buffer_.size() + data.size(), kRequestSizeLimit) << + "The HTTP request is too large."; +} + +std::string HttpRequestParser::ShiftLine() { + size_t eoln_position = buffer_.find("\r\n", buffer_position_); + DCHECK_NE(std::string::npos, eoln_position); + const int line_length = eoln_position - buffer_position_; + std::string result = buffer_.substr(buffer_position_, line_length); + buffer_position_ += line_length + 2; + return result; +} + +HttpRequestParser::ParseResult HttpRequestParser::ParseRequest() { + DCHECK_NE(STATE_ACCEPTED, state_); + // Parse the request from beginning. However, entire request may not be + // available in the buffer. + if (state_ == STATE_HEADERS) { + if (ParseHeaders() == ACCEPTED) + return ACCEPTED; + } + // This should not be 'else if' of the previous block, as |state_| can be + // changed in ParseHeaders(). + if (state_ == STATE_CONTENT) { + if (ParseContent() == ACCEPTED) + return ACCEPTED; + } + return WAITING; +} + +HttpRequestParser::ParseResult HttpRequestParser::ParseHeaders() { + // Check if the all request headers are available. + if (buffer_.find("\r\n\r\n", buffer_position_) == std::string::npos) + return WAITING; + + // Parse request's the first header line. + // Request main main header, eg. GET /foobar.html HTTP/1.1 + { + const std::string header_line = ShiftLine(); + std::vector<std::string> header_line_tokens; + base::SplitString(header_line, ' ', &header_line_tokens); + DCHECK_EQ(3u, header_line_tokens.size()); + // Method. + http_request_->method = GetMethodType(StringToLowerASCII( + header_line_tokens[0])); + // Address. + // Don't build an absolute URL as the parser does not know (should not + // know) anything about the server address. + http_request_->relative_url = header_line_tokens[1]; + // Protocol. + const std::string protocol = StringToLowerASCII(header_line_tokens[2]); + CHECK(protocol == "http/1.0" || protocol == "http/1.1") << + "Protocol not supported: " << protocol; + } + + // Parse further headers. + { + std::string header_name; + while (true) { + std::string header_line = ShiftLine(); + if (header_line.empty()) + break; + + if (header_line[0] == ' ' || header_line[0] == '\t') { + // Continuation of the previous multi-line header. + std::string header_value = + Trim(header_line.substr(1, header_line.size() - 1)); + http_request_->headers[header_name] += " " + header_value; + } else { + // New header. + size_t delimiter_pos = header_line.find(":"); + DCHECK_NE(std::string::npos, delimiter_pos) << "Syntax error."; + header_name = Trim(header_line.substr(0, delimiter_pos)); + std::string header_value = Trim(header_line.substr( + delimiter_pos + 1, + header_line.size() - delimiter_pos - 1)); + http_request_->headers[header_name] = header_value; + } + } + } + + // Headers done. Is any content data attached to the request? + declared_content_length_ = 0; + if (http_request_->headers.count("Content-Length") > 0) { + http_request_->has_content = true; + const bool success = base::StringToSizeT( + http_request_->headers["Content-Length"], + &declared_content_length_); + DCHECK(success) << "Malformed Content-Length header's value."; + } + if (declared_content_length_ == 0) { + // No content data, so parsing is finished. + state_ = STATE_ACCEPTED; + return ACCEPTED; + } + + // The request has not yet been parsed yet, content data is still to be + // processed. + state_ = STATE_CONTENT; + return WAITING; +} + +HttpRequestParser::ParseResult HttpRequestParser::ParseContent() { + const size_t available_bytes = buffer_.size() - buffer_position_; + const size_t fetch_bytes = std::min( + available_bytes, + declared_content_length_ - http_request_->content.size()); + http_request_->content.append(buffer_.data() + buffer_position_, + fetch_bytes); + buffer_position_ += fetch_bytes; + + if (declared_content_length_ == http_request_->content.size()) { + state_ = STATE_ACCEPTED; + return ACCEPTED; + } + + state_ = STATE_CONTENT; + return WAITING; +} + +scoped_ptr<HttpRequest> HttpRequestParser::GetRequest() { + DCHECK_EQ(STATE_ACCEPTED, state_); + scoped_ptr<HttpRequest> result = http_request_.Pass(); + + // Prepare for parsing a new request. + state_ = STATE_HEADERS; + http_request_.reset(new HttpRequest()); + buffer_.clear(); + buffer_position_ = 0; + declared_content_length_ = 0; + + return result.Pass(); +} + +HttpMethod HttpRequestParser::GetMethodType(const std::string& token) const { + if (token == "get") { + return METHOD_GET; + } else if (token == "head") { + return METHOD_HEAD; + } else if (token == "post") { + return METHOD_POST; + } else if (token == "put") { + return METHOD_PUT; + } else if (token == "delete") { + return METHOD_DELETE; + } else if (token == "patch") { + return METHOD_PATCH; + } + NOTREACHED() << "Method not implemented: " << token; + return METHOD_UNKNOWN; +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/http_request.h b/chromium/net/test/embedded_test_server/http_request.h new file mode 100644 index 00000000000..1e849d87d25 --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_request.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_EMBEDDED_TEST_SERVER_HTTP_REQUEST_H_ +#define NET_TEST_EMBEDDED_TEST_SERVER_HTTP_REQUEST_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" + +namespace net { +namespace test_server { + +// Methods of HTTP requests supported by the test HTTP server. +enum HttpMethod { + METHOD_UNKNOWN, + METHOD_GET, + METHOD_HEAD, + METHOD_POST, + METHOD_PUT, + METHOD_DELETE, + METHOD_PATCH, +}; + +// Represents a HTTP request. Since it can be big, use scoped_ptr to pass it +// instead of copying. However, the struct is copyable so tests can save and +// examine a HTTP request. +struct HttpRequest { + HttpRequest(); + ~HttpRequest(); + + std::string relative_url; // Starts with '/'. Example: "/test?query=foo" + HttpMethod method; + std::map<std::string, std::string> headers; + std::string content; + bool has_content; +}; + +// Parses the input data and produces a valid HttpRequest object. If there is +// more than one request in one chunk, then only the first one will be parsed. +// The common use is as below: +// HttpRequestParser parser; +// (...) +// void OnDataChunkReceived(Socket* socket, const char* data, int size) { +// parser.ProcessChunk(std::string(data, size)); +// if (parser.ParseRequest() == HttpRequestParser::ACCEPTED) { +// scoped_ptr<HttpRequest> request = parser.GetRequest(); +// (... process the request ...) +// } +class HttpRequestParser { + public: + // Parsing result. + enum ParseResult { + WAITING, // A request is not completed yet, waiting for more data. + ACCEPTED, // A request has been parsed and it is ready to be processed. + }; + + // Parser state. + enum State { + STATE_HEADERS, // Waiting for a request headers. + STATE_CONTENT, // Waiting for content data. + STATE_ACCEPTED, // Request has been parsed. + }; + + HttpRequestParser(); + ~HttpRequestParser(); + + // Adds chunk of data into the internal buffer. + void ProcessChunk(const base::StringPiece& data); + + // Parses the http request (including data - if provided). + // If returns ACCEPTED, then it means that the whole request has been found + // in the internal buffer (and parsed). After calling GetRequest(), it will be + // ready to parse another request. + ParseResult ParseRequest(); + + // Retrieves parsed request. Can be only called, when the parser is in + // STATE_ACCEPTED state. After calling it, the parser is ready to parse + // another request. + scoped_ptr<HttpRequest> GetRequest(); + + private: + HttpMethod GetMethodType(const std::string& token) const; + + // Parses headers and returns ACCEPTED if whole request was parsed. Otherwise + // returns WAITING. + ParseResult ParseHeaders(); + + // Parses request's content data and returns ACCEPTED if all of it have been + // processed. Chunked Transfer Encoding *is not* supported. + ParseResult ParseContent(); + + // Fetches the next line from the buffer. Result does not contain \r\n. + // Returns an empty string for an empty line. It will assert if there is + // no line available. + std::string ShiftLine(); + + scoped_ptr<HttpRequest> http_request_; + std::string buffer_; + size_t buffer_position_; // Current position in the internal buffer. + State state_; + // Content length of the request currently being parsed. + size_t declared_content_length_; + + DISALLOW_COPY_AND_ASSIGN(HttpRequestParser); +}; + +} // namespace test_server +} // namespace net + +#endif // NET_TEST_EMBEDDED_TEST_SERVER_HTTP_REQUEST_H_ diff --git a/chromium/net/test/embedded_test_server/http_request_unittest.cc b/chromium/net/test/embedded_test_server/http_request_unittest.cc new file mode 100644 index 00000000000..b45742cf37b --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_request_unittest.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/http_request.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test_server { + +TEST(HttpRequestTest, ParseRequest) { + HttpRequestParser parser; + + // Process request in chunks to check if the parser deals with border cases. + // Also, check multi-line headers as well as multiple requests in the same + // chunk. This basically should cover all the simplest border cases. + parser.ProcessChunk("POST /foobar.html HTTP/1.1\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + parser.ProcessChunk("Host: localhost:1234\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + parser.ProcessChunk("Multi-line-header: abcd\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + parser.ProcessChunk(" efgh\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + parser.ProcessChunk(" ijkl\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + parser.ProcessChunk("Content-Length: 10\r\n\r\n"); + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); + // Content data and another request in the same chunk (possible in http/1.1). + parser.ProcessChunk("1234567890GET /another.html HTTP/1.1\r\n\r\n"); + ASSERT_EQ(HttpRequestParser::ACCEPTED, parser.ParseRequest()); + + // Fetch the first request and validate it. + { + scoped_ptr<HttpRequest> request = parser.GetRequest(); + EXPECT_EQ("/foobar.html", request->relative_url); + EXPECT_EQ(METHOD_POST, request->method); + EXPECT_EQ("1234567890", request->content); + ASSERT_EQ(3u, request->headers.size()); + + EXPECT_EQ(1u, request->headers.count("Host")); + EXPECT_EQ(1u, request->headers.count("Multi-line-header")); + EXPECT_EQ(1u, request->headers.count("Content-Length")); + + EXPECT_EQ("localhost:1234", request->headers["Host"]); + EXPECT_EQ("abcd efgh ijkl", request->headers["Multi-line-header"]); + EXPECT_EQ("10", request->headers["Content-Length"]); + } + + // No other request available yet since we do not support multiple requests + // per connection. + EXPECT_EQ(HttpRequestParser::WAITING, parser.ParseRequest()); +} + +TEST(HttpRequestTest, ParseRequestWithEmptyBody) { + HttpRequestParser parser; + + parser.ProcessChunk("POST /foobar.html HTTP/1.1\r\n"); + parser.ProcessChunk("Content-Length: 0\r\n\r\n"); + ASSERT_EQ(HttpRequestParser::ACCEPTED, parser.ParseRequest()); + + scoped_ptr<HttpRequest> request = parser.GetRequest(); + EXPECT_EQ("", request->content); + EXPECT_TRUE(request->has_content); + EXPECT_EQ(1u, request->headers.count("Content-Length")); + EXPECT_EQ("0", request->headers["Content-Length"]); +} + +TEST(HttpRequestTest, ParseRequestWithoutBody) { + HttpRequestParser parser; + + parser.ProcessChunk("POST /foobar.html HTTP/1.1\r\n\r\n"); + ASSERT_EQ(HttpRequestParser::ACCEPTED, parser.ParseRequest()); + + scoped_ptr<HttpRequest> request = parser.GetRequest(); + EXPECT_EQ("", request->content); + EXPECT_FALSE(request->has_content); +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/http_response.cc b/chromium/net/test/embedded_test_server/http_response.cc new file mode 100644 index 00000000000..04155b5ffa5 --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_response.cc @@ -0,0 +1,58 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/http_response.h" + +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "net/http/http_status_code.h" + +namespace net { +namespace test_server { + +HttpResponse::~HttpResponse() { +} + +BasicHttpResponse::BasicHttpResponse() : code_(HTTP_OK) { +} + +BasicHttpResponse::~BasicHttpResponse() { +} + +std::string BasicHttpResponse::ToResponseString() const { + // Response line with headers. + std::string response_builder; + + std::string http_reason_phrase(GetHttpReasonPhrase(code_)); + + // TODO(mtomasz): For http/1.0 requests, send http/1.0. + base::StringAppendF(&response_builder, + "HTTP/1.1 %d %s\r\n", + code_, + http_reason_phrase.c_str()); + base::StringAppendF(&response_builder, "Connection: close\r\n"); + base::StringAppendF(&response_builder, + "Content-Length: %" PRIuS "\r\n", + content_.size()); + base::StringAppendF(&response_builder, + "Content-Type: %s\r\n", + content_type_.c_str()); + for (size_t i = 0; i < custom_headers_.size(); ++i) { + const std::string& header_name = custom_headers_[i].first; + const std::string& header_value = custom_headers_[i].second; + DCHECK(header_value.find_first_of("\n\r") == std::string::npos) << + "Malformed header value."; + base::StringAppendF(&response_builder, + "%s: %s\r\n", + header_name.c_str(), + header_value.c_str()); + } + base::StringAppendF(&response_builder, "\r\n"); + + return response_builder + content_; +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/embedded_test_server/http_response.h b/chromium/net/test/embedded_test_server/http_response.h new file mode 100644 index 00000000000..422a52553ea --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_response.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_ +#define NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "net/http/http_status_code.h" + +namespace net { +namespace test_server { + +// Interface for HTTP response implementations. +class HttpResponse{ + public: + virtual ~HttpResponse(); + + // Returns raw contents to be written to the network socket + // in response. If you intend to make this a valid HTTP response, + // it should start with "HTTP/x.x" line, followed by response headers. + virtual std::string ToResponseString() const = 0; +}; + +// This class is used to handle basic HTTP responses with commonly used +// response headers such as "Content-Type". +class BasicHttpResponse : public HttpResponse { + public: + BasicHttpResponse(); + virtual ~BasicHttpResponse(); + + // The response code. + HttpStatusCode code() const { return code_; } + void set_code(HttpStatusCode code) { code_ = code; } + + // The content of the response. + const std::string& content() const { return content_; } + void set_content(const std::string& content) { content_ = content; } + + // The content type. + const std::string& content_type() const { return content_type_; } + void set_content_type(const std::string& content_type) { + content_type_ = content_type; + } + + // Adds a custom header. + void AddCustomHeader(const std::string& key, const std::string& value) { + custom_headers_.push_back(std::make_pair(key, value)); + } + + // Generates and returns a http response string. + virtual std::string ToResponseString() const OVERRIDE; + + private: + HttpStatusCode code_; + std::string content_; + std::string content_type_; + std::vector<std::pair<std::string, std::string> > custom_headers_; + + DISALLOW_COPY_AND_ASSIGN(BasicHttpResponse); +}; + +} // namespace test_server +} // namespace net + +#endif // NET_TEST_EMBEDDED_TEST_SERVER_HTTP_RESPONSE_H_ diff --git a/chromium/net/test/embedded_test_server/http_response_unittest.cc b/chromium/net/test/embedded_test_server/http_response_unittest.cc new file mode 100644 index 00000000000..83b767f5fa8 --- /dev/null +++ b/chromium/net/test/embedded_test_server/http_response_unittest.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/embedded_test_server/http_response.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test_server { + +TEST(HttpResponseTest, GenerateResponse) { + BasicHttpResponse response; + response.set_code(HTTP_OK); + response.set_content("Sample content - Hello world!"); + response.set_content_type("text/plain"); + response.AddCustomHeader("Simple-Header", "Simple value."); + + std::string kExpectedResponseString = + "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Length: 29\r\n" + "Content-Type: text/plain\r\n" + "Simple-Header: Simple value.\r\n\r\n" + "Sample content - Hello world!"; + + EXPECT_EQ(kExpectedResponseString, response.ToResponseString()); +} + +} // namespace test_server +} // namespace net diff --git a/chromium/net/test/net_test_suite.cc b/chromium/net/test/net_test_suite.cc new file mode 100644 index 00000000000..175cec29754 --- /dev/null +++ b/chromium/net/test/net_test_suite.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/net_test_suite.h" + +#include "base/message_loop/message_loop.h" +#include "net/base/network_change_notifier.h" +#include "net/http/http_stream_factory.h" +#include "net/spdy/spdy_session.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(USE_NSS) || defined(OS_IOS) +#include "net/ocsp/nss_ocsp.h" +#endif + +class StaticReset : public ::testing::EmptyTestEventListener { + virtual void OnTestStart(const ::testing::TestInfo& test_info) OVERRIDE { + net::HttpStreamFactory::ResetStaticSettingsToInit(); + } +}; + +NetTestSuite::NetTestSuite(int argc, char** argv) + : TestSuite(argc, argv) { +} + +NetTestSuite::NetTestSuite(int argc, char** argv, + bool create_at_exit_manager) + : TestSuite(argc, argv, create_at_exit_manager) { +} + +NetTestSuite::~NetTestSuite() {} + +void NetTestSuite::Initialize() { + TestSuite::Initialize(); + ::testing::UnitTest::GetInstance()->listeners().Append(new StaticReset()); + InitializeTestThread(); +} + +void NetTestSuite::Shutdown() { +#if defined(USE_NSS) || defined(OS_IOS) + net::ShutdownNSSHttpIO(); +#endif + + // We want to destroy this here before the TestSuite continues to tear down + // the environment. + message_loop_.reset(); + + TestSuite::Shutdown(); +} + +void NetTestSuite::InitializeTestThread() { + network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock()); + + InitializeTestThreadNoNetworkChangeNotifier(); +} + +void NetTestSuite::InitializeTestThreadNoNetworkChangeNotifier() { + host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL); + scoped_host_resolver_proc_.Init(host_resolver_proc_.get()); + // In case any attempts are made to resolve host names, force them all to + // be mapped to localhost. This prevents DNS queries from being sent in + // the process of running these unit tests. + host_resolver_proc_->AddRule("*", "127.0.0.1"); + + message_loop_.reset(new base::MessageLoopForIO()); +} diff --git a/chromium/net/test/net_test_suite.h b/chromium/net/test/net_test_suite.h new file mode 100644 index 00000000000..c8479d724ed --- /dev/null +++ b/chromium/net/test/net_test_suite.h @@ -0,0 +1,55 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_NET_TEST_SUITE_H_ +#define NET_TEST_NET_TEST_SUITE_H_ + +#include "base/memory/ref_counted.h" +#include "base/test/test_suite.h" +#include "build/build_config.h" +#include "net/dns/mock_host_resolver.h" + +namespace base { +class MessageLoop; +} + +namespace net { +class NetworkChangeNotifier; +} + +class NetTestSuite : public base::TestSuite { + public: + NetTestSuite(int argc, char** argv); + virtual ~NetTestSuite(); + + virtual void Initialize() OVERRIDE; + + virtual void Shutdown() OVERRIDE; + + protected: + // This constructor is only accessible to specialized net test + // implementations which need to control the creation of an AtExitManager + // instance for the duration of the test. + NetTestSuite(int argc, char** argv, bool create_at_exit_manager); + + // Called from within Initialize(), but separate so that derived classes + // can initialize the NetTestSuite instance only and not + // TestSuite::Initialize(). TestSuite::Initialize() performs some global + // initialization that can only be done once. + void InitializeTestThread(); + + // Same as above, except it does not create a mock + // NetworkChangeNotifier. Use this if your test needs to create and + // manage its own mock NetworkChangeNotifier, or if your test uses + // the production NetworkChangeNotifier. + void InitializeTestThreadNoNetworkChangeNotifier(); + + private: + scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; + scoped_ptr<base::MessageLoop> message_loop_; + scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_; + net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_; +}; + +#endif // NET_TEST_NET_TEST_SUITE_H_ diff --git a/chromium/net/test/openssl_helper.cc b/chromium/net/test/openssl_helper.cc new file mode 100644 index 00000000000..25989cb6016 --- /dev/null +++ b/chromium/net/test/openssl_helper.cc @@ -0,0 +1,264 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This helper binary is only used for testing Chrome's SSL stack. + +#include <sys/types.h> +#include <sys/socket.h> + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +static const char kDefaultPEMFile[] = "net/data/ssl/certificates/ok_cert.pem"; + +// Server Name Indication callback from OpenSSL +static int sni_cb(SSL *s, int *ad, void *arg) { + const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + if (servername && strcmp(servername, "test.example.com") == 0) + *reinterpret_cast<bool*>(arg) = true; + + return SSL_TLSEXT_ERR_OK; +} + +// Client certificate verification callback from OpenSSL +static int verify_cb(int preverify_ok, X509_STORE_CTX *ctx) { + return 1; +} + +// Next Protocol Negotiation callback from OpenSSL +static int next_proto_cb(SSL *ssl, const unsigned char **out, + unsigned int *outlen, void *arg) { + bool* npn_mispredict = reinterpret_cast<bool*>(arg); + static char kProtos[] = "\003foo\003bar"; + static char kProtos2[] = "\003baz\003boo"; + static unsigned count = 0; + + if (!*npn_mispredict || count == 0) { + *out = (const unsigned char*) kProtos; + *outlen = sizeof(kProtos) - 1; + } else { + *out = (const unsigned char*) kProtos2; + *outlen = sizeof(kProtos2) - 1; + } + count++; + return SSL_TLSEXT_ERR_OK; +} + +int +main(int argc, char **argv) { + SSL_library_init(); + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + bool sni = false, sni_good = false, snap_start = false; + bool snap_start_recovery = false, sslv3 = false, session_tickets = false; + bool fail_resume = false, client_cert = false, npn = false; + bool npn_mispredict = false; + + const char* key_file = kDefaultPEMFile; + const char* cert_file = kDefaultPEMFile; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "sni") == 0) { + // Require SNI + sni = true; + } else if (strcmp(argv[i], "snap-start") == 0) { + // Support Snap Start + snap_start = true; + } else if (strcmp(argv[i], "snap-start-recovery") == 0) { + // Support Snap Start, but always trigger a recovery + snap_start = true; + snap_start_recovery = true; + } else if (strcmp(argv[i], "sslv3") == 0) { + // Use SSLv3 + sslv3 = true; + } else if (strcmp(argv[i], "session-tickets") == 0) { + // Enable Session Tickets + session_tickets = true; + } else if (strcmp(argv[i], "fail-resume") == 0) { + // Always fail to resume sessions + fail_resume = true; + } else if (strcmp(argv[i], "client-cert") == 0) { + // Request a client certificate + client_cert = true; + } else if (strcmp(argv[i], "npn") == 0) { + // Advertise NPN + npn = true; + } else if (strcmp(argv[i], "npn-mispredict") == 0) { + // Advertise NPN + npn = true; + npn_mispredict = true; + } else if (strcmp(argv[i], "--key-file") == 0) { + // Use alternative key file + i++; + if (i == argc) { + fprintf(stderr, "Missing argument to --key-file\n"); + return 1; + } + key_file = argv[i]; + } else if (strcmp(argv[i], "--cert-file") == 0) { + // Use alternative certificate file + i++; + if (i == argc) { + fprintf(stderr, "Missing argument to --cert-file\n"); + return 1; + } + cert_file = argv[i]; + } else { + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + return 1; + } + } + + SSL_CTX* ctx; + + if (sslv3) { + ctx = SSL_CTX_new(SSLv3_server_method()); + } else { + ctx = SSL_CTX_new(TLSv1_server_method()); + } + + if (sni) { + SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); + SSL_CTX_set_tlsext_servername_arg(ctx, &sni_good); + } + + BIO* key = BIO_new(BIO_s_file()); + if (BIO_read_filename(key, key_file) <= 0) { + fprintf(stderr, "Failed to read %s\n", key_file); + return 1; + } + + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL); + if (!pkey) { + fprintf(stderr, "Failed to parse %s\n", key_file); + return 1; + } + BIO_free(key); + + + BIO* cert = BIO_new(BIO_s_file()); + if (BIO_read_filename(cert, cert_file) <= 0) { + fprintf(stderr, "Failed to read %s\n", cert_file); + return 1; + } + + X509 *pcert = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL); + if (!pcert) { + fprintf(stderr, "Failed to parse %s\n", cert_file); + return 1; + } + BIO_free(cert); + + if (SSL_CTX_use_certificate(ctx, pcert) <= 0) { + fprintf(stderr, "Failed to load %s\n", cert_file); + return 1; + } + + if (SSL_CTX_use_PrivateKey(ctx, pkey) <= 0) { + fprintf(stderr, "Failed to load %s\n", key_file); + return 1; + } + + if (!SSL_CTX_check_private_key(ctx)) { + fprintf(stderr, "Public and private keys don't match\n"); + return 1; + } + + if (client_cert) + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_cb); + + if (session_tickets) + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); + + if (snap_start) { + static const unsigned char orbit[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + SSL_CTX_set_snap_start_orbit(ctx, orbit); + } + + if (npn) + SSL_CTX_set_next_protos_advertised_cb(ctx, next_proto_cb, &npn_mispredict); + + unsigned connection_limit = 1; + if (snap_start || session_tickets) + connection_limit = 2; + if (npn_mispredict) + connection_limit = 3; + + for (unsigned connections = 0; connections < connection_limit; + connections++) { + const int fd = accept(3, NULL, NULL); + + SSL* server = SSL_new(ctx); + BIO* bio = BIO_new_socket(fd, 1 /* take ownership of fd */); + SSL_set_bio(server, bio, bio); + + if (fail_resume) { + SSL_set_session_id_context(server, (unsigned char*) &connections, + sizeof(connections)); + } + + int err; + for (;;) { + const int ret = SSL_accept(server); + if (ret == 1) + break; + + err = SSL_get_error(server, ret); + if (err == SSL_ERROR_WANT_READ) + continue; + if (err == SSL_ERROR_SERVER_RANDOM_VALIDATION_PENDING && snap_start) { + SSL_set_suggested_server_random_validity( + server, !snap_start_recovery); + continue; + } + ERR_print_errors_fp(stderr); + fprintf(stderr, "SSL_accept failed: %d\n", err); + return 1; + } + + if (sni && !sni_good) { + fprintf(stderr, "SNI failed\n"); + return 1; + } + + if (npn) { + const unsigned char *data, *expected_data; + unsigned len, expected_len; + SSL_get0_next_proto_negotiated(server, &data, &len); + if (!npn_mispredict || connections == 0) { + expected_data = (unsigned char*) "foo"; + expected_len = 3; + } else { + expected_data = (unsigned char*) "baz"; + expected_len = 3; + } + if (len != expected_len || memcmp(data, expected_data, len) != 0) { + fprintf(stderr, "Bad NPN: %d\n", len); + return 1; + } + } + + unsigned char buffer[6]; + + int ret = SSL_read(server, buffer, sizeof(buffer)); + if (ret == -1) { + err = SSL_get_error(server, ret); + ERR_print_errors_fp(stderr); + fprintf(stderr, "SSL_read failed: %d\n", err); + } + if (memcmp(buffer, "hello!", sizeof(buffer)) == 0) { + SSL_write(server, "goodbye!", 8); + } + + SSL_shutdown(server); + SSL_shutdown(server); + } + + SSL_CTX_free(ctx); + + return 0; +} diff --git a/chromium/net/test/python_utils.cc b/chromium/net/test/python_utils.cc new file mode 100644 index 00000000000..30c5f679a8a --- /dev/null +++ b/chromium/net/test/python_utils.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/python_utils.h" + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/environment.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" + +const char kPythonPathEnv[] = "PYTHONPATH"; + +void AppendToPythonPath(const base::FilePath& dir) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + std::string old_path; + std::string dir_path; +#if defined(OS_WIN) + dir_path = WideToUTF8(dir.value()); +#elif defined(OS_POSIX) + dir_path = dir.value(); +#endif + if (!env->GetVar(kPythonPathEnv, &old_path)) { + env->SetVar(kPythonPathEnv, dir_path.c_str()); + } else if (old_path.find(dir_path) == std::string::npos) { + std::string new_path(old_path); +#if defined(OS_WIN) + new_path.append(";"); +#elif defined(OS_POSIX) + new_path.append(":"); +#endif + new_path.append(dir_path.c_str()); + env->SetVar(kPythonPathEnv, new_path); + } +} + +namespace { + +// Search for |to_try|, rolling up the directory tree from +// |start_dir|. If found, return true and put the path to |to_try| in +// |out_dir|. If not, return false and leave |out_dir| untouched. +bool TryRelativeToDir(const base::FilePath& start_dir, + const base::FilePath& to_try, + base::FilePath* out_dir) { + base::FilePath dir(start_dir); + while (!base::DirectoryExists(dir.Append(to_try))) { + base::FilePath parent = dir.DirName(); + if (parent == dir) { + // We hit the root directory. + return false; + } + dir = parent; + } + *out_dir = dir; + return true; +} + +} // namespace + +bool GetPyProtoPath(base::FilePath* dir) { + // Locate the Python code generated by the protocol buffers compiler. + base::FilePath generated_code_dir; + if (!PathService::Get(base::DIR_EXE, &generated_code_dir)) { + LOG(ERROR) << "Can't find " << generated_code_dir.value(); + return false; + } + + const base::FilePath kPyProto(FILE_PATH_LITERAL("pyproto")); + +#if defined(OS_MACOSX) || defined(OS_CHROMEOS) + base::FilePath source_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_dir)) { + LOG(ERROR) << "Can't find " << source_dir.value(); + return false; + } + // On Mac, and possibly Chrome OS, DIR_EXE might be pointing deep + // into the Release/ (or Debug/) directory and we can't depend on + // how far down it goes. So we walk upwards from DIR_EXE until we + // find a likely looking spot. + if (!TryRelativeToDir(generated_code_dir, kPyProto, dir)) { + LOG(WARNING) << "Can't find " << kPyProto.value() + << " next to " << generated_code_dir.value(); + // On Chrome OS, we may have installed the test binaries and support tools + // in a wholly separate location, relative to DIR_SOURCE_ROOT. We'll want + // to do a similar investigation from that point as well. + generated_code_dir = source_dir + .Append(FILE_PATH_LITERAL("out")) + .Append(FILE_PATH_LITERAL("Release")); + if (!TryRelativeToDir(generated_code_dir, kPyProto, dir)) { + LOG(WARNING) << "Can't find " << kPyProto.value() + << " next to " << generated_code_dir.value(); + return false; + } + } + generated_code_dir = *dir; +#endif + *dir = generated_code_dir.Append(kPyProto); + VLOG(2) << "Found " << kPyProto.value() << " in " << dir->value(); + return true; +} + +bool GetPythonCommand(CommandLine* python_cmd) { + DCHECK(python_cmd); + base::FilePath dir; +#if defined(OS_WIN) + if (!PathService::Get(base::DIR_SOURCE_ROOT, &dir)) + return false; + dir = dir.Append(FILE_PATH_LITERAL("third_party")) + .Append(FILE_PATH_LITERAL("python_26")) + .Append(FILE_PATH_LITERAL("python.exe")); +#elif defined(OS_POSIX) + dir = base::FilePath("python"); +#endif + + python_cmd->SetProgram(dir); + + // Launch python in unbuffered mode, so that python output doesn't mix with + // gtest output in buildbot log files. See http://crbug.com/147368. + python_cmd->AppendArg("-u"); + + return true; +} diff --git a/chromium/net/test/python_utils.h b/chromium/net/test/python_utils.h new file mode 100644 index 00000000000..30776456503 --- /dev/null +++ b/chromium/net/test/python_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_PYTHON_UTILS_H_ +#define NET_TEST_PYTHON_UTILS_H_ + +#include "base/compiler_specific.h" + +class CommandLine; + +namespace base { +class FilePath; +} + +// This is the python path variable name. +extern const char kPythonPathEnv[]; + +// Appends the dir to python path environment variable. +void AppendToPythonPath(const base::FilePath& dir); + +// Return the location of the compiler-generated python protobuf. +bool GetPyProtoPath(base::FilePath* dir); + +// Returns the command that should be used to launch Python. +bool GetPythonCommand(CommandLine* python_cmd) WARN_UNUSED_RESULT; + +#endif // NET_TEST_PYTHON_UTILS_H_ diff --git a/chromium/net/test/python_utils_unittest.cc b/chromium/net/test/python_utils_unittest.cc new file mode 100644 index 00000000000..04f11ec266d --- /dev/null +++ b/chromium/net/test/python_utils_unittest.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/command_line.h" +#include "base/environment.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/process/launch.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "net/test/python_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PythonUtils, Append) { + const base::FilePath::CharType kAppendDir1[] = + FILE_PATH_LITERAL("test/path_append1"); + const base::FilePath::CharType kAppendDir2[] = + FILE_PATH_LITERAL("test/path_append2"); + + scoped_ptr<base::Environment> env(base::Environment::Create()); + + std::string python_path; + base::FilePath append_path1(kAppendDir1); + base::FilePath append_path2(kAppendDir2); + + // Get a clean start + env->UnSetVar(kPythonPathEnv); + + // Append the path + AppendToPythonPath(append_path1); + env->GetVar(kPythonPathEnv, &python_path); + ASSERT_EQ(python_path, "test/path_append1"); + + // Append the safe path again, nothing changes + AppendToPythonPath(append_path2); + env->GetVar(kPythonPathEnv, &python_path); +#if defined(OS_WIN) + ASSERT_EQ(std::string("test/path_append1;test/path_append2"), python_path); +#elif defined(OS_POSIX) + ASSERT_EQ(std::string("test/path_append1:test/path_append2"), python_path); +#endif +} + +TEST(PythonUtils, PythonRunTime) { + CommandLine cmd_line(CommandLine::NO_PROGRAM); + EXPECT_TRUE(GetPythonCommand(&cmd_line)); + + // Run a python command to print a string and make sure the output is what + // we want. + cmd_line.AppendArg("-c"); + std::string input("PythonUtilsTest"); + std::string python_cmd = base::StringPrintf("print '%s';", input.c_str()); + cmd_line.AppendArg(python_cmd); + std::string output; + EXPECT_TRUE(base::GetAppOutput(cmd_line, &output)); + TrimWhitespace(output, TRIM_TRAILING, &output); + EXPECT_EQ(input, output); +} diff --git a/chromium/net/test/run_all_unittests.cc b/chromium/net/test/run_all_unittests.cc new file mode 100644 index 00000000000..d8392ff0e2a --- /dev/null +++ b/chromium/net/test/run_all_unittests.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/metrics/statistics_recorder.h" +#include "build/build_config.h" +#include "crypto/nss_util.h" +#include "net/socket/client_socket_pool_base.h" +#include "net/socket/ssl_server_socket.h" +#include "net/spdy/spdy_session.h" +#include "net/test/net_test_suite.h" + +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#include "net/android/net_jni_registrar.h" +#endif + +#if !defined(OS_IOS) +#include "net/proxy/proxy_resolver_v8.h" +#endif + +using net::internal::ClientSocketPoolBaseHelper; +using net::SpdySession; + +int main(int argc, char** argv) { + // Record histograms, so we can get histograms data in tests. + base::StatisticsRecorder::Initialize(); + +#if defined(OS_ANDROID) + // Register JNI bindings for android. Doing it early as the test suite setup + // may initiate a call to Java. + net::android::RegisterJni(base::android::AttachCurrentThread()); +#endif + + NetTestSuite test_suite(argc, argv); + ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false); + +#if defined(OS_WIN) + // We want to be sure to init NSPR on the main thread. + crypto::EnsureNSPRInit(); +#endif + + // Enable support for SSL server sockets, which must be done while + // single-threaded. + net::EnableSSLServerSockets(); + +#if !defined(OS_IOS) + // This has to be done on the main thread. + net::ProxyResolverV8::RememberDefaultIsolate(); +#endif + + return test_suite.Run(); +} diff --git a/chromium/net/test/spawned_test_server/base_test_server.cc b/chromium/net/test/spawned_test_server/base_test_server.cc new file mode 100644 index 00000000000..c13745bf51e --- /dev/null +++ b/chromium/net/test/spawned_test_server/base_test_server.cc @@ -0,0 +1,407 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/base_test_server.h" + +#include <string> +#include <vector> + +#include "base/base64.h" +#include "base/file_util.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/values.h" +#include "net/base/address_list.h" +#include "net/base/host_port_pair.h" +#include "net/base/net_errors.h" +#include "net/base/net_log.h" +#include "net/base/net_util.h" +#include "net/base/test_completion_callback.h" +#include "net/cert/test_root_certs.h" +#include "net/dns/host_resolver.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +std::string GetHostname(BaseTestServer::Type type, + const BaseTestServer::SSLOptions& options) { + if (BaseTestServer::UsingSSL(type) && + options.server_certificate == + BaseTestServer::SSLOptions::CERT_MISMATCHED_NAME) { + // Return a different hostname string that resolves to the same hostname. + return "localhost"; + } + + // Use the 127.0.0.1 as default. + return BaseTestServer::kLocalhost; +} + +void GetCiphersList(int cipher, base::ListValue* values) { + if (cipher & BaseTestServer::SSLOptions::BULK_CIPHER_RC4) + values->Append(new base::StringValue("rc4")); + if (cipher & BaseTestServer::SSLOptions::BULK_CIPHER_AES128) + values->Append(new base::StringValue("aes128")); + if (cipher & BaseTestServer::SSLOptions::BULK_CIPHER_AES256) + values->Append(new base::StringValue("aes256")); + if (cipher & BaseTestServer::SSLOptions::BULK_CIPHER_3DES) + values->Append(new base::StringValue("3des")); +} + +} // namespace + +BaseTestServer::SSLOptions::SSLOptions() + : server_certificate(CERT_OK), + ocsp_status(OCSP_OK), + cert_serial(0), + request_client_certificate(false), + bulk_ciphers(SSLOptions::BULK_CIPHER_ANY), + record_resume(false), + tls_intolerant(TLS_INTOLERANT_NONE) {} + +BaseTestServer::SSLOptions::SSLOptions( + BaseTestServer::SSLOptions::ServerCertificate cert) + : server_certificate(cert), + ocsp_status(OCSP_OK), + cert_serial(0), + request_client_certificate(false), + bulk_ciphers(SSLOptions::BULK_CIPHER_ANY), + record_resume(false), + tls_intolerant(TLS_INTOLERANT_NONE) {} + +BaseTestServer::SSLOptions::~SSLOptions() {} + +base::FilePath BaseTestServer::SSLOptions::GetCertificateFile() const { + switch (server_certificate) { + case CERT_OK: + case CERT_MISMATCHED_NAME: + return base::FilePath(FILE_PATH_LITERAL("ok_cert.pem")); + case CERT_EXPIRED: + return base::FilePath(FILE_PATH_LITERAL("expired_cert.pem")); + case CERT_CHAIN_WRONG_ROOT: + // This chain uses its own dedicated test root certificate to avoid + // side-effects that may affect testing. + return base::FilePath(FILE_PATH_LITERAL("redundant-server-chain.pem")); + case CERT_AUTO: + return base::FilePath(); + default: + NOTREACHED(); + } + return base::FilePath(); +} + +std::string BaseTestServer::SSLOptions::GetOCSPArgument() const { + if (server_certificate != CERT_AUTO) + return std::string(); + + switch (ocsp_status) { + case OCSP_OK: + return "ok"; + case OCSP_REVOKED: + return "revoked"; + case OCSP_INVALID: + return "invalid"; + case OCSP_UNAUTHORIZED: + return "unauthorized"; + case OCSP_UNKNOWN: + return "unknown"; + default: + NOTREACHED(); + return std::string(); + } +} + +const char BaseTestServer::kLocalhost[] = "127.0.0.1"; + +BaseTestServer::BaseTestServer(Type type, const std::string& host) + : type_(type), + started_(false), + log_to_console_(false) { + Init(host); +} + +BaseTestServer::BaseTestServer(Type type, const SSLOptions& ssl_options) + : ssl_options_(ssl_options), + type_(type), + started_(false), + log_to_console_(false) { + DCHECK(UsingSSL(type)); + Init(GetHostname(type, ssl_options)); +} + +BaseTestServer::~BaseTestServer() {} + +const HostPortPair& BaseTestServer::host_port_pair() const { + DCHECK(started_); + return host_port_pair_; +} + +const base::DictionaryValue& BaseTestServer::server_data() const { + DCHECK(started_); + DCHECK(server_data_.get()); + return *server_data_; +} + +std::string BaseTestServer::GetScheme() const { + switch (type_) { + case TYPE_FTP: + return "ftp"; + case TYPE_HTTP: + return "http"; + case TYPE_HTTPS: + return "https"; + case TYPE_WS: + return "ws"; + case TYPE_WSS: + return "wss"; + case TYPE_TCP_ECHO: + case TYPE_UDP_ECHO: + default: + NOTREACHED(); + } + return std::string(); +} + +bool BaseTestServer::GetAddressList(AddressList* address_list) const { + DCHECK(address_list); + + scoped_ptr<HostResolver> resolver(HostResolver::CreateDefaultResolver(NULL)); + HostResolver::RequestInfo info(host_port_pair_); + TestCompletionCallback callback; + int rv = resolver->Resolve(info, address_list, callback.callback(), NULL, + BoundNetLog()); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + if (rv != net::OK) { + LOG(ERROR) << "Failed to resolve hostname: " << host_port_pair_.host(); + return false; + } + return true; +} + +uint16 BaseTestServer::GetPort() { + return host_port_pair_.port(); +} + +void BaseTestServer::SetPort(uint16 port) { + host_port_pair_.set_port(port); +} + +GURL BaseTestServer::GetURL(const std::string& path) const { + return GURL(GetScheme() + "://" + host_port_pair_.ToString() + "/" + path); +} + +GURL BaseTestServer::GetURLWithUser(const std::string& path, + const std::string& user) const { + return GURL(GetScheme() + "://" + user + "@" + host_port_pair_.ToString() + + "/" + path); +} + +GURL BaseTestServer::GetURLWithUserAndPassword(const std::string& path, + const std::string& user, + const std::string& password) const { + return GURL(GetScheme() + "://" + user + ":" + password + "@" + + host_port_pair_.ToString() + "/" + path); +} + +// static +bool BaseTestServer::GetFilePathWithReplacements( + const std::string& original_file_path, + const std::vector<StringPair>& text_to_replace, + std::string* replacement_path) { + std::string new_file_path = original_file_path; + bool first_query_parameter = true; + const std::vector<StringPair>::const_iterator end = text_to_replace.end(); + for (std::vector<StringPair>::const_iterator it = text_to_replace.begin(); + it != end; + ++it) { + const std::string& old_text = it->first; + const std::string& new_text = it->second; + std::string base64_old; + std::string base64_new; + if (!base::Base64Encode(old_text, &base64_old)) + return false; + if (!base::Base64Encode(new_text, &base64_new)) + return false; + if (first_query_parameter) { + new_file_path += "?"; + first_query_parameter = false; + } else { + new_file_path += "&"; + } + new_file_path += "replace_text="; + new_file_path += base64_old; + new_file_path += ":"; + new_file_path += base64_new; + } + + *replacement_path = new_file_path; + return true; +} + +void BaseTestServer::Init(const std::string& host) { + host_port_pair_ = HostPortPair(host, 0); + + // TODO(battre) Remove this after figuring out why the TestServer is flaky. + // http://crbug.com/96594 + log_to_console_ = true; +} + +void BaseTestServer::SetResourcePath(const base::FilePath& document_root, + const base::FilePath& certificates_dir) { + // This method shouldn't get called twice. + DCHECK(certificates_dir_.empty()); + document_root_ = document_root; + certificates_dir_ = certificates_dir; + DCHECK(!certificates_dir_.empty()); +} + +bool BaseTestServer::ParseServerData(const std::string& server_data) { + VLOG(1) << "Server data: " << server_data; + base::JSONReader json_reader; + scoped_ptr<base::Value> value(json_reader.ReadToValue(server_data)); + if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) { + LOG(ERROR) << "Could not parse server data: " + << json_reader.GetErrorMessage(); + return false; + } + + server_data_.reset(static_cast<base::DictionaryValue*>(value.release())); + int port = 0; + if (!server_data_->GetInteger("port", &port)) { + LOG(ERROR) << "Could not find port value"; + return false; + } + if ((port <= 0) || (port > kuint16max)) { + LOG(ERROR) << "Invalid port value: " << port; + return false; + } + host_port_pair_.set_port(port); + + return true; +} + +bool BaseTestServer::LoadTestRootCert() const { + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + if (!root_certs) + return false; + + // Should always use absolute path to load the root certificate. + base::FilePath root_certificate_path = certificates_dir_; + if (!certificates_dir_.IsAbsolute()) { + base::FilePath src_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) + return false; + root_certificate_path = src_dir.Append(certificates_dir_); + } + + return root_certs->AddFromFile( + root_certificate_path.AppendASCII("root_ca_cert.pem")); +} + +bool BaseTestServer::SetupWhenServerStarted() { + DCHECK(host_port_pair_.port()); + + if (UsingSSL(type_) && !LoadTestRootCert()) + return false; + + started_ = true; + allowed_port_.reset(new ScopedPortException(host_port_pair_.port())); + return true; +} + +void BaseTestServer::CleanUpWhenStoppingServer() { + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + root_certs->Clear(); + + host_port_pair_.set_port(0); + allowed_port_.reset(); + started_ = false; +} + +// Generates a dictionary of arguments to pass to the Python test server via +// the test server spawner, in the form of +// { argument-name: argument-value, ... } +// Returns false if an invalid configuration is specified. +bool BaseTestServer::GenerateArguments(base::DictionaryValue* arguments) const { + DCHECK(arguments); + + arguments->SetString("host", host_port_pair_.host()); + arguments->SetInteger("port", host_port_pair_.port()); + arguments->SetString("data-dir", document_root_.value()); + + if (VLOG_IS_ON(1) || log_to_console_) + arguments->Set("log-to-console", base::Value::CreateNullValue()); + + if (UsingSSL(type_)) { + // Check the certificate arguments of the HTTPS server. + base::FilePath certificate_path(certificates_dir_); + base::FilePath certificate_file(ssl_options_.GetCertificateFile()); + if (!certificate_file.value().empty()) { + certificate_path = certificate_path.Append(certificate_file); + if (certificate_path.IsAbsolute() && + !base::PathExists(certificate_path)) { + LOG(ERROR) << "Certificate path " << certificate_path.value() + << " doesn't exist. Can't launch https server."; + return false; + } + arguments->SetString("cert-and-key-file", certificate_path.value()); + } + + // Check the client certificate related arguments. + if (ssl_options_.request_client_certificate) + arguments->Set("ssl-client-auth", base::Value::CreateNullValue()); + scoped_ptr<base::ListValue> ssl_client_certs(new base::ListValue()); + + std::vector<base::FilePath>::const_iterator it; + for (it = ssl_options_.client_authorities.begin(); + it != ssl_options_.client_authorities.end(); ++it) { + if (it->IsAbsolute() && !base::PathExists(*it)) { + LOG(ERROR) << "Client authority path " << it->value() + << " doesn't exist. Can't launch https server."; + return false; + } + ssl_client_certs->Append(new base::StringValue(it->value())); + } + + if (ssl_client_certs->GetSize()) + arguments->Set("ssl-client-ca", ssl_client_certs.release()); + } + + if (type_ == TYPE_HTTPS) { + arguments->Set("https", base::Value::CreateNullValue()); + + std::string ocsp_arg = ssl_options_.GetOCSPArgument(); + if (!ocsp_arg.empty()) + arguments->SetString("ocsp", ocsp_arg); + + if (ssl_options_.cert_serial != 0) { + arguments->Set("cert-serial", + base::Value::CreateIntegerValue(ssl_options_.cert_serial)); + } + + // Check bulk cipher argument. + scoped_ptr<base::ListValue> bulk_cipher_values(new base::ListValue()); + GetCiphersList(ssl_options_.bulk_ciphers, bulk_cipher_values.get()); + if (bulk_cipher_values->GetSize()) + arguments->Set("ssl-bulk-cipher", bulk_cipher_values.release()); + if (ssl_options_.record_resume) + arguments->Set("https-record-resume", base::Value::CreateNullValue()); + if (ssl_options_.tls_intolerant != SSLOptions::TLS_INTOLERANT_NONE) { + arguments->Set("tls-intolerant", + new base::FundamentalValue(ssl_options_.tls_intolerant)); + } + } + + return GenerateAdditionalArguments(arguments); +} + +bool BaseTestServer::GenerateAdditionalArguments( + base::DictionaryValue* arguments) const { + return true; +} + +} // namespace net diff --git a/chromium/net/test/spawned_test_server/base_test_server.h b/chromium/net/test/spawned_test_server/base_test_server.h new file mode 100644 index 00000000000..ff395c56f8b --- /dev/null +++ b/chromium/net/test/spawned_test_server/base_test_server.h @@ -0,0 +1,263 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_SPAWNED_TEST_SERVER_BASE_TEST_SERVER_H_ +#define NET_TEST_SPAWNED_TEST_SERVER_BASE_TEST_SERVER_H_ + +#include <string> +#include <utility> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/host_port_pair.h" + +class GURL; + +namespace base { +class DictionaryValue; +} + +namespace net { + +class AddressList; +class ScopedPortException; + +// The base class of Test server implementation. +class BaseTestServer { + public: + typedef std::pair<std::string, std::string> StringPair; + + // Following types represent protocol schemes. See also + // http://www.iana.org/assignments/uri-schemes.html + enum Type { + TYPE_BASIC_AUTH_PROXY, + TYPE_FTP, + TYPE_HTTP, + TYPE_HTTPS, + TYPE_WS, + TYPE_WSS, + TYPE_TCP_ECHO, + TYPE_UDP_ECHO, + }; + + // Container for various options to control how the HTTPS or WSS server is + // initialized. + struct SSLOptions { + enum ServerCertificate { + CERT_OK, + + // CERT_AUTO causes the testserver to generate a test certificate issued + // by "Testing CA" (see net/data/ssl/certificates/ocsp-test-root.pem). + CERT_AUTO, + + CERT_MISMATCHED_NAME, + CERT_EXPIRED, + // Cross-signed certificate to test PKIX path building. Contains an + // intermediate cross-signed by an unknown root, while the client (via + // TestRootStore) is expected to have a self-signed version of the + // intermediate. + CERT_CHAIN_WRONG_ROOT, + }; + + // OCSPStatus enumerates the types of OCSP response that the testserver + // can produce. + enum OCSPStatus { + OCSP_OK, + OCSP_REVOKED, + OCSP_INVALID, + OCSP_UNAUTHORIZED, + OCSP_UNKNOWN, + }; + + // Bitmask of bulk encryption algorithms that the test server supports + // and that can be selectively enabled or disabled. + enum BulkCipher { + // Special value used to indicate that any algorithm the server supports + // is acceptable. Preferred over explicitly OR-ing all ciphers. + BULK_CIPHER_ANY = 0, + + BULK_CIPHER_RC4 = (1 << 0), + BULK_CIPHER_AES128 = (1 << 1), + BULK_CIPHER_AES256 = (1 << 2), + + // NOTE: 3DES support in the Python test server has external + // dependencies and not be available on all machines. Clients may not + // be able to connect if only 3DES is specified. + BULK_CIPHER_3DES = (1 << 3), + }; + + // NOTE: the values of these enumerators are passed to the the Python test + // server. Do not change them. + enum TLSIntolerantLevel { + TLS_INTOLERANT_NONE = 0, + TLS_INTOLERANT_ALL = 1, // Intolerant of all TLS versions. + TLS_INTOLERANT_TLS1_1 = 2, // Intolerant of TLS 1.1 or higher. + TLS_INTOLERANT_TLS1_2 = 3, // Intolerant of TLS 1.2 or higher. + }; + + // Initialize a new SSLOptions using CERT_OK as the certificate. + SSLOptions(); + + // Initialize a new SSLOptions that will use the specified certificate. + explicit SSLOptions(ServerCertificate cert); + ~SSLOptions(); + + // Returns the relative filename of the file that contains the + // |server_certificate|. + base::FilePath GetCertificateFile() const; + + // GetOCSPArgument returns the value of any OCSP argument to testserver or + // the empty string if there is none. + std::string GetOCSPArgument() const; + + // The certificate to use when serving requests. + ServerCertificate server_certificate; + + // If |server_certificate==CERT_AUTO| then this determines the type of OCSP + // response returned. + OCSPStatus ocsp_status; + + // If not zero, |cert_serial| will be the serial number of the + // auto-generated leaf certificate when |server_certificate==CERT_AUTO|. + uint64 cert_serial; + + // True if a CertificateRequest should be sent to the client during + // handshaking. + bool request_client_certificate; + + // If |request_client_certificate| is true, an optional list of files, + // each containing a single, PEM-encoded X.509 certificates. The subject + // from each certificate will be added to the certificate_authorities + // field of the CertificateRequest. + std::vector<base::FilePath> client_authorities; + + // A bitwise-OR of BulkCipher that should be used by the + // HTTPS server, or BULK_CIPHER_ANY to indicate that all implemented + // ciphers are acceptable. + int bulk_ciphers; + + // If true, pass the --https-record-resume argument to testserver.py which + // causes it to log session cache actions and echo the log on + // /ssl-session-cache. + bool record_resume; + + // If not TLS_INTOLERANT_NONE, the server will abort any handshake that + // negotiates an intolerant TLS version in order to test version fallback. + TLSIntolerantLevel tls_intolerant; + }; + + // Pass as the 'host' parameter during construction to server on 127.0.0.1 + static const char kLocalhost[]; + + // Initialize a TestServer listening on a specific host (IP or hostname). + BaseTestServer(Type type, const std::string& host); + + // Initialize a TestServer with a specific set of SSLOptions for HTTPS or WSS. + explicit BaseTestServer(Type type, const SSLOptions& ssl_options); + + // Returns the host port pair used by current Python based test server only + // if the server is started. + const HostPortPair& host_port_pair() const; + + const base::FilePath& document_root() const { return document_root_; } + const base::DictionaryValue& server_data() const; + std::string GetScheme() const; + bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT; + + GURL GetURL(const std::string& path) const; + + GURL GetURLWithUser(const std::string& path, + const std::string& user) const; + + GURL GetURLWithUserAndPassword(const std::string& path, + const std::string& user, + const std::string& password) const; + + static bool GetFilePathWithReplacements( + const std::string& original_path, + const std::vector<StringPair>& text_to_replace, + std::string* replacement_path); + + static bool UsingSSL(Type type) { + return type == BaseTestServer::TYPE_HTTPS || + type == BaseTestServer::TYPE_WSS; + } + + protected: + virtual ~BaseTestServer(); + Type type() const { return type_; } + + // Gets port currently assigned to host_port_pair_ without checking + // whether it's available (server started) or not. + uint16 GetPort(); + + // Sets |port| as the actual port used by Python based test server. + void SetPort(uint16 port); + + // Set up internal status when the server is started. + bool SetupWhenServerStarted() WARN_UNUSED_RESULT; + + // Clean up internal status when starting to stop server. + void CleanUpWhenStoppingServer(); + + // Set path of test resources. + void SetResourcePath(const base::FilePath& document_root, + const base::FilePath& certificates_dir); + + // Parses the server data read from the test server. Returns true + // on success. + bool ParseServerData(const std::string& server_data) WARN_UNUSED_RESULT; + + // Generates a DictionaryValue with the arguments for launching the external + // Python test server. + bool GenerateArguments(base::DictionaryValue* arguments) const + WARN_UNUSED_RESULT; + + // Subclasses can override this to add arguments that are specific to their + // own test servers. + virtual bool GenerateAdditionalArguments( + base::DictionaryValue* arguments) const WARN_UNUSED_RESULT; + + private: + void Init(const std::string& host); + + // Marks the root certificate of an HTTPS test server as trusted for + // the duration of tests. + bool LoadTestRootCert() const WARN_UNUSED_RESULT; + + // Document root of the test server. + base::FilePath document_root_; + + // Directory that contains the SSL certificates. + base::FilePath certificates_dir_; + + // Address the test server listens on. + HostPortPair host_port_pair_; + + // Holds the data sent from the server (e.g., port number). + scoped_ptr<base::DictionaryValue> server_data_; + + // If |type_| is TYPE_HTTPS or TYPE_WSS, the TLS settings to use for the test + // server. + SSLOptions ssl_options_; + + Type type_; + + // Has the server been started? + bool started_; + + // Enables logging of the server to the console. + bool log_to_console_; + + scoped_ptr<ScopedPortException> allowed_port_; + + DISALLOW_COPY_AND_ASSIGN(BaseTestServer); +}; + +} // namespace net + +#endif // NET_TEST_SPAWNED_TEST_SERVER_BASE_TEST_SERVER_H_ + diff --git a/chromium/net/test/spawned_test_server/local_test_server.cc b/chromium/net/test/spawned_test_server/local_test_server.cc new file mode 100644 index 00000000000..bf1b0590786 --- /dev/null +++ b/chromium/net/test/spawned_test_server/local_test_server.cc @@ -0,0 +1,252 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/local_test_server.h" + +#include "base/command_line.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/process/kill.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "net/base/host_port_pair.h" +#include "net/base/net_errors.h" +#include "net/test/python_utils.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +bool AppendArgumentFromJSONValue(const std::string& key, + const base::Value& value_node, + CommandLine* command_line) { + std::string argument_name = "--" + key; + switch (value_node.GetType()) { + case base::Value::TYPE_NULL: + command_line->AppendArg(argument_name); + break; + case base::Value::TYPE_INTEGER: { + int value; + bool result = value_node.GetAsInteger(&value); + DCHECK(result); + command_line->AppendArg(argument_name + "=" + base::IntToString(value)); + break; + } + case Value::TYPE_STRING: { + std::string value; + bool result = value_node.GetAsString(&value); + if (!result || value.empty()) + return false; + command_line->AppendArg(argument_name + "=" + value); + break; + } + case base::Value::TYPE_BOOLEAN: + case base::Value::TYPE_DOUBLE: + case base::Value::TYPE_LIST: + case base::Value::TYPE_DICTIONARY: + case base::Value::TYPE_BINARY: + default: + NOTREACHED() << "improper json type"; + return false; + } + return true; +} + +} // namespace + +LocalTestServer::LocalTestServer(Type type, + const std::string& host, + const base::FilePath& document_root) + : BaseTestServer(type, host) { + if (!Init(document_root)) + NOTREACHED(); +} + +LocalTestServer::LocalTestServer(Type type, + const SSLOptions& ssl_options, + const base::FilePath& document_root) + : BaseTestServer(type, ssl_options) { + if (!Init(document_root)) + NOTREACHED(); +} + +LocalTestServer::~LocalTestServer() { + Stop(); +} + +bool LocalTestServer::GetTestServerPath(base::FilePath* testserver_path) const { + base::FilePath testserver_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_dir)) { + LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; + return false; + } + testserver_dir = testserver_dir.Append(FILE_PATH_LITERAL("net")) + .Append(FILE_PATH_LITERAL("tools")) + .Append(FILE_PATH_LITERAL("testserver")); + *testserver_path = testserver_dir.Append(FILE_PATH_LITERAL("testserver.py")); + return true; +} + +bool LocalTestServer::Start() { + return StartInBackground() && BlockUntilStarted(); +} + +bool LocalTestServer::StartInBackground() { + // Get path to Python server script. + base::FilePath testserver_path; + if (!GetTestServerPath(&testserver_path)) + return false; + + if (!SetPythonPath()) + return false; + + if (!LaunchPython(testserver_path)) + return false; + + return true; +} + +bool LocalTestServer::BlockUntilStarted() { + if (!WaitToStart()) { + Stop(); + return false; + } + + return SetupWhenServerStarted(); +} + +bool LocalTestServer::Stop() { + CleanUpWhenStoppingServer(); + + if (!process_handle_) + return true; + + // First check if the process has already terminated. + bool ret = base::WaitForSingleProcess(process_handle_, base::TimeDelta()); + if (!ret) + ret = base::KillProcess(process_handle_, 1, true); + + if (ret) { + base::CloseProcessHandle(process_handle_); + process_handle_ = base::kNullProcessHandle; + } else { + VLOG(1) << "Kill failed?"; + } + + return ret; +} + +bool LocalTestServer::Init(const base::FilePath& document_root) { + if (document_root.IsAbsolute()) + return false; + + // At this point, the port that the test server will listen on is unknown. + // The test server will listen on an ephemeral port, and write the port + // number out over a pipe that this TestServer object will read from. Once + // that is complete, the host port pair will contain the actual port. + DCHECK(!GetPort()); + process_handle_ = base::kNullProcessHandle; + + base::FilePath src_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &src_dir)) + return false; + SetResourcePath(src_dir.Append(document_root), + src_dir.AppendASCII("net") + .AppendASCII("data") + .AppendASCII("ssl") + .AppendASCII("certificates")); + return true; +} + +bool LocalTestServer::SetPythonPath() const { + base::FilePath third_party_dir; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) { + LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT"; + return false; + } + third_party_dir = third_party_dir.AppendASCII("third_party"); + + // For simplejson. (simplejson, unlike all the other Python modules + // we include, doesn't have an extra 'simplejson' directory, so we + // need to include its parent directory, i.e. third_party_dir). + AppendToPythonPath(third_party_dir); + + AppendToPythonPath(third_party_dir.AppendASCII("tlslite")); + AppendToPythonPath( + third_party_dir.AppendASCII("pyftpdlib").AppendASCII("src")); + AppendToPythonPath( + third_party_dir.AppendASCII("pywebsocket").AppendASCII("src")); + + // Locate the Python code generated by the protocol buffers compiler. + base::FilePath pyproto_dir; + if (!GetPyProtoPath(&pyproto_dir)) { + LOG(WARNING) << "Cannot find pyproto dir for generated code. " + << "Testserver features that rely on it will not work"; + return true; + } + AppendToPythonPath(pyproto_dir); + + return true; +} + +bool LocalTestServer::AddCommandLineArguments(CommandLine* command_line) const { + base::DictionaryValue arguments_dict; + if (!GenerateArguments(&arguments_dict)) + return false; + + // Serialize the argument dictionary into CommandLine. + for (base::DictionaryValue::Iterator it(arguments_dict); !it.IsAtEnd(); + it.Advance()) { + const base::Value& value = it.value(); + const std::string& key = it.key(); + + // Add arguments from a list. + if (value.IsType(Value::TYPE_LIST)) { + const base::ListValue* list = NULL; + if (!value.GetAsList(&list) || !list || list->empty()) + return false; + for (base::ListValue::const_iterator list_it = list->begin(); + list_it != list->end(); ++list_it) { + if (!AppendArgumentFromJSONValue(key, *(*list_it), command_line)) + return false; + } + } else if (!AppendArgumentFromJSONValue(key, value, command_line)) { + return false; + } + } + + // Append the appropriate server type argument. + switch (type()) { + case TYPE_HTTP: // The default type is HTTP, no argument required. + break; + case TYPE_HTTPS: + command_line->AppendArg("--https"); + break; + case TYPE_WS: + case TYPE_WSS: + command_line->AppendArg("--websocket"); + break; + case TYPE_FTP: + command_line->AppendArg("--ftp"); + break; + case TYPE_TCP_ECHO: + command_line->AppendArg("--tcp-echo"); + break; + case TYPE_UDP_ECHO: + command_line->AppendArg("--udp-echo"); + break; + case TYPE_BASIC_AUTH_PROXY: + command_line->AppendArg("--basic-auth-proxy"); + break; + default: + NOTREACHED(); + return false; + } + + return true; +} + +} // namespace net diff --git a/chromium/net/test/spawned_test_server/local_test_server.h b/chromium/net/test/spawned_test_server/local_test_server.h new file mode 100644 index 00000000000..cfd2eb3baee --- /dev/null +++ b/chromium/net/test/spawned_test_server/local_test_server.h @@ -0,0 +1,117 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_SPAWNED_TEST_SERVER_LOCAL_TEST_SERVER_H_ +#define NET_TEST_SPAWNED_TEST_SERVER_LOCAL_TEST_SERVER_H_ + +#include <string> + +#include "base/file_util.h" +#include "base/process/process_handle.h" +#include "net/test/spawned_test_server/base_test_server.h" + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#endif + +class CommandLine; + +namespace net { + +// The LocalTestServer runs an external Python-based test server in the +// same machine in which the LocalTestServer runs. +class LocalTestServer : public BaseTestServer { + public: + // Initialize a TestServer listening on a specific host (IP or hostname). + // |document_root| must be a relative path under the root tree. + LocalTestServer(Type type, + const std::string& host, + const base::FilePath& document_root); + + // Initialize a TestServer with a specific set of SSLOptions. + // |document_root| must be a relative path under the root tree. + LocalTestServer(Type type, + const SSLOptions& ssl_options, + const base::FilePath& document_root); + + virtual ~LocalTestServer(); + + // Start the test server and block until it's ready. Returns true on success. + bool Start() WARN_UNUSED_RESULT; + + // Start the test server without blocking. Use this if you need multiple test + // servers (such as WebSockets and HTTP, or HTTP and HTTPS). You must call + // BlockUntilStarted on all servers your test requires before executing the + // test. For example: + // + // // Start the servers in parallel. + // ASSERT_TRUE(http_server.StartInBackground()); + // ASSERT_TRUE(websocket_server.StartInBackground()); + // // Wait for both servers to be ready. + // ASSERT_TRUE(http_server.BlockUntilStarted()); + // ASSERT_TRUE(websocket_server.BlockUntilStarted()); + // RunMyTest(); + // + // Returns true on success. + bool StartInBackground() WARN_UNUSED_RESULT; + + // Block until ths test server is ready. Returns true on success. See + // StartInBackground() documentation for more information. + bool BlockUntilStarted() WARN_UNUSED_RESULT; + + // Stop the server started by Start(). + bool Stop(); + + // Modify PYTHONPATH to contain libraries we need. + virtual bool SetPythonPath() const WARN_UNUSED_RESULT; + + // Returns true if the base::FilePath for the testserver python script is + // successfully stored in |*testserver_path|. + virtual bool GetTestServerPath(base::FilePath* testserver_path) const + WARN_UNUSED_RESULT; + + // Adds the command line arguments for the Python test server to + // |command_line|. Returns true on success. + virtual bool AddCommandLineArguments(CommandLine* command_line) const + WARN_UNUSED_RESULT; + + // Returns the actual path of document root for test cases. This function + // should be called by test cases to retrieve the actual document root path. + base::FilePath GetDocumentRoot() const { return document_root(); }; + + private: + bool Init(const base::FilePath& document_root); + + // Launches the Python test server. Returns true on success. + bool LaunchPython(const base::FilePath& testserver_path) WARN_UNUSED_RESULT; + + // Waits for the server to start. Returns true on success. + bool WaitToStart() WARN_UNUSED_RESULT; + + // Handle of the Python process running the test server. + base::ProcessHandle process_handle_; + +#if defined(OS_WIN) + // JobObject used to clean up orphaned child processes. + base::win::ScopedHandle job_handle_; + + // The pipe file handle we read from. + base::win::ScopedHandle child_read_fd_; + + // The pipe file handle the child and we write to. + base::win::ScopedHandle child_write_fd_; +#endif + +#if defined(OS_POSIX) + // The file descriptor the child writes to when it starts. + int child_fd_; + file_util::ScopedFD child_fd_closer_; +#endif + + DISALLOW_COPY_AND_ASSIGN(LocalTestServer); +}; + +} // namespace net + +#endif // NET_TEST_SPAWNED_TEST_SERVER_LOCAL_TEST_SERVER_H_ diff --git a/chromium/net/test/spawned_test_server/local_test_server_posix.cc b/chromium/net/test/spawned_test_server/local_test_server_posix.cc new file mode 100644 index 00000000000..10c2d0f9932 --- /dev/null +++ b/chromium/net/test/spawned_test_server/local_test_server_posix.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/local_test_server.h" + +#include <poll.h> + +#include <vector> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/process/kill.h" +#include "base/process/launch.h" +#include "base/process/process_iterator.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/test/test_timeouts.h" +#include "net/test/python_utils.h" + +namespace { + +// Helper class used to detect and kill orphaned python test server processes. +// Checks if the command line of a process contains |path_string| (the path +// from which the test server was launched) and |port_string| (the port used by +// the test server), and if the parent pid of the process is 1 (indicating that +// it is an orphaned process). +class OrphanedTestServerFilter : public base::ProcessFilter { + public: + OrphanedTestServerFilter( + const std::string& path_string, const std::string& port_string) + : path_string_(path_string), + port_string_(port_string) {} + + virtual bool Includes(const base::ProcessEntry& entry) const OVERRIDE { + if (entry.parent_pid() != 1) + return false; + bool found_path_string = false; + bool found_port_string = false; + for (std::vector<std::string>::const_iterator it = + entry.cmd_line_args().begin(); + it != entry.cmd_line_args().end(); + ++it) { + if (it->find(path_string_) != std::string::npos) + found_path_string = true; + if (it->find(port_string_) != std::string::npos) + found_port_string = true; + } + return found_path_string && found_port_string; + } + + private: + std::string path_string_; + std::string port_string_; + DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter); +}; + +// Given a file descriptor, reads into |buffer| until |bytes_max| +// bytes has been read or an error has been encountered. Returns true +// if the read was successful. |remaining_time| is used as a timeout. +bool ReadData(int fd, ssize_t bytes_max, uint8* buffer, + base::TimeDelta* remaining_time) { + ssize_t bytes_read = 0; + base::TimeTicks previous_time = base::TimeTicks::Now(); + while (bytes_read < bytes_max) { + struct pollfd poll_fds[1]; + + poll_fds[0].fd = fd; + poll_fds[0].events = POLLIN | POLLPRI; + poll_fds[0].revents = 0; + + int rv = HANDLE_EINTR(poll(poll_fds, 1, + remaining_time->InMilliseconds())); + if (rv == 0) { + LOG(ERROR) << "poll() timed out; bytes_read=" << bytes_read; + return false; + } else if (rv < 0) { + PLOG(ERROR) << "poll() failed for child file descriptor; bytes_read=" + << bytes_read; + return false; + } + + base::TimeTicks current_time = base::TimeTicks::Now(); + base::TimeDelta elapsed_time_cycle = current_time - previous_time; + DCHECK_GE(elapsed_time_cycle.InMilliseconds(), 0); + *remaining_time -= elapsed_time_cycle; + previous_time = current_time; + + ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read, + bytes_max - bytes_read)); + if (num_bytes <= 0) + return false; + bytes_read += num_bytes; + } + return true; +} + +} // namespace + +namespace net { + +bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) { + // Log is useful in the event you want to run a nearby script (e.g. a test) in + // the same environment as the TestServer. + VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv); + + CommandLine python_command(CommandLine::NO_PROGRAM); + if (!GetPythonCommand(&python_command)) + return false; + + python_command.AppendArgPath(testserver_path); + if (!AddCommandLineArguments(&python_command)) + return false; + + int pipefd[2]; + if (pipe(pipefd) != 0) { + PLOG(ERROR) << "Could not create pipe."; + return false; + } + + // Save the read half. The write half is sent to the child. + child_fd_ = pipefd[0]; + child_fd_closer_.reset(&child_fd_); + file_util::ScopedFD write_closer(&pipefd[1]); + base::FileHandleMappingVector map_write_fd; + map_write_fd.push_back(std::make_pair(pipefd[1], pipefd[1])); + + python_command.AppendArg("--startup-pipe=" + base::IntToString(pipefd[1])); + + // Try to kill any orphaned testserver processes that may be running. + OrphanedTestServerFilter filter(testserver_path.value(), + base::IntToString(GetPort())); + if (!base::KillProcesses("python", -1, &filter)) { + LOG(WARNING) << "Failed to clean up older orphaned testserver instances."; + } + + // Launch a new testserver process. + base::LaunchOptions options; + + options.fds_to_remap = &map_write_fd; + if (!base::LaunchProcess(python_command, options, &process_handle_)) { + LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString(); + return false; + } + + return true; +} + +bool LocalTestServer::WaitToStart() { + file_util::ScopedFD child_fd_closer(child_fd_closer_.release()); + + base::TimeDelta remaining_time = TestTimeouts::action_timeout(); + + uint32 server_data_len = 0; + if (!ReadData(child_fd_, sizeof(server_data_len), + reinterpret_cast<uint8*>(&server_data_len), + &remaining_time)) { + LOG(ERROR) << "Could not read server_data_len"; + return false; + } + std::string server_data(server_data_len, '\0'); + if (!ReadData(child_fd_, server_data_len, + reinterpret_cast<uint8*>(&server_data[0]), + &remaining_time)) { + LOG(ERROR) << "Could not read server_data (" << server_data_len + << " bytes)"; + return false; + } + + if (!ParseServerData(server_data)) { + LOG(ERROR) << "Could not parse server_data: " << server_data; + return false; + } + + return true; +} + +} // namespace net diff --git a/chromium/net/test/spawned_test_server/local_test_server_win.cc b/chromium/net/test/spawned_test_server/local_test_server_win.cc new file mode 100644 index 00000000000..fd26483ed47 --- /dev/null +++ b/chromium/net/test/spawned_test_server/local_test_server_win.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/local_test_server.h" + +#include <windows.h> +#include <wincrypt.h> + +#include "base/base_paths.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/process/launch.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/test_timeouts.h" +#include "base/threading/thread.h" +#include "base/win/scoped_handle.h" +#include "net/test/python_utils.h" + +#pragma comment(lib, "crypt32.lib") + +namespace { + +// Writes |size| bytes to |handle| and sets |*unblocked| to true. +// Used as a crude timeout mechanism by ReadData(). +void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) { + std::string unblock_data(size, '\0'); + // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the + // pipe. Make sure the call succeeded, otherwise we are very likely to hang. + DWORD bytes_written = 0; + LOG(WARNING) << "Timeout reached; unblocking pipe by writing " + << size << " bytes"; + CHECK(WriteFile(handle, unblock_data.data(), size, &bytes_written, + NULL)); + CHECK_EQ(size, bytes_written); + *unblocked = true; +} + +// Given a file handle, reads into |buffer| until |bytes_max| bytes +// has been read or an error has been encountered. Returns +// true if the read was successful. +bool ReadData(HANDLE read_fd, HANDLE write_fd, + DWORD bytes_max, uint8* buffer) { + base::Thread thread("test_server_watcher"); + if (!thread.Start()) + return false; + + // Prepare a timeout in case the server fails to start. + bool unblocked = false; + thread.message_loop()->PostDelayedTask( + FROM_HERE, base::Bind(UnblockPipe, write_fd, bytes_max, &unblocked), + TestTimeouts::action_max_timeout()); + + DWORD bytes_read = 0; + while (bytes_read < bytes_max) { + DWORD num_bytes; + if (!ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read, + &num_bytes, NULL)) { + PLOG(ERROR) << "ReadFile failed"; + return false; + } + if (num_bytes <= 0) { + LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes; + return false; + } + bytes_read += num_bytes; + } + + thread.Stop(); + // If the timeout kicked in, abort. + if (unblocked) { + LOG(ERROR) << "Timeout exceeded for ReadData"; + return false; + } + + return true; +} + +} // namespace + +namespace net { + +bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) { + CommandLine python_command(CommandLine::NO_PROGRAM); + if (!GetPythonCommand(&python_command)) + return false; + + python_command.AppendArgPath(testserver_path); + if (!AddCommandLineArguments(&python_command)) + return false; + + HANDLE child_read = NULL; + HANDLE child_write = NULL; + if (!CreatePipe(&child_read, &child_write, NULL, 0)) { + PLOG(ERROR) << "Failed to create pipe"; + return false; + } + child_read_fd_.Set(child_read); + child_write_fd_.Set(child_write); + + // Have the child inherit the write half. + if (!SetHandleInformation(child_write, HANDLE_FLAG_INHERIT, + HANDLE_FLAG_INHERIT)) { + PLOG(ERROR) << "Failed to enable pipe inheritance"; + return false; + } + + // Pass the handle on the command-line. Although HANDLE is a + // pointer, truncating it on 64-bit machines is okay. See + // http://msdn.microsoft.com/en-us/library/aa384203.aspx + // + // "64-bit versions of Windows use 32-bit handles for + // interoperability. When sharing a handle between 32-bit and 64-bit + // applications, only the lower 32 bits are significant, so it is + // safe to truncate the handle (when passing it from 64-bit to + // 32-bit) or sign-extend the handle (when passing it from 32-bit to + // 64-bit)." + python_command.AppendArg("--startup-pipe=" + + base::IntToString(reinterpret_cast<uintptr_t>(child_write))); + + job_handle_.Set(CreateJobObject(NULL, NULL)); + if (!job_handle_.IsValid()) { + LOG(ERROR) << "Could not create JobObject."; + return false; + } + + if (!base::SetJobObjectAsKillOnJobClose(job_handle_.Get())) { + LOG(ERROR) << "Could not SetInformationJobObject."; + return false; + } + + base::LaunchOptions launch_options; + launch_options.inherit_handles = true; + launch_options.job_handle = job_handle_.Get(); + if (!base::LaunchProcess(python_command, launch_options, &process_handle_)) { + LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString(); + return false; + } + + return true; +} + +bool LocalTestServer::WaitToStart() { + base::win::ScopedHandle read_fd(child_read_fd_.Take()); + base::win::ScopedHandle write_fd(child_write_fd_.Take()); + + uint32 server_data_len = 0; + if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(server_data_len), + reinterpret_cast<uint8*>(&server_data_len))) { + LOG(ERROR) << "Could not read server_data_len"; + return false; + } + std::string server_data(server_data_len, '\0'); + if (!ReadData(read_fd.Get(), write_fd.Get(), server_data_len, + reinterpret_cast<uint8*>(&server_data[0]))) { + LOG(ERROR) << "Could not read server_data (" << server_data_len + << " bytes)"; + return false; + } + + if (!ParseServerData(server_data)) { + LOG(ERROR) << "Could not parse server_data: " << server_data; + return false; + } + + return true; +} + +} // namespace net + diff --git a/chromium/net/test/spawned_test_server/remote_test_server.cc b/chromium/net/test/spawned_test_server/remote_test_server.cc new file mode 100644 index 00000000000..a3b4ef3fdf5 --- /dev/null +++ b/chromium/net/test/spawned_test_server/remote_test_server.cc @@ -0,0 +1,204 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/remote_test_server.h" + +#include <vector> + +#include "base/base_paths.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/values.h" +#include "net/base/host_port_pair.h" +#include "net/base/net_errors.h" +#include "net/test/spawned_test_server/spawner_communicator.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +// To reduce the running time of tests, tests may be sharded across several +// devices. This means that it may be necessary to support multiple instances +// of the test server spawner and the Python test server simultaneously on the +// same host. Each pair of (test server spawner, Python test server) correspond +// to a single testing device. +// The mapping between the test server spawner and the individual Python test +// servers is written to a file on the device prior to executing any tests. +base::FilePath GetTestServerPortInfoFile() { +#if !defined(OS_ANDROID) + return base::FilePath("/tmp/net-test-server-ports"); +#else + base::FilePath test_data_dir; + PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &test_data_dir); + return test_data_dir.Append("net-test-server-ports"); +#endif +} + +// Please keep it sync with dictionary SERVER_TYPES in testserver.py +std::string GetServerTypeString(BaseTestServer::Type type) { + switch (type) { + case BaseTestServer::TYPE_FTP: + return "ftp"; + case BaseTestServer::TYPE_HTTP: + case BaseTestServer::TYPE_HTTPS: + return "http"; + case BaseTestServer::TYPE_WS: + case BaseTestServer::TYPE_WSS: + return "ws"; + case BaseTestServer::TYPE_TCP_ECHO: + return "tcpecho"; + case BaseTestServer::TYPE_UDP_ECHO: + return "udpecho"; + default: + NOTREACHED(); + } + return std::string(); +} + +} // namespace + +RemoteTestServer::RemoteTestServer(Type type, + const std::string& host, + const base::FilePath& document_root) + : BaseTestServer(type, host), + spawner_server_port_(0) { + if (!Init(document_root)) + NOTREACHED(); +} + +RemoteTestServer::RemoteTestServer(Type type, + const SSLOptions& ssl_options, + const base::FilePath& document_root) + : BaseTestServer(type, ssl_options), + spawner_server_port_(0) { + if (!Init(document_root)) + NOTREACHED(); +} + +RemoteTestServer::~RemoteTestServer() { + Stop(); +} + +bool RemoteTestServer::Start() { + if (spawner_communicator_.get()) + return true; + spawner_communicator_.reset(new SpawnerCommunicator(spawner_server_port_)); + + base::DictionaryValue arguments_dict; + if (!GenerateArguments(&arguments_dict)) + return false; + + // Append the 'server-type' argument which is used by spawner server to + // pass right server type to Python test server. + arguments_dict.SetString("server-type", GetServerTypeString(type())); + + // Generate JSON-formatted argument string. + std::string arguments_string; + base::JSONWriter::Write(&arguments_dict, &arguments_string); + if (arguments_string.empty()) + return false; + + // Start the Python test server on the remote machine. + uint16 test_server_port; + if (!spawner_communicator_->StartServer(arguments_string, + &test_server_port)) { + return false; + } + if (0 == test_server_port) + return false; + + // Construct server data to initialize BaseTestServer::server_data_. + base::DictionaryValue server_data_dict; + // At this point, the test server should be spawned on the host. Update the + // local port to real port of Python test server, which will be forwarded to + // the remote server. + server_data_dict.SetInteger("port", test_server_port); + std::string server_data; + base::JSONWriter::Write(&server_data_dict, &server_data); + if (server_data.empty() || !ParseServerData(server_data)) { + LOG(ERROR) << "Could not parse server_data: " << server_data; + return false; + } + + return SetupWhenServerStarted(); +} + +bool RemoteTestServer::StartInBackground() { + NOTIMPLEMENTED(); + return false; +} + +bool RemoteTestServer::BlockUntilStarted() { + NOTIMPLEMENTED(); + return false; +} + +bool RemoteTestServer::Stop() { + if (!spawner_communicator_.get()) + return true; + CleanUpWhenStoppingServer(); + bool stopped = spawner_communicator_->StopServer(); + // Explicitly reset |spawner_communicator_| to avoid reusing the stopped one. + spawner_communicator_.reset(NULL); + return stopped; +} + +// On Android, the document root in the device is not the same as the document +// root in the host machine where the test server is launched. So prepend +// DIR_SOURCE_ROOT here to get the actual path of document root on the Android +// device. +base::FilePath RemoteTestServer::GetDocumentRoot() const { + base::FilePath src_dir; + PathService::Get(base::DIR_SOURCE_ROOT, &src_dir); + return src_dir.Append(document_root()); +} + +bool RemoteTestServer::Init(const base::FilePath& document_root) { + if (document_root.IsAbsolute()) + return false; + + // Gets ports information used by test server spawner and Python test server. + int test_server_port = 0; + + // Parse file to extract the ports information. + std::string port_info; + if (!file_util::ReadFileToString(GetTestServerPortInfoFile(), + &port_info) || + port_info.empty()) { + return false; + } + + std::vector<std::string> ports; + base::SplitString(port_info, ':', &ports); + if (ports.size() != 2u) + return false; + + // Verify the ports information. + base::StringToInt(ports[0], &spawner_server_port_); + if (!spawner_server_port_ || + static_cast<uint32>(spawner_server_port_) >= kuint16max) + return false; + + // Allow the test_server_port to be 0, which means the test server spawner + // will pick up a random port to run the test server. + base::StringToInt(ports[1], &test_server_port); + if (static_cast<uint32>(test_server_port) >= kuint16max) + return false; + SetPort(test_server_port); + + SetResourcePath(document_root, base::FilePath().AppendASCII("net") + .AppendASCII("data") + .AppendASCII("ssl") + .AppendASCII("certificates")); + return true; +} + +} // namespace net + diff --git a/chromium/net/test/spawned_test_server/remote_test_server.h b/chromium/net/test/spawned_test_server/remote_test_server.h new file mode 100644 index 00000000000..de12e4ebe44 --- /dev/null +++ b/chromium/net/test/spawned_test_server/remote_test_server.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_SPAWNED_TEST_SERVER_REMOTE_TEST_SERVER_H_ +#define NET_TEST_SPAWNED_TEST_SERVER_REMOTE_TEST_SERVER_H_ + +#include <string> + +#include "net/test/spawned_test_server/base_test_server.h" + +namespace net { + +class SpawnerCommunicator; + +// The RemoteTestServer runs an external Python-based test server in another +// machine that is different from the machine in which RemoteTestServer runs. +class RemoteTestServer : public BaseTestServer { + public: + // Initialize a TestServer listening on a specific host (IP or hostname). + // |document_root| must be a relative path under the root tree. + RemoteTestServer(Type type, + const std::string& host, + const base::FilePath& document_root); + + // Initialize a TestServer with a specific set of SSLOptions. + // |document_root| must be a relative path under the root tree. + RemoteTestServer(Type type, + const SSLOptions& ssl_options, + const base::FilePath& document_root); + + virtual ~RemoteTestServer(); + + // Starts the Python test server on the host, instead of on the device, and + // blocks until the server is ready. + bool Start() WARN_UNUSED_RESULT; + + // These are currently unused and unimplemented for RemoteTestServer. See + // the same methods in LocalTestServer for more information. + bool StartInBackground() WARN_UNUSED_RESULT; + bool BlockUntilStarted() WARN_UNUSED_RESULT; + + // Stops the Python test server that is running on the host machine. + bool Stop(); + + // Returns the actual path of document root for the test cases. This function + // should be called by test cases to retrieve the actual document root path + // on the Android device, otherwise document_root() function is used to get + // the document root. + base::FilePath GetDocumentRoot() const; + + private: + bool Init(const base::FilePath& document_root); + + // The local port used to communicate with the TestServer spawner. This is + // used to control the startup and shutdown of the Python TestServer running + // on the remote machine. On Android, this port will be redirected to the + // same port on the host machine. + int spawner_server_port_; + + // Helper to start and stop instances of the Python test server that runs on + // the host machine. + scoped_ptr<SpawnerCommunicator> spawner_communicator_; + + DISALLOW_COPY_AND_ASSIGN(RemoteTestServer); +}; + +} // namespace net + +#endif // NET_TEST_SPAWNED_TEST_SERVER_REMOTE_TEST_SERVER_H_ + diff --git a/chromium/net/test/spawned_test_server/spawned_test_server.h b/chromium/net/test/spawned_test_server/spawned_test_server.h new file mode 100644 index 00000000000..9bf5831d503 --- /dev/null +++ b/chromium/net/test/spawned_test_server/spawned_test_server.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_SPAWNED_TEST_SERVER_SPAWNED_TEST_SERVER_H_ +#define NET_TEST_SPAWNED_TEST_SERVER_SPAWNED_TEST_SERVER_H_ + +#include "build/build_config.h" + +#if defined(OS_ANDROID) +#include "net/test/spawned_test_server/remote_test_server.h" +#else +#include "net/test/spawned_test_server/local_test_server.h" +#endif + +namespace net { + +#if defined(OS_ANDROID) +typedef RemoteTestServer SpawnedTestServer; +#else +typedef LocalTestServer SpawnedTestServer; +#endif + +} // namespace net + +#endif // NET_TEST_SPAWNED_TEST_SERVER_SPAWNED_TEST_SERVER_H_ + diff --git a/chromium/net/test/spawned_test_server/spawner_communicator.cc b/chromium/net/test/spawned_test_server/spawner_communicator.cc new file mode 100644 index 00000000000..f93ff971d6f --- /dev/null +++ b/chromium/net/test/spawned_test_server/spawner_communicator.cc @@ -0,0 +1,379 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/test/spawned_test_server/spawner_communicator.h" + +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/supports_user_data.h" +#include "base/test/test_timeouts.h" +#include "base/time/time.h" +#include "base/values.h" +#include "build/build_config.h" +#include "net/base/net_util.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/base/upload_data_stream.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_test_util.h" +#include "url/gurl.h" + +namespace net { + +namespace { + +GURL GenerateSpawnerCommandURL(const std::string& command, uint16 port) { + // Always performs HTTP request for sending command to the spawner server. + return GURL(base::StringPrintf("%s:%u/%s", "http://127.0.0.1", port, + command.c_str())); +} + +int kBufferSize = 2048; + +// A class to hold all data needed to send a command to spawner server. +class SpawnerRequestData : public base::SupportsUserData::Data { + public: + SpawnerRequestData(int id, int* result_code, std::string* data_received) + : request_id_(id), + buf_(new IOBuffer(kBufferSize)), + result_code_(result_code), + data_received_(data_received), + response_started_count_(0) { + DCHECK(result_code); + *result_code_ = OK; + DCHECK(data_received); + data_received_->clear(); + } + + virtual ~SpawnerRequestData() {} + + bool DoesRequestIdMatch(int request_id) const { + return request_id_ == request_id; + } + + IOBuffer* buf() const { return buf_.get(); } + + bool IsResultOK() const { return *result_code_ == OK; } + + void ClearReceivedData() { data_received_->clear(); } + + void SetResultCode(int result_code) { *result_code_ = result_code; } + + void IncreaseResponseStartedCount() { response_started_count_++; } + + int response_started_count() const { return response_started_count_; } + + // Write data read from URLRequest::Read() to |data_received_|. Returns true + // if |num_bytes| is great than 0. |num_bytes| is 0 for EOF, < 0 on errors. + bool ConsumeBytesRead(int num_bytes) { + // Error while reading, or EOF. + if (num_bytes <= 0) + return false; + + data_received_->append(buf_->data(), num_bytes); + return true; + } + + private: + // Unique ID for the current request. + int request_id_; + + // Buffer that URLRequest writes into. + scoped_refptr<IOBuffer> buf_; + + // Holds the error condition that was hit on the current request, or OK. + int* result_code_; + + // Data received from server; + std::string* data_received_; + + // Used to track how many times the OnResponseStarted get called after + // sending a command to spawner server. + int response_started_count_; + + DISALLOW_COPY_AND_ASSIGN(SpawnerRequestData); +}; + +} // namespace + +SpawnerCommunicator::SpawnerCommunicator(uint16 port) + : io_thread_("spawner_communicator"), + event_(false, false), + port_(port), + next_id_(0), + weak_factory_(this), + is_running_(false) {} + +SpawnerCommunicator::~SpawnerCommunicator() { + DCHECK(!is_running_); +} + +void SpawnerCommunicator::WaitForResponse() { + DCHECK_NE(base::MessageLoop::current(), io_thread_.message_loop()); + event_.Wait(); + event_.Reset(); +} + +void SpawnerCommunicator::StartIOThread() { + DCHECK_NE(base::MessageLoop::current(), io_thread_.message_loop()); + if (is_running_) + return; + + allowed_port_.reset(new ScopedPortException(port_)); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + is_running_ = io_thread_.StartWithOptions(options); + DCHECK(is_running_); +} + +void SpawnerCommunicator::Shutdown() { + DCHECK_NE(base::MessageLoop::current(), io_thread_.message_loop()); + DCHECK(is_running_); + // The request and its context should be created and destroyed only on the + // IO thread. + DCHECK(!cur_request_.get()); + DCHECK(!context_.get()); + is_running_ = false; + io_thread_.Stop(); + allowed_port_.reset(); +} + +void SpawnerCommunicator::SendCommandAndWaitForResult( + const std::string& command, + const std::string& post_data, + int* result_code, + std::string* data_received) { + if (!result_code || !data_received) + return; + // Start the communicator thread to talk to test server spawner. + StartIOThread(); + DCHECK(io_thread_.message_loop()); + + // Since the method will be blocked until SpawnerCommunicator gets result + // from the spawner server or timed-out. It's safe to use base::Unretained + // when using base::Bind. + io_thread_.message_loop()->PostTask(FROM_HERE, base::Bind( + &SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread, + base::Unretained(this), command, post_data, result_code, data_received)); + WaitForResponse(); +} + +void SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread( + const std::string& command, + const std::string& post_data, + int* result_code, + std::string* data_received) { + base::MessageLoop* loop = io_thread_.message_loop(); + DCHECK(loop); + DCHECK_EQ(base::MessageLoop::current(), loop); + + // Prepare the URLRequest for sending the command. + DCHECK(!cur_request_.get()); + context_.reset(new TestURLRequestContext); + cur_request_.reset(context_->CreateRequest( + GenerateSpawnerCommandURL(command, port_), this)); + DCHECK(cur_request_.get()); + int current_request_id = ++next_id_; + SpawnerRequestData* data = new SpawnerRequestData(current_request_id, + result_code, + data_received); + DCHECK(data); + cur_request_->SetUserData(this, data); + + if (post_data.empty()) { + cur_request_->set_method("GET"); + } else { + cur_request_->set_method("POST"); + scoped_ptr<UploadElementReader> reader( + UploadOwnedBytesElementReader::CreateWithString(post_data)); + cur_request_->set_upload(make_scoped_ptr( + UploadDataStream::CreateWithReader(reader.Pass(), 0))); + net::HttpRequestHeaders headers; + headers.SetHeader(net::HttpRequestHeaders::kContentType, + "application/json"); + cur_request_->SetExtraRequestHeaders(headers); + } + + // Post a task to timeout this request if it takes too long. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&SpawnerCommunicator::OnTimeout, + weak_factory_.GetWeakPtr(), + current_request_id), + TestTimeouts::action_max_timeout()); + + // Start the request. + cur_request_->Start(); +} + +void SpawnerCommunicator::OnTimeout(int id) { + // Timeout tasks may outlive the URLRequest they reference. Make sure it + // is still applicable. + if (!cur_request_.get()) + return; + SpawnerRequestData* data = + static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this)); + DCHECK(data); + + if (!data->DoesRequestIdMatch(id)) + return; + // Set the result code and cancel the timed-out task. + data->SetResultCode(ERR_TIMED_OUT); + cur_request_->Cancel(); + OnSpawnerCommandCompleted(cur_request_.get()); +} + +void SpawnerCommunicator::OnSpawnerCommandCompleted(URLRequest* request) { + if (!cur_request_.get()) + return; + DCHECK_EQ(request, cur_request_.get()); + SpawnerRequestData* data = + static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this)); + DCHECK(data); + + // If request is faild,return the error code. + if (!cur_request_->status().is_success()) + data->SetResultCode(cur_request_->status().error()); + + if (!data->IsResultOK()) { + LOG(ERROR) << "request failed, status: " + << static_cast<int>(request->status().status()) + << ", error: " << request->status().error(); + // Clear the buffer of received data if any net error happened. + data->ClearReceivedData(); + } else { + DCHECK_EQ(1, data->response_started_count()); + } + + // Clear current request to indicate the completion of sending a command + // to spawner server and getting the result. + cur_request_.reset(); + context_.reset(); + // Invalidate the weak pointers on the IO thread. + weak_factory_.InvalidateWeakPtrs(); + + // Wakeup the caller in user thread. + event_.Signal(); +} + +void SpawnerCommunicator::ReadResult(URLRequest* request) { + DCHECK_EQ(request, cur_request_.get()); + SpawnerRequestData* data = + static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this)); + DCHECK(data); + + IOBuffer* buf = data->buf(); + // Read as many bytes as are available synchronously. + while (true) { + int num_bytes; + if (!request->Read(buf, kBufferSize, &num_bytes)) { + // Check whether the read failed synchronously. + if (!request->status().is_io_pending()) + OnSpawnerCommandCompleted(request); + return; + } + if (!data->ConsumeBytesRead(num_bytes)) { + OnSpawnerCommandCompleted(request); + return; + } + } +} + +void SpawnerCommunicator::OnResponseStarted(URLRequest* request) { + DCHECK_EQ(request, cur_request_.get()); + SpawnerRequestData* data = + static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this)); + DCHECK(data); + + data->IncreaseResponseStartedCount(); + + if (!request->status().is_success()) { + OnSpawnerCommandCompleted(request); + return; + } + + // Require HTTP responses to have a success status code. + if (request->GetResponseCode() != 200) { + LOG(ERROR) << "Spawner server returned bad status: " + << request->response_headers()->GetStatusLine(); + data->SetResultCode(ERR_FAILED); + request->Cancel(); + OnSpawnerCommandCompleted(request); + return; + } + + ReadResult(request); +} + +void SpawnerCommunicator::OnReadCompleted(URLRequest* request, int num_bytes) { + if (!cur_request_.get()) + return; + DCHECK_EQ(request, cur_request_.get()); + SpawnerRequestData* data = + static_cast<SpawnerRequestData*>(cur_request_->GetUserData(this)); + DCHECK(data); + + if (data->ConsumeBytesRead(num_bytes)) { + // Keep reading. + ReadResult(request); + } else { + OnSpawnerCommandCompleted(request); + } +} + +bool SpawnerCommunicator::StartServer(const std::string& arguments, + uint16* port) { + *port = 0; + // Send the start command to spawner server to start the Python test server + // on remote machine. + std::string server_return_data; + int result_code; + SendCommandAndWaitForResult("start", arguments, &result_code, + &server_return_data); + if (OK != result_code || server_return_data.empty()) + return false; + + // Check whether the data returned from spawner server is JSON-formatted. + scoped_ptr<base::Value> value(base::JSONReader::Read(server_return_data)); + if (!value.get() || !value->IsType(base::Value::TYPE_DICTIONARY)) { + LOG(ERROR) << "Invalid server data: " << server_return_data.c_str(); + return false; + } + + // Check whether spawner server returns valid data. + base::DictionaryValue* server_data = + static_cast<base::DictionaryValue*>(value.get()); + std::string message; + if (!server_data->GetString("message", &message) || message != "started") { + LOG(ERROR) << "Invalid message in server data: "; + return false; + } + int int_port; + if (!server_data->GetInteger("port", &int_port) || int_port <= 0 || + int_port > kuint16max) { + LOG(ERROR) << "Invalid port value: " << int_port; + return false; + } + *port = static_cast<uint16>(int_port); + return true; +} + +bool SpawnerCommunicator::StopServer() { + // It's OK to stop the SpawnerCommunicator without starting it. Some tests + // have test server on their test fixture but do not actually use it. + if (!is_running_) + return true; + + // When the test is done, ask the test server spawner to kill the test server + // on the remote machine. + std::string server_return_data; + int result_code; + SendCommandAndWaitForResult("kill", "", &result_code, &server_return_data); + Shutdown(); + if (OK != result_code || server_return_data != "killed") + return false; + return true; +} + +} // namespace net diff --git a/chromium/net/test/spawned_test_server/spawner_communicator.h b/chromium/net/test/spawned_test_server/spawner_communicator.h new file mode 100644 index 00000000000..bf426e63502 --- /dev/null +++ b/chromium/net/test/spawned_test_server/spawner_communicator.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_TEST_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_ +#define NET_TEST_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "net/url_request/url_request.h" + +namespace net { + +class ScopedPortException; + +// SpawnerCommunicator communicates with a spawner server that runs on a +// remote system. +// +// The test server used by unit tests is written in Python. However, Android +// does not support running Python code, so the test server cannot run on the +// same device running unit tests. +// +// The actual test server is executed on the host machine, while the unit tests +// themselves continue running on the device. To control the test server on the +// host machine, a second HTTP server is started, the spawner server, which +// controls the life cycle of remote test servers. Calls to start/kill the +// net::SpawnedTestServer are then redirected to the spawner server via +// this spawner communicator. +// +// Currently only three commands are supported by spawner. +// +// (1) Start Python test server, format is: +// Path: "/start". +// Method: "POST". +// Data to server: all arguments needed to launch the Python test server, in +// JSON format. +// Data from server: a JSON dict includes the following two field if success, +// "port": the port the Python test server actually listen on that. +// "message": must be "started". +// +// (2) Kill Python test server, format is: +// Path: "/kill". +// Method: "GET". +// Data to server: None. +// Data from server: String "killed" returned if success. +// +// (3) Ping Python test server to see whether it is alive, format is: +// Path: "/ping". +// Method: "GET". +// Data to server: None. +// Data from server: String "ready" returned if success. +// +// The internal I/O thread is required by net stack to perform net I/O. +// The Start/StopServer methods block the caller thread until result is +// fetched from spawner server or timed-out. +class SpawnerCommunicator : public net::URLRequest::Delegate { + public: + explicit SpawnerCommunicator(uint16 port); + virtual ~SpawnerCommunicator(); + + // Starts an instance of the Python test server on the host/ machine. + // If successfully started, returns true, setting |*port| to the port + // on the local machine that can be used to communicate with the remote + // test server. + bool StartServer(const std::string& arguments, + uint16* port) WARN_UNUSED_RESULT; + + bool StopServer() WARN_UNUSED_RESULT; + + private: + // Starts the IO thread. Called on the user thread. + void StartIOThread(); + + // Shuts down the remote test server spawner. Called on the user thread. + void Shutdown(); + + // Waits for the server response on IO thread. Called on the user thread. + void WaitForResponse(); + + // Sends a command to the test server over HTTP, returning the result code + // |*result_code| and response data in |*data_received|, those two arguments + // must be not NULL, otherwise the method returns immediately without sending + // the |command|. If |post_data| is empty, HTTP GET will be used to send + // |command|. If |post_data| is non-empty, performs an HTTP POST. + // This method is called on the user thread. + void SendCommandAndWaitForResult(const std::string& command, + const std::string& post_data, + int* result_code, + std::string* data_received); + + // Performs the command sending on the IO thread. Called on the IO thread. + void SendCommandAndWaitForResultOnIOThread(const std::string& command, + const std::string& post_data, + int* result_code, + std::string* data_received); + + // URLRequest::Delegate methods. Called on the IO thread. + virtual void OnResponseStarted(URLRequest* request) OVERRIDE; + virtual void OnReadCompleted(URLRequest* request, int num_bytes) OVERRIDE; + + // Reads Result from the response. Called on the IO thread. + void ReadResult(URLRequest* request); + + // Called on the IO thread upon completion of the spawner command. + void OnSpawnerCommandCompleted(URLRequest* request); + + // Callback on the IO thread for time-out task of request with id |id|. + void OnTimeout(int id); + + // A thread to communicate with test_spawner server. + base::Thread io_thread_; + + // WaitableEvent to notify whether the communication is done. + base::WaitableEvent event_; + + // The local port used to communicate with the TestServer spawner. This is + // used to control the startup and shutdown of the Python TestServer running + // on the remote machine. On Android, this port will be redirected to the + // same port on the host machine. + const uint16 port_; + + // Helper to add |port_| to the list of the globally explicitly allowed ports. + scoped_ptr<ScopedPortException> allowed_port_; + + // The next ID to use for |cur_request_| (monotonically increasing). + int next_id_; + + // Factory for creating the time-out task. This takes care of revoking + // outstanding tasks when |this| is deleted. + base::WeakPtrFactory<SpawnerCommunicator> weak_factory_; + + // Request context used by |cur_request_|. + scoped_ptr<URLRequestContext> context_; + + // The current (in progress) request, or NULL. + scoped_ptr<URLRequest> cur_request_; + + // Only gets/sets |is_running_| on user's thread to avoid race-condition. + bool is_running_; + + DISALLOW_COPY_AND_ASSIGN(SpawnerCommunicator); +}; + +} // namespace net + +#endif // NET_TEST_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_ diff --git a/chromium/net/test/test_certificate_data.h b/chromium/net/test/test_certificate_data.h new file mode 100644 index 00000000000..3ccda5e34cb --- /dev/null +++ b/chromium/net/test/test_certificate_data.h @@ -0,0 +1,786 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +namespace { + +// This is the SHA1 hash of the SubjectPublicKeyInfo of nist.der. +static const char kNistSPKIHash[] = + "\x15\x60\xde\x65\x4e\x03\x9f\xd0\x08\x82" + "\xa9\x6a\xc4\x65\x8e\x6f\x92\x06\x84\x35"; + +// kSatvedaSPKIs contains the SHA1 hashes of the SPKIs of the satveda.pem +// certificate chain, in order. +static const char kSatvedaSPKIs[2][21] = { + "\xd6\x2d\x7a\x12\x02\x7f\x9b\x8e\x4f\x2b" + "\x07\xc5\xfb\xf9\x2a\x2e\x9a\xcc\x0e\xe3", + "\xba\x2e\xb5\xa8\x3e\x13\x23\xd9\x53\x4b" + "\x5e\x65\xbc\xe7\xa3\x13\x5d\xd0\xa9\x96", +}; + +// kSatvedaSPKIsSHA256 contains the SHA256 hashes of the SPKIs of the +// satveda.pem certificate chain, in order. +static const char kSatvedaSPKIsSHA256[2][33] = { + "\xb9\x42\xab\xf2\x08\x63\xef\x81\x70\x88\x45\xc4\x39\xa2\x6e\x9c" + "\x2f\x9a\xf9\xf4\xcb\x23\x61\xd4\x83\x97\x61\x6d\xf2\x5b\x27\xa8", + "\x32\xb6\x4b\x66\x72\x7a\x20\x63\xe4\x06\x6f\x3b\x95\x8c\xb0\xaa" + "\xee\x57\x6a\x5e\xce\xfd\x95\x33\x99\xbb\x88\x74\x73\x1d\x95\x87", +}; + +// Certificates for test data. They're obtained with: +// +// $ openssl s_client -connect [host]:443 -showcerts > /tmp/host.pem < /dev/null +// $ openssl x509 -inform PEM -outform DER < /tmp/host.pem > /tmp/host.der +// $ xxd -i /tmp/host.der +// +// TODO(wtc): move these certificates to data files in the +// src/net/data/ssl/certificates directory. + +// The linux compiler is nitty about unused variables. Declaring variables +// in headers is not generally a good idea, but for our test data it is not +// a big deal. Mark these as potentially unused so that the compiler won't +// complain. +#ifdef __GNUC__ +#define VARIABLE_IS_NOT_USED __attribute__ ((unused)) +#else +#define VARIABLE_IS_NOT_USED +#endif + +// Google's cert. + +unsigned char VARIABLE_IS_NOT_USED google_der[] = { + 0x30, 0x82, 0x03, 0x21, 0x30, 0x82, 0x02, 0x8a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x01, 0x2a, 0x39, 0x76, 0x0d, 0x3f, 0x4f, 0xc9, 0x0b, + 0xe7, 0xbd, 0x2b, 0xcf, 0x95, 0x2e, 0x7a, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4c, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x5a, + 0x41, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, + 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x28, 0x50, 0x74, 0x79, 0x29, 0x20, + 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x0d, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x47, + 0x43, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x33, + 0x32, 0x37, 0x32, 0x32, 0x32, 0x30, 0x30, 0x37, 0x5a, 0x17, 0x0d, 0x31, + 0x30, 0x30, 0x33, 0x32, 0x37, 0x32, 0x32, 0x32, 0x30, 0x30, 0x37, 0x5a, + 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, + 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x4d, + 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, 0x77, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x17, 0x30, + 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0e, 0x77, 0x77, 0x77, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x81, + 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, + 0x81, 0x81, 0x00, 0xd6, 0xb9, 0xe1, 0xad, 0xb8, 0x61, 0x0b, 0x1f, 0x4e, + 0xb6, 0x3c, 0x09, 0x3d, 0xab, 0xe8, 0xe3, 0x2b, 0xb6, 0xe8, 0xa4, 0x3a, + 0x78, 0x2f, 0xd3, 0x51, 0x20, 0x22, 0x45, 0x95, 0xd8, 0x00, 0x91, 0x33, + 0x9a, 0xa7, 0xa2, 0x48, 0xea, 0x30, 0x57, 0x26, 0x97, 0x66, 0xc7, 0x5a, + 0xef, 0xf1, 0x9b, 0x0c, 0x3f, 0xe1, 0xb9, 0x7f, 0x7b, 0xc3, 0xc7, 0xcc, + 0xaf, 0x9c, 0xd0, 0x1f, 0x3c, 0x81, 0x15, 0x10, 0x58, 0xfc, 0x06, 0xb3, + 0xbf, 0xbc, 0x9c, 0x02, 0xb9, 0x51, 0xdc, 0xfb, 0xa6, 0xb9, 0x17, 0x42, + 0xe6, 0x46, 0xe7, 0x22, 0xcf, 0x6c, 0x27, 0x10, 0xfe, 0x54, 0xe6, 0x92, + 0x6c, 0x0c, 0x60, 0x76, 0x9a, 0xce, 0xf8, 0x7f, 0xac, 0xb8, 0x5a, 0x08, + 0x4a, 0xdc, 0xb1, 0x64, 0xbd, 0xa0, 0x74, 0x41, 0xb2, 0xac, 0x8f, 0x86, + 0x9d, 0x1a, 0xde, 0x58, 0x09, 0xfd, 0x6c, 0x0a, 0x25, 0xe0, 0x79, 0x02, + 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xe7, 0x30, 0x81, 0xe4, 0x30, 0x28, + 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x21, 0x30, 0x1f, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x42, 0x04, 0x01, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x2f, 0x30, 0x2d, 0x30, 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, + 0x77, 0x74, 0x65, 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x72, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, + 0x04, 0x66, 0x30, 0x64, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x02, 0x86, 0x32, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x5f, 0x53, 0x47, + 0x43, 0x5f, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x03, 0x81, 0x81, 0x00, 0x39, 0xb6, 0xfb, 0x11, 0xbc, 0x33, 0x2c, + 0xc3, 0x90, 0x48, 0xe3, 0x6e, 0xc3, 0x9b, 0x38, 0xb1, 0x42, 0xd1, 0x00, + 0x09, 0x58, 0x63, 0xa0, 0xe1, 0x98, 0x1c, 0x85, 0xf2, 0xef, 0x10, 0x1d, + 0x60, 0x4e, 0x51, 0x09, 0x62, 0xf5, 0x05, 0xbd, 0x9d, 0x4f, 0x87, 0x6c, + 0x98, 0x72, 0x07, 0x80, 0xc3, 0x59, 0x48, 0x14, 0xe2, 0xd6, 0xef, 0xd0, + 0x8f, 0x33, 0x6a, 0x68, 0x31, 0xfa, 0xb7, 0xbb, 0x85, 0xcc, 0xf7, 0xc7, + 0x47, 0x7b, 0x67, 0x93, 0x3c, 0xc3, 0x16, 0x51, 0x9b, 0x6f, 0x87, 0x20, + 0xfd, 0x67, 0x4c, 0x2b, 0xea, 0x6a, 0x49, 0xdb, 0x11, 0xd1, 0xbd, 0xd7, + 0x95, 0x22, 0x43, 0x7a, 0x06, 0x7b, 0x4e, 0xf6, 0x37, 0x8e, 0xa2, 0xb9, + 0xcf, 0x1f, 0xa5, 0xd2, 0xbd, 0x3b, 0x04, 0x97, 0x39, 0xb3, 0x0f, 0xfa, + 0x38, 0xb5, 0xaf, 0x55, 0x20, 0x88, 0x60, 0x93, 0xf2, 0xde, 0xdb, 0xff, + 0xdf +}; + +// webkit.org's cert. + +unsigned char VARIABLE_IS_NOT_USED webkit_der[] = { + 0x30, 0x82, 0x05, 0x0d, 0x30, 0x82, 0x03, 0xf5, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xca, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07, + 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73, + 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, + 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x05, + 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37, 0x30, 0x1e, + 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, + 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x33, 0x31, 0x38, 0x32, + 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, + 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, 0x65, 0x72, 0x74, 0x69, + 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, + 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0c, 0x4d, 0x61, + 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x31, 0x15, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0c, 0x2a, 0x2e, 0x77, + 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x81, 0x9f, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, + 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, 0xf2, 0xc0, 0x4f, 0xe0, + 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, 0xc9, 0x26, 0x3a, 0x1b, + 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, 0xf0, 0x40, 0x9e, 0x27, + 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, 0xb8, 0x9b, 0x01, 0x2f, + 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, 0x7e, 0x8e, 0xcd, 0xa6, + 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, 0x2a, 0x6d, 0x0e, 0xba, + 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, 0xdc, 0x1e, 0x34, 0xd0, + 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, 0x65, 0xf6, 0x85, 0x3a, + 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, 0xce, 0x88, 0x47, 0xcd, + 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, 0x57, 0xdc, 0xca, 0x99, + 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, 0xe9, 0xb1, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, 0x82, 0x01, 0xca, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, + 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, 0x4a, 0xa0, 0x48, 0x86, + 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x6f, 0x64, 0x61, + 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x69, + 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, 0x30, 0x49, 0x30, 0x47, + 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07, 0x17, + 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, + 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, + 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, + 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, + 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, 0xdf, 0x60, 0x32, 0xcc, + 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, 0x9c, 0x16, 0x58, 0x32, + 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, + 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, + 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x1c, 0x30, 0x1a, 0x82, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x1e, 0x6a, 0xe7, + 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, 0x99, 0xb4, 0x18, + 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, 0xf4, 0x7d, 0xd5, + 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, 0x56, 0xe3, 0x94, + 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, 0x64, 0xd3, 0x7c, + 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, 0x74, 0xe8, 0x35, + 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, 0x95, 0x24, 0x9e, + 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, 0x8d, 0x23, 0x9c, + 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, 0xa0, 0xe9, 0x7d, + 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, 0x9a, 0x01, 0x31, + 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, 0xbb, 0xbb, 0xcd, + 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, 0x71, 0x08, 0xd2, + 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, 0x12, 0x6a, 0x6f, + 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, 0xe0, 0xb5, 0xb3, + 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, 0xca, 0x20, 0xd3, + 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, 0x93, 0x8c, 0x64, + 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, 0xd3, 0xc1, 0x50, + 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, 0x7a, 0x21, 0x87, + 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, 0xbe, 0xcd, 0x70, + 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, 0x01, 0x6f, 0x37, + 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, 0x35, 0x52, 0xdd, + 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, 0x3c, 0xd8, 0x48, + 0x8a +}; + +// thawte.com's cert (it's EV-licious!). +unsigned char VARIABLE_IS_NOT_USED thawte_der[] = { + 0x30, 0x82, 0x04, 0xa5, 0x30, 0x82, 0x03, 0x8d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x17, 0x76, 0x05, 0x88, 0x95, 0x58, 0xee, 0xbb, 0x00, + 0xda, 0x10, 0xe5, 0xf0, 0xf3, 0x9c, 0xf0, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, + 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, + 0x54, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x61, 0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, + 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, + 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, + 0x30, 0x38, 0x31, 0x31, 0x31, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5a, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x31, 0x31, 0x37, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xc7, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01, 0x03, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x19, 0x30, 0x17, 0x06, 0x0b, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01, 0x02, 0x14, 0x08, 0x44, + 0x65, 0x6c, 0x61, 0x77, 0x61, 0x72, 0x65, 0x31, 0x1b, 0x30, 0x19, 0x06, + 0x03, 0x55, 0x04, 0x0f, 0x13, 0x12, 0x56, 0x31, 0x2e, 0x30, 0x2c, 0x20, + 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x35, 0x2e, 0x28, 0x62, 0x29, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x0a, 0x54, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x10, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13, 0x07, 0x33, 0x38, 0x39, 0x38, + 0x32, 0x36, 0x31, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, + 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x14, 0x0d, + 0x4d, 0x6f, 0x75, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x69, 0x65, + 0x77, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x14, 0x0e, + 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, + 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xe7, 0x89, 0x68, 0xb5, 0x6e, + 0x1d, 0x38, 0x19, 0xf6, 0x2d, 0x61, 0xc2, 0x00, 0xba, 0x6e, 0xab, 0x66, + 0x92, 0xd6, 0x85, 0x87, 0x2d, 0xd5, 0xa8, 0x58, 0xa9, 0x7a, 0x75, 0x27, + 0x9d, 0xed, 0x9e, 0xfe, 0x06, 0x71, 0x70, 0x2d, 0x21, 0x70, 0x4c, 0x3e, + 0x9c, 0xb6, 0xd5, 0x5d, 0x44, 0x92, 0xb4, 0xe0, 0xee, 0x7c, 0x0a, 0x50, + 0x4c, 0x0d, 0x67, 0x98, 0xaa, 0x01, 0x0e, 0x37, 0xa3, 0x2a, 0xef, 0xe6, + 0xe0, 0x11, 0x7b, 0xee, 0xb0, 0xa2, 0xb4, 0x32, 0x64, 0xa7, 0x0d, 0xda, + 0x6c, 0x15, 0xf8, 0xc5, 0xa5, 0x5a, 0x2c, 0xfc, 0xc9, 0xa6, 0x3c, 0x88, + 0x88, 0xbf, 0xdf, 0xa7, 0x38, 0xf0, 0x78, 0xed, 0x81, 0x93, 0x29, 0x0c, + 0xae, 0xc7, 0xab, 0x51, 0x21, 0x5e, 0xca, 0x95, 0xe5, 0x48, 0x52, 0x41, + 0xb6, 0x18, 0x60, 0x04, 0x19, 0x6f, 0x3d, 0x80, 0x14, 0xd3, 0xaf, 0x23, + 0x03, 0x10, 0x95, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x49, + 0x30, 0x82, 0x01, 0x45, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, + 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x39, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x32, 0x30, 0x30, 0x30, 0x2e, 0xa0, 0x2c, 0xa0, 0x2a, 0x86, + 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x45, 0x56, 0x43, 0x41, 0x32, 0x30, 0x30, + 0x36, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, + 0x86, 0xf8, 0x45, 0x01, 0x07, 0x30, 0x01, 0x30, 0x28, 0x30, 0x26, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, + 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xcd, 0x32, 0xe2, + 0xf2, 0x5d, 0x25, 0x47, 0x02, 0xaa, 0x8f, 0x79, 0x4b, 0x32, 0xee, 0x03, + 0x99, 0xfd, 0x30, 0x49, 0xd1, 0x30, 0x76, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6a, 0x30, 0x68, 0x30, 0x22, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, + 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x42, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x36, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, + 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, + 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x54, 0x68, 0x61, 0x77, + 0x74, 0x65, 0x5f, 0x45, 0x56, 0x5f, 0x43, 0x41, 0x5f, 0x32, 0x30, 0x30, + 0x36, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0xb2, 0xa0, 0x96, 0xdd, 0xec, 0x04, 0x38, 0x6b, 0xc3, 0x7a, 0xad, + 0x23, 0x44, 0x91, 0xe5, 0x62, 0x8c, 0xb1, 0xf6, 0x9c, 0x03, 0x21, 0x1f, + 0xef, 0x03, 0xd9, 0xca, 0x63, 0xb2, 0xf8, 0xdb, 0x5a, 0x93, 0xc2, 0xcc, + 0xf1, 0x7c, 0x6f, 0xeb, 0x0f, 0x51, 0x7b, 0x4b, 0xe7, 0xb5, 0xfc, 0xbc, + 0x9b, 0x87, 0x48, 0xcc, 0x5b, 0xf9, 0xc8, 0x66, 0xa4, 0x40, 0xac, 0xe9, + 0x42, 0x5d, 0xed, 0xf3, 0x53, 0x13, 0xe7, 0xbd, 0x6e, 0x7f, 0x50, 0x53, + 0x64, 0xb3, 0x95, 0xf1, 0x42, 0x4f, 0x36, 0x54, 0xb4, 0x1e, 0x7f, 0x18, + 0x37, 0x39, 0x3b, 0x06, 0x5b, 0xe5, 0x13, 0xd9, 0x57, 0xbc, 0xd5, 0x68, + 0xe3, 0x71, 0x5f, 0x5f, 0x2b, 0xf5, 0xa6, 0xc2, 0x8f, 0x67, 0x81, 0x3a, + 0x44, 0x63, 0x8c, 0x36, 0xfa, 0xa8, 0xed, 0xfd, 0xd7, 0x5e, 0xa2, 0x9f, + 0xb0, 0x9d, 0x47, 0x86, 0xfb, 0x71, 0x60, 0x8e, 0xc8, 0xd3, 0x45, 0x19, + 0xb7, 0xda, 0xcd, 0x9e, 0xea, 0x70, 0x10, 0x87, 0x37, 0x10, 0xdd, 0x2c, + 0x11, 0xdf, 0xee, 0x02, 0x21, 0xa6, 0x75, 0xe6, 0xd6, 0x9f, 0x54, 0x72, + 0x61, 0xe6, 0x5c, 0x1e, 0x6e, 0x16, 0xf6, 0x8e, 0xb8, 0xfc, 0x47, 0x80, + 0x05, 0x4b, 0xf7, 0x2d, 0x02, 0xee, 0x50, 0x26, 0xd1, 0x48, 0x01, 0x60, + 0xdc, 0x3c, 0xa7, 0xdb, 0xeb, 0xca, 0x8b, 0xa6, 0xff, 0x9e, 0x47, 0x5d, + 0x87, 0x40, 0xf8, 0xd2, 0x82, 0xd7, 0x13, 0x64, 0x0e, 0xd4, 0xb3, 0x29, + 0x22, 0xa7, 0xe0, 0xc8, 0xcd, 0x8c, 0x4d, 0xf5, 0x11, 0x21, 0x26, 0x02, + 0x43, 0x33, 0x8e, 0xa9, 0x3f, 0x91, 0xd4, 0x05, 0x97, 0xc9, 0xd3, 0x42, + 0x6b, 0x05, 0x99, 0xf6, 0x16, 0x71, 0x67, 0x65, 0xc7, 0x96, 0xdf, 0x2a, + 0xd7, 0x54, 0x63, 0x25, 0xc0, 0x28, 0xf7, 0x1c, 0xee, 0xcd, 0x8b, 0xe4, + 0x9d, 0x32, 0xa3, 0x81, 0x55 +}; + +// A certificate for www.paypal.com with a NULL byte in the common name. +// From http://www.gossamer-threads.com/lists/fulldisc/full-disclosure/70363 +unsigned char VARIABLE_IS_NOT_USED paypal_null_der[] = { + 0x30, 0x82, 0x06, 0x44, 0x30, 0x82, 0x05, 0xad, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x03, 0x00, 0xf0, 0x9b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x82, 0x01, + 0x12, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x45, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x09, 0x42, 0x61, 0x72, 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x42, 0x61, 0x72, + 0x63, 0x65, 0x6c, 0x6f, 0x6e, 0x61, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x20, 0x49, 0x50, 0x53, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x73, 0x2e, 0x6c, + 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x14, 0x25, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x43, 0x2e, 0x49, 0x2e, 0x46, 0x2e, + 0x20, 0x20, 0x42, 0x2d, 0x42, 0x36, 0x32, 0x32, 0x31, 0x30, 0x36, 0x39, + 0x35, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x25, + 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, + 0x69, 0x70, 0x73, 0x43, 0x41, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x32, 0x32, 0x34, 0x32, 0x33, 0x30, + 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x32, 0x32, 0x34, + 0x32, 0x33, 0x30, 0x34, 0x31, 0x37, 0x5a, 0x30, 0x81, 0x94, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x43, 0x61, + 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0d, 0x53, 0x61, 0x6e, 0x20, 0x46, + 0x72, 0x61, 0x6e, 0x63, 0x69, 0x73, 0x63, 0x6f, 0x31, 0x11, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x55, 0x6e, 0x69, + 0x74, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, + 0x77, 0x77, 0x77, 0x2e, 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, + 0x6f, 0x6d, 0x00, 0x73, 0x73, 0x6c, 0x2e, 0x73, 0x65, 0x63, 0x75, 0x72, + 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x63, 0x63, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, + 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd2, 0x69, 0xfa, 0x6f, 0x3a, + 0x00, 0xb4, 0x21, 0x1b, 0xc8, 0xb1, 0x02, 0xd7, 0x3f, 0x19, 0xb2, 0xc4, + 0x6d, 0xb4, 0x54, 0xf8, 0x8b, 0x8a, 0xcc, 0xdb, 0x72, 0xc2, 0x9e, 0x3c, + 0x60, 0xb9, 0xc6, 0x91, 0x3d, 0x82, 0xb7, 0x7d, 0x99, 0xff, 0xd1, 0x29, + 0x84, 0xc1, 0x73, 0x53, 0x9c, 0x82, 0xdd, 0xfc, 0x24, 0x8c, 0x77, 0xd5, + 0x41, 0xf3, 0xe8, 0x1e, 0x42, 0xa1, 0xad, 0x2d, 0x9e, 0xff, 0x5b, 0x10, + 0x26, 0xce, 0x9d, 0x57, 0x17, 0x73, 0x16, 0x23, 0x38, 0xc8, 0xd6, 0xf1, + 0xba, 0xa3, 0x96, 0x5b, 0x16, 0x67, 0x4a, 0x4f, 0x73, 0x97, 0x3a, 0x4d, + 0x14, 0xa4, 0xf4, 0xe2, 0x3f, 0x8b, 0x05, 0x83, 0x42, 0xd1, 0xd0, 0xdc, + 0x2f, 0x7a, 0xe5, 0xb6, 0x10, 0xb2, 0x11, 0xc0, 0xdc, 0x21, 0x2a, 0x90, + 0xff, 0xae, 0x97, 0x71, 0x5a, 0x49, 0x81, 0xac, 0x40, 0xf3, 0x3b, 0xb8, + 0x59, 0xb2, 0x4f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x03, 0x21, + 0x30, 0x82, 0x03, 0x1d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, + 0x02, 0x30, 0x00, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, + 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x06, 0x40, 0x30, 0x0b, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xf8, 0x30, + 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0x8f, 0x61, 0x34, 0x43, + 0x55, 0x14, 0x7f, 0x27, 0x09, 0xce, 0x4c, 0x8b, 0xea, 0x9b, 0x7b, 0x19, + 0x25, 0xbc, 0x6e, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, + 0x30, 0x16, 0x80, 0x14, 0x0e, 0x07, 0x60, 0xd4, 0x39, 0xc9, 0x1b, 0x5b, + 0x5d, 0x90, 0x7b, 0x23, 0xc8, 0xd2, 0x34, 0x9d, 0x4a, 0x9a, 0x46, 0x39, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x02, 0x30, 0x00, 0x30, + 0x1c, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x15, 0x30, 0x13, 0x81, 0x11, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x40, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x72, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d, 0x04, 0x65, 0x16, 0x63, 0x4f, 0x72, + 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, + 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, + 0x4f, 0x54, 0x20, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x45, 0x44, + 0x2e, 0x20, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x20, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, + 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x30, 0x2f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, + 0x01, 0x02, 0x04, 0x22, 0x16, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, + 0x32, 0x2f, 0x30, 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, + 0x42, 0x01, 0x04, 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, + 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, + 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x46, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x03, + 0x04, 0x39, 0x16, 0x37, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, + 0x72, 0x65, 0x76, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x4c, + 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, + 0x43, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x07, + 0x04, 0x36, 0x16, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, + 0x72, 0x65, 0x6e, 0x65, 0x77, 0x61, 0x6c, 0x43, 0x4c, 0x41, 0x53, 0x45, + 0x41, 0x31, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x3f, 0x30, 0x41, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x34, 0x16, + 0x32, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, + 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x68, + 0x74, 0x6d, 0x6c, 0x30, 0x81, 0x83, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, + 0x7c, 0x30, 0x7a, 0x30, 0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x70, + 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x70, 0x73, 0x63, + 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, 0x63, 0x61, 0x32, + 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, 0x31, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x3d, 0xa0, 0x3b, 0xa0, 0x39, 0x86, 0x37, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x62, 0x61, 0x63, 0x6b, + 0x2e, 0x69, 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x2f, 0x69, 0x70, 0x73, + 0x63, 0x61, 0x32, 0x30, 0x30, 0x32, 0x43, 0x4c, 0x41, 0x53, 0x45, 0x41, + 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x69, + 0x70, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x81, 0x81, 0x00, 0x68, 0xee, 0x79, 0x97, 0x97, 0xdd, 0x3b, 0xef, + 0x16, 0x6a, 0x06, 0xf2, 0x14, 0x9a, 0x6e, 0xcd, 0x9e, 0x12, 0xf7, 0xaa, + 0x83, 0x10, 0xbd, 0xd1, 0x7c, 0x98, 0xfa, 0xc7, 0xae, 0xd4, 0x0e, 0x2c, + 0x9e, 0x38, 0x05, 0x9d, 0x52, 0x60, 0xa9, 0x99, 0x0a, 0x81, 0xb4, 0x98, + 0x90, 0x1d, 0xae, 0xbb, 0x4a, 0xd7, 0xb9, 0xdc, 0x88, 0x9e, 0x37, 0x78, + 0x41, 0x5b, 0xf7, 0x82, 0xa5, 0xf2, 0xba, 0x41, 0x25, 0x5a, 0x90, 0x1a, + 0x1e, 0x45, 0x38, 0xa1, 0x52, 0x58, 0x75, 0x94, 0x26, 0x44, 0xfb, 0x20, + 0x07, 0xba, 0x44, 0xcc, 0xe5, 0x4a, 0x2d, 0x72, 0x3f, 0x98, 0x47, 0xf6, + 0x26, 0xdc, 0x05, 0x46, 0x05, 0x07, 0x63, 0x21, 0xab, 0x46, 0x9b, 0x9c, + 0x78, 0xd5, 0x54, 0x5b, 0x3d, 0x0c, 0x1e, 0xc8, 0x64, 0x8c, 0xb5, 0x50, + 0x23, 0x82, 0x6f, 0xdb, 0xb8, 0x22, 0x1c, 0x43, 0x96, 0x07, 0xa8, 0xbb +}; + +// DER-encoded X.509 DistinguishedNames. +// +// To output the subject or issuer of a certificate: +// +// openssl asn1parse -i -inform DER -in <cert> +// +// The output will contain +// SEQUENCE [This is the issuer name] +// ... +// SEQUENCE [This is the validity period] +// UTCTIME (or GENERALTIME) +// UTCTIME +// SEQUENCE [This is the subject] +// ... +// +// The OFFSET is the first column before the column, e.g. for '21:d=2', the +// offset is 21 for the SEQUENCE you're interested in. +// The LENGTH is 'hl + l'. +// +// To generate the table, then use the following for a DER-encoded +// certificate: +// +// xxd -i -s $OFFSET -l $LENGTH <cert> +// +// For PEM certificates, convert them to DER before, as in: +// +// openssl x509 -inform PEM -outform DER -in <cert> | +// xxd -i -s $OFFSET -l $LENGTH +// + +// 0:d=0 hl=2 l= 95 cons: SEQUENCE +// 2:d=1 hl=2 l= 11 cons: SET +// 4:d=2 hl=2 l= 9 cons: SEQUENCE +// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US +// 15:d=1 hl=2 l= 23 cons: SET +// 17:d=2 hl=2 l= 21 cons: SEQUENCE +// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 24:d=3 hl=2 l= 14 prim: PRINTABLESTRING :VeriSign, Inc. +// 40:d=1 hl=2 l= 55 cons: SET +// 42:d=2 hl=2 l= 53 cons: SEQUENCE +// 44:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 49:d=3 hl=2 l= 46 prim: PRINTABLESTRING : +// Class 1 Public Primary Certification Authority +const uint8 VARIABLE_IS_NOT_USED VerisignDN[] = { + 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, + 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79 +}; + +// 0:d=0 hl=2 l= 125 cons: SEQUENCE +// 2:d=1 hl=2 l= 11 cons: SET +// 4:d=2 hl=2 l= 9 cons: SEQUENCE +// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :IL +// 15:d=1 hl=2 l= 22 cons: SET +// 17:d=2 hl=2 l= 20 cons: SEQUENCE +// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 24:d=3 hl=2 l= 13 prim: PRINTABLESTRING :StartCom Ltd. +// 39:d=1 hl=2 l= 43 cons: SET +// 41:d=2 hl=2 l= 41 cons: SEQUENCE +// 43:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 48:d=3 hl=2 l= 34 prim: PRINTABLESTRING : +// Secure Digital Certificate Signing +// 84:d=1 hl=2 l= 41 cons: SET +// 86:d=2 hl=2 l= 39 cons: SEQUENCE +// 88:d=3 hl=2 l= 3 prim: OBJECT :commonName +// 93:d=3 hl=2 l= 32 prim: PRINTABLESTRING : +// StartCom Certification Authority +const uint8 VARIABLE_IS_NOT_USED StartComDN[] = { + 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, + 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, + 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, + 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 +}; + +// 0:d=0 hl=3 l= 174 cons: SEQUENCE +// 3:d=1 hl=2 l= 11 cons: SET +// 5:d=2 hl=2 l= 9 cons: SEQUENCE +// 7:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 12:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US +// 16:d=1 hl=2 l= 11 cons: SET +// 18:d=2 hl=2 l= 9 cons: SEQUENCE +// 20:d=3 hl=2 l= 3 prim: OBJECT :stateOrProvinceName +// 25:d=3 hl=2 l= 2 prim: PRINTABLESTRING :UT +// 29:d=1 hl=2 l= 23 cons: SET +// 31:d=2 hl=2 l= 21 cons: SEQUENCE +// 33:d=3 hl=2 l= 3 prim: OBJECT :localityName +// 38:d=3 hl=2 l= 14 prim: PRINTABLESTRING :Salt Lake City +// 54:d=1 hl=2 l= 30 cons: SET +// 56:d=2 hl=2 l= 28 cons: SEQUENCE +// 58:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 63:d=3 hl=2 l= 21 prim: PRINTABLESTRING :The USERTRUST Network +// 86:d=1 hl=2 l= 33 cons: SET +// 88:d=2 hl=2 l= 31 cons: SEQUENCE +// 90:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 95:d=3 hl=2 l= 24 prim: PRINTABLESTRING :http://www.usertrust.com +//121:d=1 hl=2 l= 54 cons: SET +//123:d=2 hl=2 l= 52 cons: SEQUENCE +//125:d=3 hl=2 l= 3 prim: OBJECT :commonName +//130:d=3 hl=2 l= 45 prim: PRINTABLESTRING : +// UTN-USERFirst-Client Authentication and Email +const uint8 VARIABLE_IS_NOT_USED UserTrustDN[] = { + 0x30, 0x81, 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, + 0x65, 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, + 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, + 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x2d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x45, 0x6d, 0x61, 0x69, 0x6c +}; + +// 0:d=0 hl=3 l= 190 cons: SEQUENCE +// 3:d=1 hl=2 l= 63 cons: SET +// 5:d=2 hl=2 l= 61 cons: SEQUENCE +// 7:d=3 hl=2 l= 3 prim: OBJECT :commonName +// 12:d=3 hl=2 l= 54 prim: UTF8STRING : +// TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı +// 68:d=1 hl=2 l= 11 cons: SET +// 70:d=2 hl=2 l= 9 cons: SEQUENCE +// 72:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 77:d=3 hl=2 l= 2 prim: PRINTABLESTRING :TR +// 81:d=1 hl=2 l= 15 cons: SET +// 83:d=2 hl=2 l= 13 cons: SEQUENCE +// 85:d=3 hl=2 l= 3 prim: OBJECT :localityName +// 90:d=3 hl=2 l= 6 prim: UTF8STRING :Ankara +// 98:d=1 hl=2 l= 93 cons: SET +//100:d=2 hl=2 l= 91 cons: SEQUENCE +//102:d=3 hl=2 l= 3 prim: OBJECT :organizationName +//107:d=3 hl=2 l= 84 prim: UTF8STRING : +// TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. +// (c) Kasım 2005 +const uint8 VARIABLE_IS_NOT_USED TurkTrustDN[] = { + 0x30, 0x81, 0xbe, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x36, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, + 0x20, 0x45, 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x6b, 0x20, + 0x53, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69, + 0x7a, 0x6d, 0x65, 0x74, 0x20, 0x53, 0x61, 0xc4, 0x9f, 0x6c, 0x61, 0x79, + 0xc4, 0xb1, 0x63, 0xc4, 0xb1, 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x54, 0x52, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x06, 0x41, 0x6e, 0x6b, 0x61, + 0x72, 0x61, 0x31, 0x5d, 0x30, 0x5b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x54, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, + 0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0, 0x6c, 0x65, 0x74, 0x69, + 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c, 0x69, + 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc, 0x76, 0x65, 0x6e, 0x6c, + 0x69, 0xc4, 0x9f, 0x69, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c, + 0x65, 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e, 0x2e, 0x20, 0x28, 0x63, + 0x29, 0x20, 0x4b, 0x61, 0x73, 0xc4, 0xb1, 0x6d, 0x20, 0x32, 0x30, 0x30, + 0x35, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x35, 0x31, 0x31, 0x30, 0x37, 0x31, + 0x30, 0x30, 0x37, 0x35, 0x37 +}; + +// 33:d=2 hl=3 l= 207 cons: SEQUENCE +// 36:d=3 hl=2 l= 11 cons: SET +// 38:d=4 hl=2 l= 9 cons: SEQUENCE +// 40:d=5 hl=2 l= 3 prim: OBJECT :countryName +// 45:d=5 hl=2 l= 2 prim: PRINTABLESTRING :AT +// 49:d=3 hl=3 l= 139 cons: SET +// 52:d=4 hl=3 l= 136 cons: SEQUENCE +// 55:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 60:d=5 hl=3 l= 128 prim: BMPSTRING : +// A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH +//191:d=3 hl=2 l= 24 cons: SET +//193:d=4 hl=2 l= 22 cons: SEQUENCE +//195:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +//200:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01 +//217:d=3 hl=2 l= 24 cons: SET +//219:d=4 hl=2 l= 22 cons: SEQUENCE +//221:d=5 hl=2 l= 3 prim: OBJECT :commonName +//226:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01 +const uint8 VARIABLE_IS_NOT_USED ATrustQual01DN[] = { + 0x30, 0x81, 0xcf, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x54, 0x31, 0x81, 0x8b, 0x30, 0x81, 0x88, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x1e, 0x81, 0x80, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x54, + 0x00, 0x72, 0x00, 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00, 0x47, + 0x00, 0x65, 0x00, 0x73, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x66, 0x00, 0xfc, + 0x00, 0x72, 0x00, 0x20, 0x00, 0x53, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, + 0x00, 0x65, 0x00, 0x72, 0x00, 0x68, 0x00, 0x65, 0x00, 0x69, 0x00, 0x74, + 0x00, 0x73, 0x00, 0x73, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, + 0x00, 0x6d, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x20, + 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x72, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, + 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x65, + 0x00, 0x68, 0x00, 0x72, 0x00, 0x20, 0x00, 0x47, 0x00, 0x6d, 0x00, 0x62, + 0x00, 0x48, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, + 0x0f, 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, + 0x6c, 0x2d, 0x30, 0x31, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x0f, 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, + 0x75, 0x61, 0x6c, 0x2d, 0x30, 0x31, 0x30, 0x1e, 0x17 +}; + +// 34:d=2 hl=3 l= 180 cons: SEQUENCE +// 37:d=3 hl=2 l= 20 cons: SET +// 39:d=4 hl=2 l= 18 cons: SEQUENCE +// 41:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 46:d=5 hl=2 l= 11 prim: PRINTABLESTRING :Entrust.net +// 59:d=3 hl=2 l= 64 cons: SET +// 61:d=4 hl=2 l= 62 cons: SEQUENCE +// 63:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 68:d=5 hl=2 l= 55 prim: T61STRING : +// www.entrust.net/CPS_2048 incorp. by ref. (limits liab.) +//125:d=3 hl=2 l= 37 cons: SET +//127:d=4 hl=2 l= 35 cons: SEQUENCE +//129:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +//134:d=5 hl=2 l= 28 prim: PRINTABLESTRING : +// (c) 1999 Entrust.net Limited +//164:d=3 hl=2 l= 51 cons: SET +//166:d=4 hl=2 l= 49 cons: SEQUENCE +//168:d=5 hl=2 l= 3 prim: OBJECT :commonName +//173:d=5 hl=2 l= 42 prim: PRINTABLESTRING : +// Entrust.net Certification Authority (2048) +const uint8 VARIABLE_IS_NOT_USED EntrustDN[] = { + 0x30, 0x81, 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, + 0x74, 0x31, 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, + 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, + 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, + 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, + 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, + 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, + 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, + 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, + 0x34, 0x38, 0x29 +}; + +// 46:d=2 hl=2 l= 76 cons: SEQUENCE +// 48:d=3 hl=2 l= 11 cons: SET +// 50:d=4 hl=2 l= 9 cons: SEQUENCE +// 52:d=5 hl=2 l= 3 prim: OBJECT :countryName +// 57:d=5 hl=2 l= 2 prim: PRINTABLESTRING :ZA +// 61:d=3 hl=2 l= 37 cons: SET +// 63:d=4 hl=2 l= 35 cons: SEQUENCE +// 65:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 70:d=5 hl=2 l= 28 prim: PRINTABLESTRING : +// Thawte Consulting (Pty) Ltd. +// 100:d=3 hl=2 l= 22 cons: SET +// 102:d=4 hl=2 l= 20 cons: SEQUENCE +// 104:d=5 hl=2 l= 3 prim: OBJECT :commonName +// 109:d=5 hl=2 l= 13 prim: PRINTABLESTRING :Thawte SGC CA +const uint8 VARIABLE_IS_NOT_USED ThawteDN[] = { + 0x30, 0x4C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x5A, 0x41, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x1C, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x43, 0x6F, 0x6E, + 0x73, 0x75, 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x28, 0x50, 0x74, 0x79, + 0x29, 0x20, 0x4C, 0x74, 0x64, 0x2E, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x0D, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, + 0x53, 0x47, 0x43, 0x20, 0x43, 0x41 +}; + +// 47:d=2 hl=2 l= 108 cons: SEQUENCE +// 49:d=3 hl=2 l= 11 cons: SET +// 51:d=4 hl=2 l= 9 cons: SEQUENCE +// 53:d=5 hl=2 l= 3 prim: OBJECT :countryName +// 58:d=5 hl=2 l= 2 prim: PRINTABLESTRING :US +// 62:d=3 hl=2 l= 22 cons: SET +// 64:d=4 hl=2 l= 20 cons: SEQUENCE +// 66:d=5 hl=2 l= 3 prim: OBJECT :stateOrProvinceName +// 71:d=5 hl=2 l= 13 prim: PRINTABLESTRING :Massachusetts +// 86:d=3 hl=2 l= 46 cons: SET +// 88:d=4 hl=2 l= 44 cons: SEQUENCE +// 90:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 95:d=5 hl=2 l= 37 prim: PRINTABLESTRING : +// Massachusetts Institute of Technology +// 134:d=3 hl=2 l= 21 cons: SET +// 136:d=4 hl=2 l= 19 cons: SEQUENCE +// 138:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 143:d=5 hl=2 l= 12 prim: PRINTABLESTRING :Client CA v1 +const uint8 VARIABLE_IS_NOT_USED MITDN[] = { + 0x30, 0x6C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0D, 0x4D, 0x61, 0x73, 0x73, 0x61, 0x63, 0x68, 0x75, 0x73, 0x65, + 0x74, 0x74, 0x73, 0x31, 0x2E, 0x30, 0x2C, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x25, 0x4D, 0x61, 0x73, 0x73, 0x61, 0x63, 0x68, 0x75, 0x73, 0x65, + 0x74, 0x74, 0x73, 0x20, 0x49, 0x6E, 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, + 0x65, 0x20, 0x6F, 0x66, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6E, 0x6F, 0x6C, + 0x6F, 0x67, 0x79, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0B, + 0x13, 0x0C, 0x43, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x43, 0x41, 0x20, + 0x76, 0x31 +}; + +} // namespace |