Skip to content

Commit 0510847

Browse files
committed
add TLS Upgrade, DNS, Error Handling API
1 parent a2a2bbe commit 0510847

File tree

2 files changed

+233
-18
lines changed

2 files changed

+233
-18
lines changed

include/boost/python/eventloop.hpp

+42
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ class event_loop
2121
event_loop(boost::asio::io_context& ctx):
2222
_strand{ctx}, _created_time{std::chrono::steady_clock::now()}
2323
{
24+
try
25+
{
26+
_pymod_ssl = import("ssl");
27+
}
28+
catch (const error_already_set& e)
29+
{
30+
if (PyErr_ExceptionMatches(PyExc_ImportError))
31+
{
32+
PyErr_Clear();
33+
}
34+
}
2435
}
2536

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

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

93+
void start_tls(object transport, object protocol, object sslcontext,
94+
bool server_side = false,
95+
object server_hostname = object(),
96+
object ssl_handshake_timeout = object());
97+
98+
object getaddrinfo(object host, int port, int family = 0, int type = 0, int proto = 0, int flags = 0);
99+
100+
object getnameinfo(object sockaddr, int flags = 0);
101+
102+
void set_exception_handler(object handler)
103+
{
104+
if (handler != object() && !PyObject_HasAttrString(handler.ptr(), "__call__")) {
105+
PyErr_SetString(PyExc_TypeError, "A callable object or None is expected");
106+
throw_error_already_set();
107+
}
108+
_exception_handler = handler;
109+
}
110+
111+
object get_exception_handler()
112+
{
113+
return _exception_handler;
114+
}
115+
116+
void default_exception_handler(object context);
117+
118+
void call_exception_handler(object context);
119+
82120
private:
83121
int64_t _timer_id = 0;
122+
object _pymod_ssl = object();
84123
object _pymod_socket = import("socket");
124+
object _pymod_traceback = import("traceback");
125+
object _pymod_logger = import("asyncio.log").attr("logger");
126+
object _exception_handler = object();
85127
boost::asio::io_context::strand _strand;
86128
std::unordered_map<int, std::unique_ptr<boost::asio::steady_timer>> _id_to_timer_map;
87129
// read: key = fd * 2 + 0, write: key = fd * 2 + 1

src/eventloop.cpp

+191-18
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// 3. _ensure_fd_no_transport
1010
// 4. _ensure_resolve
1111

12+
#include <iostream>
1213
#include <boost/asio.hpp>
1314
#include <boost/bind.hpp>
1415
#include <boost/python.hpp>
@@ -52,7 +53,9 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
5253
if (err != object(0)) {
5354
// TODO: print the address
5455
PyErr_SetString(PyExc_OSError, "Connect call failed {address}");
56+
throw_error_already_set();
5557
}
58+
prom.set_value();
5659
}
5760
catch (const error_already_set& e)
5861
{
@@ -67,15 +70,10 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
6770
{
6871
// raise
6972
}
70-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
71-
{
72-
PyErr_Clear();
73-
prom.set_exception(std::current_exception());
74-
}
7573
else
7674
{
7775
PyErr_Clear();
78-
prom.set_value();
76+
prom.set_exception(std::current_exception());
7977
}
8078
}
8179
}
@@ -91,6 +89,7 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
9189
conn = ret[0];
9290
address = ret[1];
9391
conn.attr("setblocking")(object(false));
92+
prom.set_value(make_tuple(conn, address));
9493
}
9594
catch (const error_already_set& e)
9695
{
@@ -107,19 +106,27 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
107106
{
108107
// raise
109108
}
110-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
111-
{
112-
PyErr_Clear();
113-
prom.set_exception(std::current_exception());
114-
}
115109
else
116110
{
117111
PyErr_Clear();
118-
prom.set_value(make_tuple(conn, address));
112+
prom.set_exception(std::current_exception());
119113
}
120114
}
121115
}
122116

117+
void _getaddrinfo_handler(object pymod_socket, std::promise<object>& prom,
118+
object host, int port, int family, int type, int proto, int flags)
119+
{
120+
object res = pymod_socket.attr("getaddrinfo")(host, port, family, type, proto, flags);
121+
prom.set_value(res);
122+
}
123+
124+
void _getnameinfo_handler(object pymod_socket, std::promise<object>& prom, object sockaddr, int flags)
125+
{
126+
object res = pymod_socket.attr("getnameinfo")(sockaddr, flags);
127+
prom.set_value(res);
128+
}
129+
123130
}
124131

125132
void event_loop::_add_reader_or_writer(int fd, object f, int key)
@@ -237,6 +244,7 @@ void event_loop::sock_connect(object sock, object address)
237244
try
238245
{
239246
sock.attr("connect")(address);
247+
prom.set_value();
240248
}
241249
catch (const error_already_set& e)
242250
{
@@ -253,15 +261,10 @@ void event_loop::sock_connect(object sock, object address)
253261
{
254262
// raise
255263
}
256-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
257-
{
258-
PyErr_Clear();
259-
prom.set_exception(std::current_exception());
260-
}
261264
else
262265
{
263266
PyErr_Clear();
264-
prom.set_value();
267+
prom.set_exception(std::current_exception());
265268
}
266269
}
267270
fut.wait();
@@ -279,6 +282,176 @@ object event_loop::sock_accept(object sock)
279282
void event_loop::sock_sendfile(object sock, object file, int offset, int count, bool fallback)
280283
{
281284
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
285+
throw_error_already_set();
286+
}
287+
288+
// TODO: implement this
289+
void event_loop::start_tls(object transport, object protocol, object sslcontext,
290+
bool server_side, object server_hostname, object ssl_handshake_timeout)
291+
{
292+
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
293+
throw_error_already_set();
294+
}
295+
296+
object event_loop::getaddrinfo(object host, int port, int family, int type, int proto, int flags)
297+
{
298+
std::promise<object> prom;
299+
std::future<object> fut = prom.get_future();
300+
call_soon(make_function(
301+
bind(_getaddrinfo_handler, _pymod_socket, boost::ref(prom), host, port, family, type, proto, flags),
302+
default_call_policies(),
303+
boost::mpl::vector<void, object>()));
304+
return fut.get();
305+
}
306+
307+
object event_loop::getnameinfo(object sockaddr, int flags)
308+
{
309+
std::promise<object> prom;
310+
std::future<object> fut = prom.get_future();
311+
call_soon(make_function(
312+
bind(_getnameinfo_handler, _pymod_socket, boost::ref(prom), sockaddr, flags),
313+
default_call_policies(),
314+
boost::mpl::vector<void, object>()));
315+
return fut.get();
316+
}
317+
318+
void event_loop::default_exception_handler(object context)
319+
{
320+
object message = context.attr("get")(str("message"));
321+
if (message == object())
322+
{
323+
message = str("Unhandled exception in event loop");
324+
}
325+
326+
object exception = context.attr("get")(str("exception"));
327+
object exc_info;
328+
if (exception != object())
329+
{
330+
exc_info = make_tuple(exception.attr("__class__"), exception, exception.attr("__traceback__"));
331+
}
332+
else
333+
{
334+
exc_info = object(false);
335+
}
336+
if (!PyObject_IsTrue(context.attr("__contains__")(str("source_traceback")).ptr()) &&
337+
_exception_handler != object() &&
338+
_exception_handler.attr("_source_traceback") != object())
339+
{
340+
context["handle_traceback"] = _exception_handler.attr("_source_traceback");
341+
}
342+
343+
list log_lines;
344+
log_lines.append(message);
345+
list context_keys(context.attr("keys"));
346+
context_keys.sort();
347+
for (int i = 0; i < len(context_keys); i++)
348+
{
349+
std::string key = extract<std::string>(context_keys[i]);
350+
if (key == "message" || key == "exception")
351+
continue;
352+
str value(context[key]);
353+
if (key == "source_traceback")
354+
{
355+
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
356+
value = str("Object created at (most recent call last):\n");
357+
value += tb.rstrip();
358+
}
359+
else if (key == "handle_traceback")
360+
{
361+
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
362+
value = str("Handle created at (most recent call last):\n");
363+
value += tb.rstrip();
364+
}
365+
else
366+
{
367+
value = str(value.attr("__str__")());
368+
}
369+
std::ostringstream stringStream;
370+
stringStream << key << ": " << value;
371+
log_lines.append(str(stringStream.str()));
372+
}
373+
list args;
374+
dict kwargs;
375+
args.append(str("\n").join(log_lines));
376+
kwargs["exc_info"] = exc_info;
377+
_pymod_logger.attr("error")(tuple(args), **kwargs);
378+
}
379+
380+
void event_loop::call_exception_handler(object context)
381+
{
382+
if (_exception_handler == object())
383+
{
384+
try
385+
{
386+
default_exception_handler(context);
387+
}
388+
catch (const error_already_set& e)
389+
{
390+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
391+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
392+
{
393+
// raise
394+
}
395+
else
396+
{
397+
PyErr_Clear();
398+
list args;
399+
dict kwargs;
400+
args.append(str("Exception in default exception handler"));
401+
kwargs["exc_info"] = true;
402+
_pymod_logger.attr("error")(tuple(args), **kwargs);
403+
}
404+
}
405+
}
406+
else
407+
{
408+
try
409+
{
410+
_exception_handler(context);
411+
}
412+
catch (const error_already_set& e)
413+
{
414+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
415+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
416+
{
417+
// raise
418+
}
419+
else
420+
{
421+
PyObject *ptype, *pvalue, *ptraceback;
422+
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
423+
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
424+
object type(handle<>(ptype));
425+
object value(handle<>(pvalue));
426+
object traceback(handle<>(ptraceback));
427+
try
428+
{
429+
dict tmp_dict;
430+
tmp_dict["message"] = str("Unhandled error in exception handler");
431+
tmp_dict["exception"] = value;
432+
tmp_dict["context"] = context;
433+
default_exception_handler(tmp_dict);
434+
}
435+
catch (const error_already_set& e)
436+
{
437+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
438+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
439+
{
440+
// raise
441+
}
442+
else
443+
{
444+
boost::python::list args;
445+
boost::python::dict kwargs;
446+
args.append(str("Exception in default exception handler"));
447+
kwargs["exc_info"] = true;
448+
_pymod_logger.attr("error")(tuple(args), **kwargs);
449+
}
450+
}
451+
}
452+
}
453+
}
282454
}
283455

456+
284457
}}}

0 commit comments

Comments
 (0)