blob: 8b1c9f6fc442fbc192724936aa9e8c607d5424fc [file] [log] [blame]
Jonathan Balletc7b7ba72011-07-16 15:29:47 +09001.. _internals:
2
3Internals
4=========
5
6We ran into three main problems developing this: Exceptions, callbacks and
7accessing socket methods. This is what this chapter is about.
8
9
10.. _exceptions:
11
12Exceptions
13----------
14
15We realized early that most of the exceptions would be raised by the I/O
16functions of OpenSSL, so it felt natural to mimic OpenSSL's error code system,
17translating them into Python exceptions. This naturally gives us the exceptions
18:py:exc:`.SSL.ZeroReturnError`, :py:exc:`.SSL.WantReadError`,
19:py:exc:`.SSL.WantWriteError`, :py:exc:`.SSL.WantX509LookupError` and
20:py:exc:`.SSL.SysCallError`.
21
22For more information about this, see section :ref:`openssl-ssl`.
23
24
25.. _callbacks:
26
27Callbacks
28---------
29
30There are a number of problems with callbacks. First of all, OpenSSL is written
31as a C library, it's not meant to have Python callbacks, so a way around that
32is needed. Another problem is thread support. A lot of the OpenSSL I/O
33functions can block if the socket is in blocking mode, and then you want other
34Python threads to be able to do other things. The real trouble is if you've
35released the global CPython interpreter lock to do a potentially blocking
36operation, and the operation calls a callback. Then we must take the GIL back,
37since calling Python APIs without holding it is not allowed.
38
39There are two solutions to the first problem, both of which are necessary. The
40first solution to use is if the C callback allows ''userdata'' to be passed to
41it (an arbitrary pointer normally). This is great! We can set our Python
42function object as the real userdata and emulate userdata for the Python
43function in another way. The other solution can be used if an object with an
44''app_data'' system always is passed to the callback. For example, the SSL
45object in OpenSSL has app_data functions and in e.g. the verification
46callbacks, you can retrieve the related SSL object. What we do is to set our
47wrapper :py:class:`.Connection` object as app_data for the SSL object, and we can
48easily find the Python callback.
49
50The other problem is solved using thread local variables. Whenever the GIL is
51released before calling into an OpenSSL API, the PyThreadState pointer returned
52by :c:func:`PyEval_SaveState` is stored in a global thread local variable
53(using Python's own TLS API, :c:func:`PyThread_set_key_value`). When it is
54necessary to re-acquire the GIL, either after the OpenSSL API returns or in a C
55callback invoked by that OpenSSL API, the value of the thread local variable is
56retrieved (:c:func`PyThread_get_key_value`) and used to re-acquire the GIL.
57This allows Python threads to execute while OpenSSL APIs are running and allows
58use of any particular pyOpenSSL object from any Python thread, since there is
59no per-thread state associated with any of these objects and since OpenSSL is
60threadsafe (as long as properly initialized, as pyOpenSSL initializes it).
61
62
63.. _socket-methods:
64
65Accessing Socket Methods
66------------------------
67
68We quickly saw the benefit of wrapping socket methods in the
69:py:class:`.SSL.Connection` class, for an easy transition into using SSL. The
70problem here is that the :py:mod:`socket` module lacks a C API, and all the
71methods are declared static. One approach would be to have :py:mod:`.OpenSSL` as
72a submodule to the :py:mod:`socket` module, placing all the code in
73``socketmodule.c``, but this is obviously not a good solution, since you
74might not want to import tonnes of extra stuff you're not going to use when
75importing the :py:mod:`socket` module. The other approach is to somehow get a
76pointer to the method to be called, either the C function, or a callable Python
77object. This is not really a good solution either, since there's a lot of
78lookups involved.
79
80The way it works is that you have to supply a :py:class:`socket`- **like** transport
81object to the :py:class:`.SSL.Connection`. The only requirement of this object is
82that it has a :py:meth:`fileno()` method that returns a file descriptor that's
83valid at the C level (i.e. you can use the system calls read and write). If you
84want to use the :py:meth:`connect()` or :py:meth:`accept()` methods of the
85:py:class:`.SSL.Connection` object, the transport object has to supply such
86methods too. Apart from them, any method lookups in the :py:class:`.SSL.Connection`
87object that fail are passed on to the underlying transport object.
88
89Future changes might be to allow Python-level transport objects, that instead
90of having :py:meth:`fileno()` methods, have :py:meth:`read()` and :py:meth:`write()`
91methods, so more advanced features of Python can be used. This would probably
92entail some sort of OpenSSL **BIOs**, but converting Python strings back and
93forth is expensive, so this shouldn't be used unless necessary. Other nice
94things would be to be able to pass in different transport objects for reading
95and writing, but then the :py:meth:`fileno()` method of :py:class:`.SSL.Connection`
96becomes virtually useless. Also, should the method resolution be used on the
97read-transport or the write-transport?