From dfbdc1c11a201e46d61f4bc61cfbe5741fc4fd70 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 20 Jul 2021 10:37:53 +0300 Subject: Python: fixing exceptions in Future.set_result for ASGI implementation. An ASGI application can cancel the Future object returned by the receive() call. In this case, Unit's ASGI implementation should not call set_result() because the Future is already handled. In particular, the Starlette framework was noted to cancel the received Future. This patch adds a done() check for the Future before attempting a set_result(). This is related to #564 issue on GitHub. --- src/python/nxt_python_asgi_http.c | 55 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'src/python') diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index 3074c09f..c4a77d53 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -39,6 +39,8 @@ static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict); static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http); +static void nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, + PyObject *future, PyObject *msg); static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future); @@ -465,7 +467,7 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) static void nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) { - PyObject *msg, *future, *res; + PyObject *msg, *future; if (http->receive_future == NULL) { return; @@ -484,23 +486,45 @@ nxt_py_asgi_http_emit_disconnect(nxt_py_asgi_http_t *http) future = http->receive_future; http->receive_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); + nxt_py_asgi_http_set_result(http, future, msg); + + Py_DECREF(msg); +} + + +static void +nxt_py_asgi_http_set_result(nxt_py_asgi_http_t *http, PyObject *future, + PyObject *msg) +{ + PyObject *res; + + res = PyObject_CallMethodObjArgs(future, nxt_py_done_str, NULL); if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_unit_req_alert(http->req, "'done' call failed"); nxt_python_print_exception(); } + if (nxt_fast_path(res == Py_False)) { + res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, + NULL); + if (nxt_slow_path(res == NULL)) { + nxt_unit_req_alert(http->req, "'set_result' call failed"); + nxt_python_print_exception(); + } + + } else { + res = NULL; + } + Py_XDECREF(res); Py_DECREF(future); - - Py_DECREF(msg); } void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) { - PyObject *msg, *future, *res; + PyObject *msg, *future; nxt_py_asgi_http_t *http; http = req->data; @@ -524,14 +548,7 @@ nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) future = http->receive_future; http->receive_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, msg); Py_DECREF(msg); } @@ -575,15 +592,7 @@ nxt_py_asgi_http_drain(nxt_queue_link_t *lnk) future = http->send_future; http->send_future = NULL; - res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, Py_None, - NULL); - if (nxt_slow_path(res == NULL)) { - nxt_unit_req_alert(http->req, "'set_result' call failed"); - nxt_python_print_exception(); - } - - Py_XDECREF(res); - Py_DECREF(future); + nxt_py_asgi_http_set_result(http, future, Py_None); return NXT_UNIT_OK; -- cgit