
Internals
*********

We ran into three main problems developing this: Exceptions, callbacks
and accessing socket methods. This is what this chapter is about.


Exceptions
==========

We realized early that most of the exceptions would be raised by the
I/O functions of OpenSSL, so it felt natural to mimic OpenSSL's error
code system, translating them into Python exceptions. This naturally
gives us the exceptions "SSL.ZeroReturnError", "SSL.WantReadError",
"SSL.WantWriteError", "SSL.WantX509LookupError" and
"SSL.SysCallError".

For more information about this, see section *SSL --- An interface to
the SSL-specific parts of OpenSSL*.


Callbacks
=========

Callbacks were more of a problem when pyOpenSSL was written in C.
Having switched to being written in Python using cffi, callbacks are
now straightforward. The problems that originally existed no longer do
(if you are interested in the details you can find descriptions of
those problems in the version control history for this document).


Accessing Socket Methods
========================

We quickly saw the benefit of wrapping socket methods in the
"SSL.Connection" class, for an easy transition into using SSL. The
problem here is that the "socket" module lacks a C API, and all the
methods are declared static. One approach would be to have "OpenSSL"
as a submodule to the "socket" module, placing all the code in
"socketmodule.c", but this is obviously not a good solution, since you
might not want to import tonnes of extra stuff you're not going to use
when importing the "socket" module. The other approach is to somehow
get a pointer to the method to be called, either the C function, or a
callable Python object. This is not really a good solution either,
since there's a lot of lookups involved.

The way it works is that you have to supply a "socket"- **like**
transport object to the "SSL.Connection". The only requirement of this
object is that it has a "fileno()" method that returns a file
descriptor that's valid at the C level (i.e. you can use the system
calls read and write). If you want to use the "connect()" or
"accept()" methods of the "SSL.Connection" object, the transport
object has to supply such methods too. Apart from them, any method
lookups in the "SSL.Connection" object that fail are passed on to the
underlying transport object.

Future changes might be to allow Python-level transport objects, that
instead of having "fileno()" methods, have "read()" and "write()"
methods, so more advanced features of Python can be used. This would
probably entail some sort of OpenSSL **BIOs**, but converting Python
strings back and forth is expensive, so this shouldn't be used unless
necessary. Other nice things would be to be able to pass in different
transport objects for reading and writing, but then the "fileno()"
method of "SSL.Connection" becomes virtually useless. Also, should the
method resolution be used on the read-transport or the write-
transport?
