There are a number of problems with callbacks. First of all, OpenSSL is written as a C library, it's not meant to have Python callbacks, so a way around that is needed. Another problem is thread support. A lot of the OpenSSL I/O functions can block if the socket is in blocking mode, and then you want other Python threads to be able to do other things. The real trouble is if you've released the thread lock to do a potentially blocking operation, and the operation calls a callback. Then we must take the thread lock back5.
There are two solutions to the first problem, both of which are necessary. The first solution to use is if the C callback allows ''userdata'' to be passed to it (an arbitrary pointer normally). This is great! We can set our Python function object as the real userdata and emulate userdata for the Python function in another way. The other solution can be used if an object with an ''app_data'' system always is passed to the callback. For example, the SSL object in OpenSSL has app_data functions and in e.g. the verification callbacks, you can retrieve the related SSL object. What we do is to set our wrapper Connection object as app_data for the SSL object, and we can easily find the Python callback.
The other problem is also partially solved by app_data. Since we're associating our wrapper objects with the ''real'' objects, we can easily access data from the Connection object. The solution then is to simply include a PyThreadState variable in the Connection declaration, and write macros similar to Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS that allows specifying of the PyThreadState variable to use. Now we can simply ''begin allow threads'' before a potentially blocking operation, and ''end allow threads'' before calling a callback.