You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2014/05/01 13:43:45 UTC
svn commit: r1591622 [31/33] - in /httpd/mod_spdy/trunk: ./ base/
base/base.xcodeproj/ base/metrics/ build/ build/all.xcodeproj/
build/build_util.xcodeproj/ build/install.xcodeproj/ build/internal/
build/linux/ build/mac/ build/util/ build/win/ install...
Added: httpd/mod_spdy/trunk/scripts/loadtest.py
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/scripts/loadtest.py?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/scripts/loadtest.py (added)
+++ httpd/mod_spdy/trunk/scripts/loadtest.py Thu May 1 11:43:36 2014
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+# Copyright 2012 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# A simple script for load-testing mod_spdy (or any other SPDY server). For
+# example, to hit the server with 150 simultaneous SPDY clients, each fetching
+# the URLs https://example.com/ and https://example.com/image.jpg, you would
+# run:
+#
+# $ ./loadtest.py spdy 150 https://example.com/ https://example.com/image.jpg
+#
+# To run the same test with plain HTTPS clients instead of SPDY clients (for
+# comparison), you would run:
+#
+# $ ./loadtest.py https 150 https://example.com/ https://example.com/image.jpg
+#
+# Press Ctrl-C to stop the test.
+#
+# You must have spdycat (https://github.com/tatsuhiro-t/spdylay) installed and
+# on your $PATH in order to run SPDY tests, and you must have curl installed in
+# order to run HTTPS or HTTP tests.
+
+from __future__ import division # Always convert ints to floats for / operator
+from __future__ import print_function # Treat print as function, not keyword
+
+import re
+import subprocess
+import sys
+import time
+
+#=============================================================================#
+
+def print_usage_and_quit():
+ sys.stderr.write('Usage: {0} TYPE MAX_CLIENTS URL...\n'.format(sys.argv[0]))
+ sys.stderr.write('TYPE must be one of "spdy", "https", or "http"\n')
+ sys.stderr.write('MAX_CLIENTS must be a positive integer\n')
+ sys.exit(1)
+
+def with_scheme(url, scheme):
+ """Given a URL string, return a new URL string with the given scheme."""
+ if re.match(r'^[a-zA-Z0-9]+:', url):
+ return re.sub(r'^[a-zA-Z0-9]+:', scheme + ':', url)
+ elif url.startswith('//'):
+ return scheme + ':' + url
+ else:
+ return scheme + '://' + url
+
+
+class ClientProcess (object):
+ """A client subprocess that will try to load the URLs from the server."""
+
+ def __init__(self, key, command, factory):
+ self.__key = key
+ self.__child = subprocess.Popen(command, stdout=open('/dev/null', 'wb'))
+ self.__start_time = time.time()
+ self.__factory = factory
+
+ def get_key(self):
+ return self.__key
+
+ def get_start_time(self):
+ return self.__start_time
+
+ def check_done(self):
+ """If the client is done, print time and return True, else return False."""
+ code = self.__child.poll()
+ if code is None:
+ return False
+ else:
+ duration = time.time() - self.__start_time
+ self.__factory._client_finished(self.__key, code, duration)
+ return True
+
+ def kill(self):
+ """Shut down this client."""
+ self.__child.kill()
+
+
+class ClientFactory (object):
+ """A factory for ClientProcess objects, that also tracks stats."""
+
+ def __init__(self, command):
+ """Create a factory that will use the given command for subprocesses."""
+ self.__command = command
+ self.num_started = 0
+ self.num_finished = 0
+ self.max_duration = 0.0
+ self.total_duration = 0.0
+
+ def new_client(self):
+ """Create and return a new ClientProcess."""
+ self.num_started += 1
+ return ClientProcess(key=self.num_started, command=self.__command,
+ factory=self)
+
+ def _client_finished(self, key, code, duration):
+ """Called by each ClientProcess when it finishes."""
+ self.num_finished += 1
+ self.max_duration = max(self.max_duration, duration)
+ self.total_duration += duration
+ print('Client {0} exit {1} after {2:.3f}s'.format(key, code, duration))
+
+#=============================================================================#
+
+if len(sys.argv) < 4:
+ print_usage_and_quit()
+
+# Determine what type of test we're doing and what URL scheme to use.
+TYPE = sys.argv[1].lower()
+if TYPE not in ['spdy', 'https', 'http']:
+ print_usage_and_quit()
+SCHEME = 'https' if TYPE == 'spdy' else TYPE
+
+# Determine how many clients to have at once.
+try:
+ MAX_CLIENTS = int(sys.argv[2])
+except ValueError:
+ print_usage_and_quit()
+if MAX_CLIENTS < 1:
+ print_usage_and_quit()
+
+# Collect the URLs to fetch from.
+URLS = []
+for url in sys.argv[3:]:
+ URLS.append(with_scheme(url, SCHEME))
+
+# Put together the subprocess command to issue for each client.
+if TYPE == 'spdy':
+ # The -n flag tells spdycat throw away the downloaded data without saving it.
+ COMMAND = ['spdycat', '-n'] + URLS
+else:
+ # The -s flag tells curl to be silent (don't display progress meter); the -k
+ # flag tells curl to ignore certificate errors (e.g. self-signed certs).
+ COMMAND = ['curl', '-sk'] + URLS
+
+# Print out a summary of the test we'll be doing before we start.
+print('TYPE={0}'.format(TYPE))
+print('URLS ({0}):'.format(len(URLS)))
+for url in URLS:
+ print(' ' + url)
+print('MAX_CLIENTS={0}'.format(MAX_CLIENTS))
+
+# Run the test.
+factory = ClientFactory(COMMAND)
+clients = []
+try:
+ # Start us off with an initial batch of clients.
+ for index in xrange(MAX_CLIENTS):
+ clients.append(factory.new_client())
+ # Each time a client finishes, replace it with a new client.
+ # TODO(mdsteele): This is a busy loop, which isn't great. What we want is to
+ # sleep until one or more children are done. Maybe we could do something
+ # clever that would allow us to do a select() call here or something.
+ while True:
+ for index in xrange(MAX_CLIENTS):
+ if clients[index].check_done():
+ clients[index] = factory.new_client()
+# Stop when the user hits Ctrl-C, and print a summary of the results.
+except KeyboardInterrupt:
+ print()
+ if clients:
+ slowpoke = min(clients, key=(lambda c: c.get_key()))
+ print('Earliest unfinished client, {0}, not done after {1:.3f}s'.format(
+ slowpoke.get_key(), time.time() - slowpoke.get_start_time()))
+ if factory.num_finished > 0:
+ print('Avg time per client: {0:.3f}s ({1} started, {2} completed)'.format(
+ factory.total_duration / factory.num_finished,
+ factory.num_started, factory.num_finished))
+ print('Max time per client: {0:.3f}s'.format(factory.max_duration))
+ print("URLs served per second: {0:.3f}".format(
+ factory.num_finished * len(URLS) / factory.total_duration))
+for client in clients:
+ client.kill()
+
+#=============================================================================#
Propchange: httpd/mod_spdy/trunk/scripts/loadtest.py
------------------------------------------------------------------------------
svn:eol-style = native
Added: httpd/mod_spdy/trunk/scripts/mod_ssl_with_npn.patch
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/scripts/mod_ssl_with_npn.patch?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/scripts/mod_ssl_with_npn.patch (added)
+++ httpd/mod_spdy/trunk/scripts/mod_ssl_with_npn.patch Thu May 1 11:43:36 2014
@@ -0,0 +1,231 @@
+Index: modules/ssl/ssl_engine_init.c
+===================================================================
+--- modules/ssl/ssl_engine_init.c (revision 1367982)
++++ modules/ssl/ssl_engine_init.c (working copy)
+@@ -559,6 +559,11 @@
+ SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH);
+
+ SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
++
++#ifdef HAVE_TLS_NPN
++ SSL_CTX_set_next_protos_advertised_cb(
++ ctx, ssl_callback_AdvertiseNextProtos, NULL);
++#endif
+ }
+
+ static void ssl_init_ctx_verify(server_rec *s,
+Index: modules/ssl/ssl_engine_io.c
+===================================================================
+--- modules/ssl/ssl_engine_io.c (revision 1367982)
++++ modules/ssl/ssl_engine_io.c (working copy)
+@@ -338,6 +338,7 @@
+ apr_pool_t *pool;
+ char buffer[AP_IOBUFSIZE];
+ ssl_filter_ctx_t *filter_ctx;
++ int npn_finished; /* 1 if NPN has finished, 0 otherwise */
+ } bio_filter_in_ctx_t;
+
+ /*
+@@ -1409,6 +1410,27 @@
+ APR_BRIGADE_INSERT_TAIL(bb, bucket);
+ }
+
++#ifdef HAVE_TLS_NPN
++ /* By this point, Next Protocol Negotiation (NPN) should be completed (if
++ * our version of OpenSSL supports it). If we haven't already, find out
++ * which protocol was decided upon and inform other modules by calling
++ * npn_proto_negotiated_hook. */
++ if (!inctx->npn_finished) {
++ const unsigned char *next_proto = NULL;
++ unsigned next_proto_len = 0;
++
++ SSL_get0_next_proto_negotiated(
++ inctx->ssl, &next_proto, &next_proto_len);
++ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, f->c,
++ "SSL NPN negotiated protocol: '%s'",
++ apr_pstrmemdup(f->c->pool, (const char*)next_proto,
++ next_proto_len));
++ modssl_run_npn_proto_negotiated_hook(
++ f->c, (const char*)next_proto, next_proto_len);
++ inctx->npn_finished = 1;
++ }
++#endif
++
+ return APR_SUCCESS;
+ }
+
+@@ -1753,6 +1775,7 @@
+ inctx->block = APR_BLOCK_READ;
+ inctx->pool = c->pool;
+ inctx->filter_ctx = filter_ctx;
++ inctx->npn_finished = 0;
+ }
+
+ void ssl_io_filter_init(conn_rec *c, SSL *ssl)
+Index: modules/ssl/ssl_engine_kernel.c
+===================================================================
+--- modules/ssl/ssl_engine_kernel.c (revision 1367982)
++++ modules/ssl/ssl_engine_kernel.c (working copy)
+@@ -2139,3 +2139,84 @@
+ }
+
+ #endif /* HAVE_SRP */
++
++#ifdef HAVE_TLS_NPN
++/*
++ * This callback function is executed when SSL needs to decide what protocols
++ * to advertise during Next Protocol Negotiation (NPN). It must produce a
++ * string in wire format -- a sequence of length-prefixed strings -- indicating
++ * the advertised protocols. Refer to SSL_CTX_set_next_protos_advertised_cb
++ * in OpenSSL for reference.
++ */
++int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data_out,
++ unsigned int *size_out, void *arg)
++{
++ conn_rec *c = (conn_rec*)SSL_get_app_data(ssl);
++ apr_array_header_t *protos;
++ int num_protos;
++ unsigned int size;
++ int i;
++ unsigned char *data;
++ unsigned char *start;
++
++ *data_out = NULL;
++ *size_out = 0;
++
++ /* If the connection object is not available, then there's nothing for us
++ * to do. */
++ if (c == NULL) {
++ return SSL_TLSEXT_ERR_OK;
++ }
++
++ /* Invoke our npn_advertise_protos hook, giving other modules a chance to
++ * add alternate protocol names to advertise. */
++ protos = apr_array_make(c->pool, 0, sizeof(char*));
++ modssl_run_npn_advertise_protos_hook(c, protos);
++ num_protos = protos->nelts;
++
++ /* We now have a list of null-terminated strings; we need to concatenate
++ * them together into a single string, where each protocol name is prefixed
++ * by its length. First, calculate how long that string will be. */
++ size = 0;
++ for (i = 0; i < num_protos; ++i) {
++ const char *string = APR_ARRAY_IDX(protos, i, const char*);
++ unsigned int length = strlen(string);
++ /* If the protocol name is too long (the length must fit in one byte),
++ * then log an error and skip it. */
++ if (length > 255) {
++ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
++ "SSL NPN protocol name too long (length=%u): %s",
++ length, string);
++ continue;
++ }
++ /* Leave room for the length prefix (one byte) plus the protocol name
++ * itself. */
++ size += 1 + length;
++ }
++
++ /* If there is nothing to advertise (either because no modules added
++ * anything to the protos array, or because all strings added to the array
++ * were skipped), then we're done. */
++ if (size == 0) {
++ return SSL_TLSEXT_ERR_OK;
++ }
++
++ /* Now we can build the string. Copy each protocol name string into the
++ * larger string, prefixed by its length. */
++ data = apr_palloc(c->pool, size * sizeof(unsigned char));
++ start = data;
++ for (i = 0; i < num_protos; ++i) {
++ const char *string = APR_ARRAY_IDX(protos, i, const char*);
++ apr_size_t length = strlen(string);
++ *start = (unsigned char)length;
++ ++start;
++ memcpy(start, string, length * sizeof(unsigned char));
++ start += length;
++ }
++
++ /* Success. */
++ *data_out = data;
++ *size_out = size;
++ return SSL_TLSEXT_ERR_OK;
++}
++#endif
+Index: modules/ssl/mod_ssl.c
+===================================================================
+--- modules/ssl/mod_ssl.c (revision 1367982)
++++ modules/ssl/mod_ssl.c (working copy)
+@@ -220,6 +220,18 @@
+ AP_END_CMD
+ };
+
++/* Implement 'modssl_run_npn_advertise_protos_hook'. */
++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(
++ modssl, AP, int, npn_advertise_protos_hook,
++ (conn_rec *connection, apr_array_header_t *protos),
++ (connection, protos), OK, DECLINED);
++
++/* Implement 'modssl_run_npn_proto_negotiated_hook'. */
++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(
++ modssl, AP, int, npn_proto_negotiated_hook,
++ (conn_rec *connection, const char *proto_name, apr_size_t proto_name_len),
++ (connection, proto_name, proto_name_len), OK, DECLINED);
++
+ /*
+ * the various processing hooks
+ */
+Index: modules/ssl/mod_ssl.h
+===================================================================
+--- modules/ssl/mod_ssl.h (revision 1367982)
++++ modules/ssl/mod_ssl.h (working copy)
+@@ -60,5 +60,26 @@
+
+ APR_DECLARE_OPTIONAL_FN(apr_array_header_t *, ssl_extlist_by_oid, (request_rec *r, const char *oidstr));
+
++/** The npn_advertise_protos optional hook allows other modules to add entries
++ * to the list of protocol names advertised by the server during the Next
++ * Protocol Negotiation (NPN) portion of the SSL handshake. The hook callee is
++ * given the connection and an APR array; it should push one or more char*'s
++ * pointing to null-terminated strings (such as "http/1.1" or "spdy/2") onto
++ * the array and return OK, or do nothing and return DECLINED. */
++APR_DECLARE_EXTERNAL_HOOK(modssl, AP, int, npn_advertise_protos_hook,
++ (conn_rec *connection, apr_array_header_t *protos));
++
++/** The npn_proto_negotiated optional hook allows other modules to discover the
++ * name of the protocol that was chosen during the Next Protocol Negotiation
++ * (NPN) portion of the SSL handshake. Note that this may be the empty string
++ * (in which case modules should probably assume HTTP), or it may be a protocol
++ * that was never even advertised by the server. The hook callee is given the
++ * connection, a non-null-terminated string containing the protocol name, and
++ * the length of the string; it should do something appropriate (i.e. insert or
++ * remove filters) and return OK, or do nothing and return DECLINED. */
++APR_DECLARE_EXTERNAL_HOOK(modssl, AP, int, npn_proto_negotiated_hook,
++ (conn_rec *connection, const char *proto_name,
++ apr_size_t proto_name_len));
++
+ #endif /* __MOD_SSL_H__ */
+ /** @} */
+Index: modules/ssl/ssl_private.h
+===================================================================
+--- modules/ssl/ssl_private.h (revision 1367982)
++++ modules/ssl/ssl_private.h (working copy)
+@@ -146,6 +146,11 @@
+ #define HAVE_ECC
+ #endif
+
++#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_NEXTPROTONEG) \
++ && !defined(OPENSSL_NO_TLSEXT)
++#define HAVE_TLS_NPN
++#endif
++
+ /* OCSP stapling */
+ #if !defined(OPENSSL_NO_OCSP) && defined(SSL_CTX_set_tlsext_status_cb)
+ #define HAVE_OCSP_STAPLING
+@@ -614,6 +619,7 @@
+ unsigned char aes_key[16];
+ } modssl_ticket_key_t;
+ #endif
++int ssl_callback_AdvertiseNextProtos(SSL *ssl, const unsigned char **data, unsigned int *len, void *arg);
+
+ typedef struct SSLSrvConfigRec SSLSrvConfigRec;
+
Added: httpd/mod_spdy/trunk/testing/OWNERS
URL: http://svn.apache.org/viewvc/httpd/mod_spdy/trunk/testing/OWNERS?rev=1591622&view=auto
==============================================================================
--- httpd/mod_spdy/trunk/testing/OWNERS (added)
+++ httpd/mod_spdy/trunk/testing/OWNERS Thu May 1 11:43:36 2014
@@ -0,0 +1 @@
+*