Skip to content

Commit 908f608

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

File tree

2 files changed

+221
-18
lines changed

2 files changed

+221
-18
lines changed

Diff for: include/boost/python/eventloop.hpp

+41
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,39 @@ 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+
}
107+
_exception_handler = handler;
108+
}
109+
110+
object get_exception_handler()
111+
{
112+
return _exception_handler;
113+
}
114+
115+
void default_exception_handler(object context);
116+
117+
void call_exception_handler(object context);
118+
82119
private:
83120
int64_t _timer_id = 0;
121+
object _pymod_ssl = object();
84122
object _pymod_socket = import("socket");
123+
object _pymod_traceback = import("traceback");
124+
object _pymod_logger = import("asyncio.log").attr("logger");
125+
object _exception_handler = object();
85126
boost::asio::io_context::strand _strand;
86127
std::unordered_map<int, std::unique_ptr<boost::asio::steady_timer>> _id_to_timer_map;
87128
// read: key = fd * 2 + 0, write: key = fd * 2 + 1

Diff for: src/eventloop.cpp

+180-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>
@@ -53,6 +54,7 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
5354
// TODO: print the address
5455
PyErr_SetString(PyExc_OSError, "Connect call failed {address}");
5556
}
57+
prom.set_value();
5658
}
5759
catch (const error_already_set& e)
5860
{
@@ -67,15 +69,10 @@ void _sock_connect_cb(object pymod_socket, std::promise<void>& prom, std::future
6769
{
6870
// raise
6971
}
70-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
71-
{
72-
PyErr_Clear();
73-
prom.set_exception(std::current_exception());
74-
}
7572
else
7673
{
7774
PyErr_Clear();
78-
prom.set_value();
75+
prom.set_exception(std::current_exception());
7976
}
8077
}
8178
}
@@ -91,6 +88,7 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
9188
conn = ret[0];
9289
address = ret[1];
9390
conn.attr("setblocking")(object(false));
91+
prom.set_value(make_tuple(conn, address));
9492
}
9593
catch (const error_already_set& e)
9694
{
@@ -107,19 +105,27 @@ void _sock_accept(event_loop& loop, std::promise<object>& prom, std::future<obje
107105
{
108106
// raise
109107
}
110-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
111-
{
112-
PyErr_Clear();
113-
prom.set_exception(std::current_exception());
114-
}
115108
else
116109
{
117110
PyErr_Clear();
118-
prom.set_value(make_tuple(conn, address));
111+
prom.set_exception(std::current_exception());
119112
}
120113
}
121114
}
122115

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

125131
void event_loop::_add_reader_or_writer(int fd, object f, int key)
@@ -237,6 +243,7 @@ void event_loop::sock_connect(object sock, object address)
237243
try
238244
{
239245
sock.attr("connect")(address);
246+
prom.set_value();
240247
}
241248
catch (const error_already_set& e)
242249
{
@@ -253,15 +260,10 @@ void event_loop::sock_connect(object sock, object address)
253260
{
254261
// raise
255262
}
256-
else if (PyErr_ExceptionMatches(PyExc_BaseException))
257-
{
258-
PyErr_Clear();
259-
prom.set_exception(std::current_exception());
260-
}
261263
else
262264
{
263265
PyErr_Clear();
264-
prom.set_value();
266+
prom.set_exception(std::current_exception());
265267
}
266268
}
267269
fut.wait();
@@ -281,4 +283,164 @@ void event_loop::sock_sendfile(object sock, object file, int offset, int count,
281283
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
282284
}
283285

286+
// TODO: implement this
287+
void event_loop::start_tls(object transport, object protocol, object sslcontext,
288+
bool server_side, object server_hostname, object ssl_handshake_timeout)
289+
{
290+
PyErr_SetString(PyExc_NotImplementedError, "Not implemented!");
291+
}
292+
293+
object event_loop::getaddrinfo(object host, int port, int family, int type, int proto, int flags)
294+
{
295+
call_soon(make_function(bind(_getaddrinfo_handler, host, port, family, type, proto, flags),
296+
default_call_policies(), boost::mpl::vector<void, object>()));
297+
return object();
298+
}
299+
300+
object event_loop::getnameinfo(object sockaddr, int flags)
301+
{
302+
call_soon(make_function(bind(_getnameinfo_handler, sockaddr, flags),
303+
default_call_policies(), boost::mpl::vector<void, object>()));
304+
return object();
305+
}
306+
307+
void event_loop::default_exception_handler(object context)
308+
{
309+
object message = context.attr("get")(str("message"));
310+
if (message == object())
311+
{
312+
message = str("Unhandled exception in event loop");
313+
}
314+
315+
object exception = context.attr("get")(str("exception"));
316+
object exc_info;
317+
if (exception != object())
318+
{
319+
exc_info = make_tuple(exception.attr("__class__"), exception, exception.attr("__traceback__"));
320+
}
321+
else
322+
{
323+
exc_info = object(false);
324+
}
325+
if (!PyObject_IsTrue(context.attr("__contains__")(str("source_traceback")).ptr()) &&
326+
_exception_handler != object() &&
327+
_exception_handler.attr("_source_traceback") != object())
328+
{
329+
context["handle_traceback"] = _exception_handler.attr("_source_traceback");
330+
}
331+
332+
list log_lines;
333+
log_lines.append(message);
334+
list context_keys(context.attr("keys"));
335+
context_keys.sort();
336+
for (int i = 0; i < len(context_keys); i++)
337+
{
338+
std::string key = extract<std::string>(context_keys[i]);
339+
if (key == "message" || key == "exception")
340+
continue;
341+
str value(context[key]);
342+
if (key == "source_traceback")
343+
{
344+
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
345+
value = str("Object created at (most recent call last):\n");
346+
value += tb.rstrip();
347+
}
348+
else if (key == "handle_traceback")
349+
{
350+
str tb = str("").join(_pymod_traceback.attr("format_list")(value));
351+
value = str("Handle created at (most recent call last):\n");
352+
value += tb.rstrip();
353+
}
354+
else
355+
{
356+
value = str(value.attr("__str__")());
357+
}
358+
std::ostringstream stringStream;
359+
stringStream << key << ": " << value;
360+
log_lines.append(str(stringStream.str()));
361+
}
362+
list args;
363+
dict kwargs;
364+
args.append(str("\n").join(log_lines));
365+
kwargs["exc_info"] = exc_info;
366+
_pymod_logger.attr("error")(tuple(args), **kwargs);
367+
}
368+
369+
void event_loop::call_exception_handler(object context)
370+
{
371+
if (_exception_handler == object())
372+
{
373+
try
374+
{
375+
default_exception_handler(context);
376+
}
377+
catch (const error_already_set& e)
378+
{
379+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
380+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
381+
{
382+
// raise
383+
}
384+
else
385+
{
386+
PyErr_Clear();
387+
list args;
388+
dict kwargs;
389+
args.append(str("Exception in default exception handler"));
390+
kwargs["exc_info"] = true;
391+
_pymod_logger.attr("error")(tuple(args), **kwargs);
392+
}
393+
}
394+
}
395+
else
396+
{
397+
try
398+
{
399+
_exception_handler(context);
400+
}
401+
catch (const error_already_set& e)
402+
{
403+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
404+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
405+
{
406+
// raise
407+
}
408+
else
409+
{
410+
PyObject *ptype, *pvalue, *ptraceback;
411+
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
412+
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
413+
object type(handle<>(ptype));
414+
object value(handle<>(pvalue));
415+
object traceback(handle<>(ptraceback));
416+
try
417+
{
418+
dict tmp_dict;
419+
tmp_dict["message"] = str("Unhandled error in exception handler");
420+
tmp_dict["exception"] = value;
421+
tmp_dict["context"] = context;
422+
default_exception_handler(tmp_dict);
423+
}
424+
catch (const error_already_set& e)
425+
{
426+
if (PyErr_ExceptionMatches(PyExc_SystemExit)
427+
|| PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
428+
{
429+
// raise
430+
}
431+
else
432+
{
433+
boost::python::list args;
434+
boost::python::dict kwargs;
435+
args.append(str("Exception in default exception handler"));
436+
kwargs["exc_info"] = true;
437+
_pymod_logger.attr("error")(tuple(args), **kwargs);
438+
}
439+
}
440+
}
441+
}
442+
}
443+
}
444+
445+
284446
}}}

0 commit comments

Comments
 (0)