Skip to content

Commit

Permalink
add TLS Upgrade, DNS, Error Handling API
Browse files Browse the repository at this point in the history
  • Loading branch information
philoinovsky committed Jul 4, 2021
1 parent a2a2bbe commit 0510847
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 18 deletions.
42 changes: 42 additions & 0 deletions include/boost/python/eventloop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ class event_loop
event_loop(boost::asio::io_context& ctx):
_strand{ctx}, _created_time{std::chrono::steady_clock::now()}
{
try
{
_pymod_ssl = import("ssl");
}
catch (const error_already_set& e)
{
if (PyErr_ExceptionMatches(PyExc_ImportError))
{
PyErr_Clear();
}
}
}

// TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
Expand Down Expand Up @@ -79,9 +90,40 @@ class event_loop

void sock_sendfile(object sock, object file, int offset = 0, int count = 0, bool fallback = true);

void start_tls(object transport, object protocol, object sslcontext,
bool server_side = false,
object server_hostname = object(),
object ssl_handshake_timeout = object());

object getaddrinfo(object host, int port, int family = 0, int type = 0, int proto = 0, int flags = 0);

object getnameinfo(object sockaddr, int flags = 0);

void set_exception_handler(object handler)
{
if (handler != object() && !PyObject_HasAttrString(handler.ptr(), "__call__")) {
PyErr_SetString(PyExc_TypeError, "A callable object or None is expected");
throw_error_already_set();
}
_exception_handler = handler;
}

object get_exception_handler()
{
return _exception_handler;
}

void default_exception_handler(object context);

void call_exception_handler(object context);

private:
int64_t _timer_id = 0;
object _pymod_ssl = object();
object _pymod_socket = import("socket");
object _pymod_traceback = import("traceback");
object _pymod_logger = import("asyncio.log").attr("logger");
object _exception_handler = object();
boost::asio::io_context::strand _strand;
std::unordered_map<int, std::unique_ptr<boost::asio::steady_timer>> _id_to_timer_map;
// read: key = fd * 2 + 0, write: key = fd * 2 + 1
Expand Down
209 changes: 191 additions & 18 deletions src/eventloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// 3. _ensure_fd_no_transport
// 4. _ensure_resolve

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/python.hpp>
Expand Down Expand Up @@ -52,7 +53,9 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
if (err != object(0)) {
// TODO: print the address
PyErr_SetString(PyExc_OSError, "Connect call failed {address}");
throw_error_already_set();
}
prom.set_value();
}
catch (const error_already_set& e)
{
Expand All @@ -67,15 +70,10 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
{
// raise
}
else if (PyErr_ExceptionMatches(PyExc_BaseException))
{
PyErr_Clear();
prom.set_exception(std::current_exception());
}
else
{
PyErr_Clear();
prom.set_value();
prom.set_exception(std::current_exception());
}
}
}
Expand All @@ -91,6 +89,7 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
conn = ret[0];
address = ret[1];
conn.attr("setblocking")(object(false));
prom.set_value(make_tuple(conn, address));
}
catch (const error_already_set& e)
{
Expand All @@ -107,19 +106,27 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
{
// raise
}
else if (PyErr_ExceptionMatches(PyExc_BaseException))
{
PyErr_Clear();
prom.set_exception(std::current_exception());
}
else
{
PyErr_Clear();
prom.set_value(make_tuple(conn, address));
prom.set_exception(std::current_exception());
}
}
}

void _getaddrinfo_handler(object pymod_socket, std::promise<object>& prom,
object host, int port, int family, int type, int proto, int flags)
{
object res = pymod_socket.attr("getaddrinfo")(host, port, family, type, proto, flags);
prom.set_value(res);
}

void _getnameinfo_handler(object pymod_socket, std::promise<object>& prom, object sockaddr, int flags)
{
object res = pymod_socket.attr("getnameinfo")(sockaddr, flags);
prom.set_value(res);
}

}

void event_loop::_add_reader_or_writer(int fd, object f, int key)
Expand Down Expand Up @@ -237,6 +244,7 @@ void event_loop::sock_connect(object sock, object address)
try
{
sock.attr("connect")(address);
prom.set_value();
}
catch (const error_already_set& e)
{
Expand All @@ -253,15 +261,10 @@ void event_loop::sock_connect(object sock, object address)
{
// raise
}
else if (PyErr_ExceptionMatches(PyExc_BaseException))
{
PyErr_Clear();
prom.set_exception(std::current_exception());
}
else
{
PyErr_Clear();
prom.set_value();
prom.set_exception(std::current_exception());
}
}
fut.wait();
Expand All @@ -279,6 +282,176 @@ object event_loop::sock_accept(object sock)
void event_loop::sock_sendfile(object sock, object file, int offset, int count, bool fallback)
{
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
throw_error_already_set();
}

// TODO: implement this
void event_loop::start_tls(object transport, object protocol, object sslcontext,
bool server_side, object server_hostname, object ssl_handshake_timeout)
{
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
throw_error_already_set();
}

object event_loop::getaddrinfo(object host, int port, int family, int type, int proto, int flags)
{
std::promise<object> prom;
std::future<object> fut = prom.get_future();
call_soon(make_function(
bind(_getaddrinfo_handler, _pymod_socket, boost::ref(prom), host, port, family, type, proto, flags),
default_call_policies(),
boost::mpl::vector<void, object>()));
return fut.get();
}

object event_loop::getnameinfo(object sockaddr, int flags)
{
std::promise<object> prom;
std::future<object> fut = prom.get_future();
call_soon(make_function(
bind(_getnameinfo_handler, _pymod_socket, boost::ref(prom), sockaddr, flags),
default_call_policies(),
boost::mpl::vector<void, object>()));
return fut.get();
}

void event_loop::default_exception_handler(object context)
{
object message = context.attr("get")(str("message"));
if (message == object())
{
message = str("Unhandled exception in event loop");
}

object exception = context.attr("get")(str("exception"));
object exc_info;
if (exception != object())
{
exc_info = make_tuple(exception.attr("__class__"), exception, exception.attr("__traceback__"));
}
else
{
exc_info = object(false);
}
if (!PyObject_IsTrue(context.attr("__contains__")(str("source_traceback")).ptr()) &&
_exception_handler != object() &&
_exception_handler.attr("_source_traceback") != object())
{
context["handle_traceback"] = _exception_handler.attr("_source_traceback");
}

list log_lines;
log_lines.append(message);
list context_keys(context.attr("keys"));
context_keys.sort();
for (int i = 0; i < len(context_keys); i++)
{
std::string key = extract<std::string>(context_keys[i]);
if (key == "message" || key == "exception")
continue;
str value(context[key]);
if (key == "source_traceback")
{
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
value = str("Object created at (most recent call last):\n");
value += tb.rstrip();
}
else if (key == "handle_traceback")
{
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
value = str("Handle created at (most recent call last):\n");
value += tb.rstrip();
}
else
{
value = str(value.attr("__str__")());
}
std::ostringstream stringStream;
stringStream << key << ": " << value;
log_lines.append(str(stringStream.str()));
}
list args;
dict kwargs;
args.append(str("\n").join(log_lines));
kwargs["exc_info"] = exc_info;
_pymod_logger.attr("error")(tuple(args), **kwargs);
}

void event_loop::call_exception_handler(object context)
{
if (_exception_handler == object())
{
try
{
default_exception_handler(context);
}
catch (const error_already_set& e)
{
if (PyErr_ExceptionMatches(PyExc_SystemExit)
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
{
// raise
}
else
{
PyErr_Clear();
list args;
dict kwargs;
args.append(str("Exception in default exception handler"));
kwargs["exc_info"] = true;
_pymod_logger.attr("error")(tuple(args), **kwargs);
}
}
}
else
{
try
{
_exception_handler(context);
}
catch (const error_already_set& e)
{
if (PyErr_ExceptionMatches(PyExc_SystemExit)
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
{
// raise
}
else
{
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
object type(handle<>(ptype));
object value(handle<>(pvalue));
object traceback(handle<>(ptraceback));
try
{
dict tmp_dict;
tmp_dict["message"] = str("Unhandled error in exception handler");
tmp_dict["exception"] = value;
tmp_dict["context"] = context;
default_exception_handler(tmp_dict);
}
catch (const error_already_set& e)
{
if (PyErr_ExceptionMatches(PyExc_SystemExit)
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
{
// raise
}
else
{
boost::python::list args;
boost::python::dict kwargs;
args.append(str("Exception in default exception handler"));
kwargs["exc_info"] = true;
_pymod_logger.attr("error")(tuple(args), **kwargs);
}
}
}
}
}
}


}}}

0 comments on commit 0510847

Please sign in to comment.