From b0182c8ca5a98d2c61f44e87e9b5dc6e8b7a9f30 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 12 Oct 2010 20:09:02 +0000 Subject: [PATCH] Issue #10075: Add a session_stats() method to SSLContext objects. --- Doc/library/ssl.rst | 26 +++++++++++++++++++++----- Lib/test/test_ssl.py | 17 +++++++++++++++++ Misc/NEWS | 2 ++ Modules/_ssl.c | 41 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index f36dbc76664..c9c6ca0de32 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -481,13 +481,17 @@ SSL Contexts .. versionadded:: 3.2 +An SSL context holds various data longer-lived than single SSL connections, +such as SSL configuration options, certificate(s) and private key(s). +It also manages a cache of SSL sessions for server-side sockets, in order +to speed up repeated connections from the same clients. + .. class:: SSLContext(protocol) - An object holding various data longer-lived than single SSL connections, - such as SSL configuration options, certificate(s) and private key(s). - You must pass *protocol* which must be one of the ``PROTOCOL_*`` constants - defined in this module. :data:`PROTOCOL_SSLv23` is recommended for - maximum interoperability. + Create a new SSL context. You must pass *protocol* which must be one + of the ``PROTOCOL_*`` constants defined in this module. + :data:`PROTOCOL_SSLv23` is recommended for maximum interoperability. + :class:`SSLContext` objects have the following methods and attributes: @@ -542,6 +546,18 @@ SSL Contexts and *suppress_ragged_eofs* have the same meaning as in the top-level :func:`wrap_socket` function. +.. method:: SSLContext.session_stats() + + Get statistics about the SSL sessions created or managed by this context. + A dictionary is returned which maps the names of each `piece of information + `_ to their + numeric values. For example, here is the total number of hits and misses + in the session cache since the context was created:: + + >>> stats = context.session_stats() + >>> stats['hits'], stats['misses'] + (0, 0) + .. attribute:: SSLContext.options An integer representing the set of SSL options enabled on this context. diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index b4ec4d52b57..bf08f6fe1eb 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -391,6 +391,23 @@ class ContextTests(unittest.TestCase): ctx.load_verify_locations(CERTFILE, CAPATH) ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH) + def test_session_stats(self): + for proto in PROTOCOLS: + ctx = ssl.SSLContext(proto) + self.assertEqual(ctx.session_stats(), { + 'number': 0, + 'connect': 0, + 'connect_good': 0, + 'connect_renegotiate': 0, + 'accept': 0, + 'accept_good': 0, + 'accept_renegotiate': 0, + 'hits': 0, + 'misses': 0, + 'timeouts': 0, + 'cache_full': 0, + }) + class NetworkedTests(unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS index ab0e29c3d80..597f76bfb0e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -13,6 +13,8 @@ Core and Builtins Library ------- +- Issue #10075: Add a session_stats() method to SSLContext objects. + - Issue #9948: Fixed problem of losing filename case information. Extensions diff --git a/Modules/_ssl.c b/Modules/_ssl.c index fd0ea43fe40..254dde69eb2 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1716,6 +1716,45 @@ context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds) return (PyObject *) newPySSLSocket(self->ctx, sock, server_side); } +static PyObject * +session_stats(PySSLContext *self, PyObject *unused) +{ + int r; + PyObject *value, *stats = PyDict_New(); + if (!stats) + return NULL; + +#define ADD_STATS(SSL_NAME, KEY_NAME) \ + value = PyLong_FromLong(SSL_CTX_sess_ ## SSL_NAME (self->ctx)); \ + if (value == NULL) \ + goto error; \ + r = PyDict_SetItemString(stats, KEY_NAME, value); \ + Py_DECREF(value); \ + if (r < 0) \ + goto error; + + ADD_STATS(number, "number"); + ADD_STATS(connect, "connect"); + ADD_STATS(connect_good, "connect_good"); + ADD_STATS(connect_renegotiate, "connect_renegotiate"); + ADD_STATS(accept, "accept"); + ADD_STATS(accept_good, "accept_good"); + ADD_STATS(accept_renegotiate, "accept_renegotiate"); + ADD_STATS(accept, "accept"); + ADD_STATS(hits, "hits"); + ADD_STATS(misses, "misses"); + ADD_STATS(timeouts, "timeouts"); + ADD_STATS(cache_full, "cache_full"); + +#undef ADD_STATS + + return stats; + +error: + Py_DECREF(stats); + return NULL; +} + static PyGetSetDef context_getsetlist[] = { {"options", (getter) get_options, (setter) set_options, NULL}, @@ -1733,6 +1772,8 @@ static struct PyMethodDef context_methods[] = { METH_VARARGS | METH_KEYWORDS, NULL}, {"load_verify_locations", (PyCFunction) load_verify_locations, METH_VARARGS | METH_KEYWORDS, NULL}, + {"session_stats", (PyCFunction) session_stats, + METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ };