1*1624Smax.romanov@nginx.com 2*1624Smax.romanov@nginx.com /* 3*1624Smax.romanov@nginx.com * Copyright (C) NGINX, Inc. 4*1624Smax.romanov@nginx.com */ 5*1624Smax.romanov@nginx.com 6*1624Smax.romanov@nginx.com 7*1624Smax.romanov@nginx.com #include <python/nxt_python.h> 8*1624Smax.romanov@nginx.com 9*1624Smax.romanov@nginx.com #if (NXT_HAVE_ASGI) 10*1624Smax.romanov@nginx.com 11*1624Smax.romanov@nginx.com #include <nxt_main.h> 12*1624Smax.romanov@nginx.com #include <nxt_unit.h> 13*1624Smax.romanov@nginx.com #include <nxt_unit_request.h> 14*1624Smax.romanov@nginx.com #include <python/nxt_python_asgi.h> 15*1624Smax.romanov@nginx.com #include <python/nxt_python_asgi_str.h> 16*1624Smax.romanov@nginx.com 17*1624Smax.romanov@nginx.com 18*1624Smax.romanov@nginx.com typedef struct { 19*1624Smax.romanov@nginx.com PyObject_HEAD 20*1624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 21*1624Smax.romanov@nginx.com nxt_queue_link_t link; 22*1624Smax.romanov@nginx.com PyObject *receive_future; 23*1624Smax.romanov@nginx.com PyObject *send_future; 24*1624Smax.romanov@nginx.com uint64_t content_length; 25*1624Smax.romanov@nginx.com uint64_t bytes_sent; 26*1624Smax.romanov@nginx.com int complete; 27*1624Smax.romanov@nginx.com PyObject *send_body; 28*1624Smax.romanov@nginx.com Py_ssize_t send_body_off; 29*1624Smax.romanov@nginx.com } nxt_py_asgi_http_t; 30*1624Smax.romanov@nginx.com 31*1624Smax.romanov@nginx.com 32*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_receive(PyObject *self, PyObject *none); 33*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http); 34*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_send(PyObject *self, PyObject *dict); 35*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, 36*1624Smax.romanov@nginx.com PyObject *dict); 37*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, 38*1624Smax.romanov@nginx.com PyObject *dict); 39*1624Smax.romanov@nginx.com static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future); 40*1624Smax.romanov@nginx.com 41*1624Smax.romanov@nginx.com 42*1624Smax.romanov@nginx.com static PyMethodDef nxt_py_asgi_http_methods[] = { 43*1624Smax.romanov@nginx.com { "receive", nxt_py_asgi_http_receive, METH_NOARGS, 0 }, 44*1624Smax.romanov@nginx.com { "send", nxt_py_asgi_http_send, METH_O, 0 }, 45*1624Smax.romanov@nginx.com { "_done", nxt_py_asgi_http_done, METH_O, 0 }, 46*1624Smax.romanov@nginx.com { NULL, NULL, 0, 0 } 47*1624Smax.romanov@nginx.com }; 48*1624Smax.romanov@nginx.com 49*1624Smax.romanov@nginx.com static PyAsyncMethods nxt_py_asgi_async_methods = { 50*1624Smax.romanov@nginx.com .am_await = nxt_py_asgi_await, 51*1624Smax.romanov@nginx.com }; 52*1624Smax.romanov@nginx.com 53*1624Smax.romanov@nginx.com static PyTypeObject nxt_py_asgi_http_type = { 54*1624Smax.romanov@nginx.com PyVarObject_HEAD_INIT(NULL, 0) 55*1624Smax.romanov@nginx.com 56*1624Smax.romanov@nginx.com .tp_name = "unit._asgi_http", 57*1624Smax.romanov@nginx.com .tp_basicsize = sizeof(nxt_py_asgi_http_t), 58*1624Smax.romanov@nginx.com .tp_dealloc = nxt_py_asgi_dealloc, 59*1624Smax.romanov@nginx.com .tp_as_async = &nxt_py_asgi_async_methods, 60*1624Smax.romanov@nginx.com .tp_flags = Py_TPFLAGS_DEFAULT, 61*1624Smax.romanov@nginx.com .tp_doc = "unit ASGI HTTP request object", 62*1624Smax.romanov@nginx.com .tp_iter = nxt_py_asgi_iter, 63*1624Smax.romanov@nginx.com .tp_iternext = nxt_py_asgi_next, 64*1624Smax.romanov@nginx.com .tp_methods = nxt_py_asgi_http_methods, 65*1624Smax.romanov@nginx.com }; 66*1624Smax.romanov@nginx.com 67*1624Smax.romanov@nginx.com static Py_ssize_t nxt_py_asgi_http_body_buf_size = 32 * 1024 * 1024; 68*1624Smax.romanov@nginx.com 69*1624Smax.romanov@nginx.com 70*1624Smax.romanov@nginx.com nxt_int_t 71*1624Smax.romanov@nginx.com nxt_py_asgi_http_init(nxt_task_t *task) 72*1624Smax.romanov@nginx.com { 73*1624Smax.romanov@nginx.com if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_http_type) != 0)) { 74*1624Smax.romanov@nginx.com nxt_alert(task, "Python failed to initialize the 'http' type object"); 75*1624Smax.romanov@nginx.com return NXT_ERROR; 76*1624Smax.romanov@nginx.com } 77*1624Smax.romanov@nginx.com 78*1624Smax.romanov@nginx.com return NXT_OK; 79*1624Smax.romanov@nginx.com } 80*1624Smax.romanov@nginx.com 81*1624Smax.romanov@nginx.com 82*1624Smax.romanov@nginx.com PyObject * 83*1624Smax.romanov@nginx.com nxt_py_asgi_http_create(nxt_unit_request_info_t *req) 84*1624Smax.romanov@nginx.com { 85*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 86*1624Smax.romanov@nginx.com 87*1624Smax.romanov@nginx.com http = PyObject_New(nxt_py_asgi_http_t, &nxt_py_asgi_http_type); 88*1624Smax.romanov@nginx.com 89*1624Smax.romanov@nginx.com if (nxt_fast_path(http != NULL)) { 90*1624Smax.romanov@nginx.com http->req = req; 91*1624Smax.romanov@nginx.com http->receive_future = NULL; 92*1624Smax.romanov@nginx.com http->send_future = NULL; 93*1624Smax.romanov@nginx.com http->content_length = -1; 94*1624Smax.romanov@nginx.com http->bytes_sent = 0; 95*1624Smax.romanov@nginx.com http->complete = 0; 96*1624Smax.romanov@nginx.com http->send_body = NULL; 97*1624Smax.romanov@nginx.com http->send_body_off = 0; 98*1624Smax.romanov@nginx.com } 99*1624Smax.romanov@nginx.com 100*1624Smax.romanov@nginx.com return (PyObject *) http; 101*1624Smax.romanov@nginx.com } 102*1624Smax.romanov@nginx.com 103*1624Smax.romanov@nginx.com 104*1624Smax.romanov@nginx.com static PyObject * 105*1624Smax.romanov@nginx.com nxt_py_asgi_http_receive(PyObject *self, PyObject *none) 106*1624Smax.romanov@nginx.com { 107*1624Smax.romanov@nginx.com PyObject *msg, *future; 108*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 109*1624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 110*1624Smax.romanov@nginx.com 111*1624Smax.romanov@nginx.com http = (nxt_py_asgi_http_t *) self; 112*1624Smax.romanov@nginx.com req = http->req; 113*1624Smax.romanov@nginx.com 114*1624Smax.romanov@nginx.com nxt_unit_req_debug(req, "asgi_http_receive"); 115*1624Smax.romanov@nginx.com 116*1624Smax.romanov@nginx.com msg = nxt_py_asgi_http_read_msg(http); 117*1624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 118*1624Smax.romanov@nginx.com return NULL; 119*1624Smax.romanov@nginx.com } 120*1624Smax.romanov@nginx.com 121*1624Smax.romanov@nginx.com future = PyObject_CallObject(nxt_py_loop_create_future, NULL); 122*1624Smax.romanov@nginx.com if (nxt_slow_path(future == NULL)) { 123*1624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create Future object"); 124*1624Smax.romanov@nginx.com nxt_python_print_exception(); 125*1624Smax.romanov@nginx.com 126*1624Smax.romanov@nginx.com Py_DECREF(msg); 127*1624Smax.romanov@nginx.com 128*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 129*1624Smax.romanov@nginx.com "failed to create Future object"); 130*1624Smax.romanov@nginx.com } 131*1624Smax.romanov@nginx.com 132*1624Smax.romanov@nginx.com if (msg != Py_None) { 133*1624Smax.romanov@nginx.com return nxt_py_asgi_set_result_soon(req, future, msg); 134*1624Smax.romanov@nginx.com } 135*1624Smax.romanov@nginx.com 136*1624Smax.romanov@nginx.com http->receive_future = future; 137*1624Smax.romanov@nginx.com Py_INCREF(http->receive_future); 138*1624Smax.romanov@nginx.com 139*1624Smax.romanov@nginx.com Py_DECREF(msg); 140*1624Smax.romanov@nginx.com 141*1624Smax.romanov@nginx.com return future; 142*1624Smax.romanov@nginx.com } 143*1624Smax.romanov@nginx.com 144*1624Smax.romanov@nginx.com 145*1624Smax.romanov@nginx.com static PyObject * 146*1624Smax.romanov@nginx.com nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http) 147*1624Smax.romanov@nginx.com { 148*1624Smax.romanov@nginx.com char *body_buf; 149*1624Smax.romanov@nginx.com ssize_t read_res; 150*1624Smax.romanov@nginx.com PyObject *msg, *body; 151*1624Smax.romanov@nginx.com Py_ssize_t size; 152*1624Smax.romanov@nginx.com nxt_unit_request_info_t *req; 153*1624Smax.romanov@nginx.com 154*1624Smax.romanov@nginx.com req = http->req; 155*1624Smax.romanov@nginx.com 156*1624Smax.romanov@nginx.com size = req->content_length; 157*1624Smax.romanov@nginx.com 158*1624Smax.romanov@nginx.com if (size > nxt_py_asgi_http_body_buf_size) { 159*1624Smax.romanov@nginx.com size = nxt_py_asgi_http_body_buf_size; 160*1624Smax.romanov@nginx.com } 161*1624Smax.romanov@nginx.com 162*1624Smax.romanov@nginx.com if (size > 0) { 163*1624Smax.romanov@nginx.com body = PyBytes_FromStringAndSize(NULL, size); 164*1624Smax.romanov@nginx.com if (nxt_slow_path(body == NULL)) { 165*1624Smax.romanov@nginx.com nxt_unit_req_alert(req, "Python failed to create body byte string"); 166*1624Smax.romanov@nginx.com nxt_python_print_exception(); 167*1624Smax.romanov@nginx.com 168*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 169*1624Smax.romanov@nginx.com "failed to create Bytes object"); 170*1624Smax.romanov@nginx.com } 171*1624Smax.romanov@nginx.com 172*1624Smax.romanov@nginx.com body_buf = PyBytes_AS_STRING(body); 173*1624Smax.romanov@nginx.com 174*1624Smax.romanov@nginx.com read_res = nxt_unit_request_read(req, body_buf, size); 175*1624Smax.romanov@nginx.com 176*1624Smax.romanov@nginx.com } else { 177*1624Smax.romanov@nginx.com body = NULL; 178*1624Smax.romanov@nginx.com read_res = 0; 179*1624Smax.romanov@nginx.com } 180*1624Smax.romanov@nginx.com 181*1624Smax.romanov@nginx.com if (read_res > 0 || read_res == size) { 182*1624Smax.romanov@nginx.com msg = nxt_py_asgi_new_msg(req, nxt_py_http_request_str); 183*1624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 184*1624Smax.romanov@nginx.com Py_XDECREF(body); 185*1624Smax.romanov@nginx.com 186*1624Smax.romanov@nginx.com return NULL; 187*1624Smax.romanov@nginx.com } 188*1624Smax.romanov@nginx.com 189*1624Smax.romanov@nginx.com #define SET_ITEM(dict, key, value) \ 190*1624Smax.romanov@nginx.com if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \ 191*1624Smax.romanov@nginx.com == -1)) \ 192*1624Smax.romanov@nginx.com { \ 193*1624Smax.romanov@nginx.com nxt_unit_req_alert(req, \ 194*1624Smax.romanov@nginx.com "Python failed to set '" #dict "." #key "' item"); \ 195*1624Smax.romanov@nginx.com PyErr_SetString(PyExc_RuntimeError, \ 196*1624Smax.romanov@nginx.com "Python failed to set '" #dict "." #key "' item"); \ 197*1624Smax.romanov@nginx.com goto fail; \ 198*1624Smax.romanov@nginx.com } 199*1624Smax.romanov@nginx.com 200*1624Smax.romanov@nginx.com if (body != NULL) { 201*1624Smax.romanov@nginx.com SET_ITEM(msg, body, body) 202*1624Smax.romanov@nginx.com } 203*1624Smax.romanov@nginx.com 204*1624Smax.romanov@nginx.com if (req->content_length > 0) { 205*1624Smax.romanov@nginx.com SET_ITEM(msg, more_body, Py_True) 206*1624Smax.romanov@nginx.com } 207*1624Smax.romanov@nginx.com 208*1624Smax.romanov@nginx.com #undef SET_ITEM 209*1624Smax.romanov@nginx.com 210*1624Smax.romanov@nginx.com Py_XDECREF(body); 211*1624Smax.romanov@nginx.com 212*1624Smax.romanov@nginx.com return msg; 213*1624Smax.romanov@nginx.com } 214*1624Smax.romanov@nginx.com 215*1624Smax.romanov@nginx.com Py_XDECREF(body); 216*1624Smax.romanov@nginx.com 217*1624Smax.romanov@nginx.com Py_RETURN_NONE; 218*1624Smax.romanov@nginx.com 219*1624Smax.romanov@nginx.com fail: 220*1624Smax.romanov@nginx.com 221*1624Smax.romanov@nginx.com Py_DECREF(msg); 222*1624Smax.romanov@nginx.com Py_XDECREF(body); 223*1624Smax.romanov@nginx.com 224*1624Smax.romanov@nginx.com return NULL; 225*1624Smax.romanov@nginx.com } 226*1624Smax.romanov@nginx.com 227*1624Smax.romanov@nginx.com 228*1624Smax.romanov@nginx.com static PyObject * 229*1624Smax.romanov@nginx.com nxt_py_asgi_http_send(PyObject *self, PyObject *dict) 230*1624Smax.romanov@nginx.com { 231*1624Smax.romanov@nginx.com PyObject *type; 232*1624Smax.romanov@nginx.com const char *type_str; 233*1624Smax.romanov@nginx.com Py_ssize_t type_len; 234*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 235*1624Smax.romanov@nginx.com 236*1624Smax.romanov@nginx.com static const nxt_str_t response_start = nxt_string("http.response.start"); 237*1624Smax.romanov@nginx.com static const nxt_str_t response_body = nxt_string("http.response.body"); 238*1624Smax.romanov@nginx.com 239*1624Smax.romanov@nginx.com http = (nxt_py_asgi_http_t *) self; 240*1624Smax.romanov@nginx.com 241*1624Smax.romanov@nginx.com type = PyDict_GetItem(dict, nxt_py_type_str); 242*1624Smax.romanov@nginx.com if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) { 243*1624Smax.romanov@nginx.com nxt_unit_req_error(http->req, "asgi_http_send: " 244*1624Smax.romanov@nginx.com "'type' is not a unicode string"); 245*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'type' is not a unicode string"); 246*1624Smax.romanov@nginx.com } 247*1624Smax.romanov@nginx.com 248*1624Smax.romanov@nginx.com type_str = PyUnicode_AsUTF8AndSize(type, &type_len); 249*1624Smax.romanov@nginx.com 250*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_send type is '%.*s'", 251*1624Smax.romanov@nginx.com (int) type_len, type_str); 252*1624Smax.romanov@nginx.com 253*1624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) response_start.length 254*1624Smax.romanov@nginx.com && memcmp(type_str, response_start.start, type_len) == 0) 255*1624Smax.romanov@nginx.com { 256*1624Smax.romanov@nginx.com return nxt_py_asgi_http_response_start(http, dict); 257*1624Smax.romanov@nginx.com } 258*1624Smax.romanov@nginx.com 259*1624Smax.romanov@nginx.com if (type_len == (Py_ssize_t) response_body.length 260*1624Smax.romanov@nginx.com && memcmp(type_str, response_body.start, type_len) == 0) 261*1624Smax.romanov@nginx.com { 262*1624Smax.romanov@nginx.com return nxt_py_asgi_http_response_body(http, dict); 263*1624Smax.romanov@nginx.com } 264*1624Smax.romanov@nginx.com 265*1624Smax.romanov@nginx.com nxt_unit_req_error(http->req, "asgi_http_send: unexpected 'type': '%.*s'", 266*1624Smax.romanov@nginx.com (int) type_len, type_str); 267*1624Smax.romanov@nginx.com 268*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_AssertionError, "unexpected 'type': '%U'", type); 269*1624Smax.romanov@nginx.com } 270*1624Smax.romanov@nginx.com 271*1624Smax.romanov@nginx.com 272*1624Smax.romanov@nginx.com static PyObject * 273*1624Smax.romanov@nginx.com nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict) 274*1624Smax.romanov@nginx.com { 275*1624Smax.romanov@nginx.com int rc; 276*1624Smax.romanov@nginx.com PyObject *status, *headers, *res; 277*1624Smax.romanov@nginx.com nxt_py_asgi_calc_size_ctx_t calc_size_ctx; 278*1624Smax.romanov@nginx.com nxt_py_asgi_add_field_ctx_t add_field_ctx; 279*1624Smax.romanov@nginx.com 280*1624Smax.romanov@nginx.com status = PyDict_GetItem(dict, nxt_py_status_str); 281*1624Smax.romanov@nginx.com if (nxt_slow_path(status == NULL || !PyLong_Check(status))) { 282*1624Smax.romanov@nginx.com nxt_unit_req_error(http->req, "asgi_http_response_start: " 283*1624Smax.romanov@nginx.com "'status' is not an integer"); 284*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'status' is not an integer"); 285*1624Smax.romanov@nginx.com } 286*1624Smax.romanov@nginx.com 287*1624Smax.romanov@nginx.com calc_size_ctx.fields_size = 0; 288*1624Smax.romanov@nginx.com calc_size_ctx.fields_count = 0; 289*1624Smax.romanov@nginx.com 290*1624Smax.romanov@nginx.com headers = PyDict_GetItem(dict, nxt_py_headers_str); 291*1624Smax.romanov@nginx.com if (headers != NULL) { 292*1624Smax.romanov@nginx.com res = nxt_py_asgi_enum_headers(headers, nxt_py_asgi_calc_size, 293*1624Smax.romanov@nginx.com &calc_size_ctx); 294*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 295*1624Smax.romanov@nginx.com return NULL; 296*1624Smax.romanov@nginx.com } 297*1624Smax.romanov@nginx.com 298*1624Smax.romanov@nginx.com Py_DECREF(res); 299*1624Smax.romanov@nginx.com } 300*1624Smax.romanov@nginx.com 301*1624Smax.romanov@nginx.com rc = nxt_unit_response_init(http->req, PyLong_AsLong(status), 302*1624Smax.romanov@nginx.com calc_size_ctx.fields_count, 303*1624Smax.romanov@nginx.com calc_size_ctx.fields_size); 304*1624Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 305*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 306*1624Smax.romanov@nginx.com "failed to allocate response object"); 307*1624Smax.romanov@nginx.com } 308*1624Smax.romanov@nginx.com 309*1624Smax.romanov@nginx.com add_field_ctx.req = http->req; 310*1624Smax.romanov@nginx.com add_field_ctx.content_length = -1; 311*1624Smax.romanov@nginx.com 312*1624Smax.romanov@nginx.com if (headers != NULL) { 313*1624Smax.romanov@nginx.com res = nxt_py_asgi_enum_headers(headers, nxt_py_asgi_add_field, 314*1624Smax.romanov@nginx.com &add_field_ctx); 315*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 316*1624Smax.romanov@nginx.com return NULL; 317*1624Smax.romanov@nginx.com } 318*1624Smax.romanov@nginx.com 319*1624Smax.romanov@nginx.com Py_DECREF(res); 320*1624Smax.romanov@nginx.com } 321*1624Smax.romanov@nginx.com 322*1624Smax.romanov@nginx.com http->content_length = add_field_ctx.content_length; 323*1624Smax.romanov@nginx.com 324*1624Smax.romanov@nginx.com Py_INCREF(http); 325*1624Smax.romanov@nginx.com return (PyObject *) http; 326*1624Smax.romanov@nginx.com } 327*1624Smax.romanov@nginx.com 328*1624Smax.romanov@nginx.com 329*1624Smax.romanov@nginx.com static PyObject * 330*1624Smax.romanov@nginx.com nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) 331*1624Smax.romanov@nginx.com { 332*1624Smax.romanov@nginx.com int rc; 333*1624Smax.romanov@nginx.com char *body_str; 334*1624Smax.romanov@nginx.com ssize_t sent; 335*1624Smax.romanov@nginx.com PyObject *body, *more_body, *future; 336*1624Smax.romanov@nginx.com Py_ssize_t body_len, body_off; 337*1624Smax.romanov@nginx.com 338*1624Smax.romanov@nginx.com body = PyDict_GetItem(dict, nxt_py_body_str); 339*1624Smax.romanov@nginx.com if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) { 340*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'body' is not a byte string"); 341*1624Smax.romanov@nginx.com } 342*1624Smax.romanov@nginx.com 343*1624Smax.romanov@nginx.com more_body = PyDict_GetItem(dict, nxt_py_more_body_str); 344*1624Smax.romanov@nginx.com if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) { 345*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool"); 346*1624Smax.romanov@nginx.com } 347*1624Smax.romanov@nginx.com 348*1624Smax.romanov@nginx.com if (nxt_slow_path(http->complete)) { 349*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 350*1624Smax.romanov@nginx.com "Unexpected ASGI message 'http.response.body' " 351*1624Smax.romanov@nginx.com "sent, after response already completed"); 352*1624Smax.romanov@nginx.com } 353*1624Smax.romanov@nginx.com 354*1624Smax.romanov@nginx.com if (nxt_slow_path(http->send_future != NULL)) { 355*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, "Concurrent send"); 356*1624Smax.romanov@nginx.com } 357*1624Smax.romanov@nginx.com 358*1624Smax.romanov@nginx.com if (body != NULL) { 359*1624Smax.romanov@nginx.com body_str = PyBytes_AS_STRING(body); 360*1624Smax.romanov@nginx.com body_len = PyBytes_GET_SIZE(body); 361*1624Smax.romanov@nginx.com 362*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_response_body: %d, %d", 363*1624Smax.romanov@nginx.com (int) body_len, (more_body == Py_True) ); 364*1624Smax.romanov@nginx.com 365*1624Smax.romanov@nginx.com if (nxt_slow_path(http->bytes_sent + body_len 366*1624Smax.romanov@nginx.com > http->content_length)) 367*1624Smax.romanov@nginx.com { 368*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 369*1624Smax.romanov@nginx.com "Response content longer than Content-Length"); 370*1624Smax.romanov@nginx.com } 371*1624Smax.romanov@nginx.com 372*1624Smax.romanov@nginx.com body_off = 0; 373*1624Smax.romanov@nginx.com 374*1624Smax.romanov@nginx.com while (body_len > 0) { 375*1624Smax.romanov@nginx.com sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0); 376*1624Smax.romanov@nginx.com if (nxt_slow_path(sent < 0)) { 377*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, "failed to send body"); 378*1624Smax.romanov@nginx.com } 379*1624Smax.romanov@nginx.com 380*1624Smax.romanov@nginx.com if (nxt_slow_path(sent == 0)) { 381*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_response_body: " 382*1624Smax.romanov@nginx.com "out of shared memory, %d", 383*1624Smax.romanov@nginx.com (int) body_len); 384*1624Smax.romanov@nginx.com 385*1624Smax.romanov@nginx.com future = PyObject_CallObject(nxt_py_loop_create_future, NULL); 386*1624Smax.romanov@nginx.com if (nxt_slow_path(future == NULL)) { 387*1624Smax.romanov@nginx.com nxt_unit_req_alert(http->req, 388*1624Smax.romanov@nginx.com "Python failed to create Future object"); 389*1624Smax.romanov@nginx.com nxt_python_print_exception(); 390*1624Smax.romanov@nginx.com 391*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 392*1624Smax.romanov@nginx.com "failed to create Future object"); 393*1624Smax.romanov@nginx.com } 394*1624Smax.romanov@nginx.com 395*1624Smax.romanov@nginx.com http->send_body = body; 396*1624Smax.romanov@nginx.com Py_INCREF(http->send_body); 397*1624Smax.romanov@nginx.com http->send_body_off = body_off; 398*1624Smax.romanov@nginx.com 399*1624Smax.romanov@nginx.com nxt_queue_insert_tail(&nxt_py_asgi_drain_queue, &http->link); 400*1624Smax.romanov@nginx.com 401*1624Smax.romanov@nginx.com http->send_future = future; 402*1624Smax.romanov@nginx.com Py_INCREF(http->send_future); 403*1624Smax.romanov@nginx.com 404*1624Smax.romanov@nginx.com return future; 405*1624Smax.romanov@nginx.com } 406*1624Smax.romanov@nginx.com 407*1624Smax.romanov@nginx.com body_str += sent; 408*1624Smax.romanov@nginx.com body_len -= sent; 409*1624Smax.romanov@nginx.com body_off += sent; 410*1624Smax.romanov@nginx.com http->bytes_sent += sent; 411*1624Smax.romanov@nginx.com } 412*1624Smax.romanov@nginx.com 413*1624Smax.romanov@nginx.com } else { 414*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_response_body: 0, %d", 415*1624Smax.romanov@nginx.com (more_body == Py_True) ); 416*1624Smax.romanov@nginx.com 417*1624Smax.romanov@nginx.com if (!nxt_unit_response_is_sent(http->req)) { 418*1624Smax.romanov@nginx.com rc = nxt_unit_response_send(http->req); 419*1624Smax.romanov@nginx.com if (nxt_slow_path(rc != NXT_UNIT_OK)) { 420*1624Smax.romanov@nginx.com return PyErr_Format(PyExc_RuntimeError, 421*1624Smax.romanov@nginx.com "failed to send response"); 422*1624Smax.romanov@nginx.com } 423*1624Smax.romanov@nginx.com } 424*1624Smax.romanov@nginx.com } 425*1624Smax.romanov@nginx.com 426*1624Smax.romanov@nginx.com if (more_body == NULL || more_body == Py_False) { 427*1624Smax.romanov@nginx.com http->complete = 1; 428*1624Smax.romanov@nginx.com } 429*1624Smax.romanov@nginx.com 430*1624Smax.romanov@nginx.com Py_INCREF(http); 431*1624Smax.romanov@nginx.com return (PyObject *) http; 432*1624Smax.romanov@nginx.com } 433*1624Smax.romanov@nginx.com 434*1624Smax.romanov@nginx.com 435*1624Smax.romanov@nginx.com void 436*1624Smax.romanov@nginx.com nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req) 437*1624Smax.romanov@nginx.com { 438*1624Smax.romanov@nginx.com PyObject *msg, *future, *res; 439*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 440*1624Smax.romanov@nginx.com 441*1624Smax.romanov@nginx.com http = req->data; 442*1624Smax.romanov@nginx.com 443*1624Smax.romanov@nginx.com nxt_unit_req_debug(req, "asgi_http_data_handler"); 444*1624Smax.romanov@nginx.com 445*1624Smax.romanov@nginx.com if (http->receive_future == NULL) { 446*1624Smax.romanov@nginx.com return; 447*1624Smax.romanov@nginx.com } 448*1624Smax.romanov@nginx.com 449*1624Smax.romanov@nginx.com msg = nxt_py_asgi_http_read_msg(http); 450*1624Smax.romanov@nginx.com if (nxt_slow_path(msg == NULL)) { 451*1624Smax.romanov@nginx.com return; 452*1624Smax.romanov@nginx.com } 453*1624Smax.romanov@nginx.com 454*1624Smax.romanov@nginx.com if (msg == Py_None) { 455*1624Smax.romanov@nginx.com Py_DECREF(msg); 456*1624Smax.romanov@nginx.com return; 457*1624Smax.romanov@nginx.com } 458*1624Smax.romanov@nginx.com 459*1624Smax.romanov@nginx.com future = http->receive_future; 460*1624Smax.romanov@nginx.com http->receive_future = NULL; 461*1624Smax.romanov@nginx.com 462*1624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, msg, NULL); 463*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 464*1624Smax.romanov@nginx.com nxt_unit_req_alert(req, "'set_result' call failed"); 465*1624Smax.romanov@nginx.com nxt_python_print_exception(); 466*1624Smax.romanov@nginx.com } 467*1624Smax.romanov@nginx.com 468*1624Smax.romanov@nginx.com Py_XDECREF(res); 469*1624Smax.romanov@nginx.com Py_DECREF(future); 470*1624Smax.romanov@nginx.com 471*1624Smax.romanov@nginx.com Py_DECREF(msg); 472*1624Smax.romanov@nginx.com } 473*1624Smax.romanov@nginx.com 474*1624Smax.romanov@nginx.com 475*1624Smax.romanov@nginx.com int 476*1624Smax.romanov@nginx.com nxt_py_asgi_http_drain(nxt_queue_link_t *lnk) 477*1624Smax.romanov@nginx.com { 478*1624Smax.romanov@nginx.com char *body_str; 479*1624Smax.romanov@nginx.com ssize_t sent; 480*1624Smax.romanov@nginx.com PyObject *future, *exc, *res; 481*1624Smax.romanov@nginx.com Py_ssize_t body_len; 482*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 483*1624Smax.romanov@nginx.com 484*1624Smax.romanov@nginx.com http = nxt_container_of(lnk, nxt_py_asgi_http_t, link); 485*1624Smax.romanov@nginx.com 486*1624Smax.romanov@nginx.com body_str = PyBytes_AS_STRING(http->send_body) + http->send_body_off; 487*1624Smax.romanov@nginx.com body_len = PyBytes_GET_SIZE(http->send_body) - http->send_body_off; 488*1624Smax.romanov@nginx.com 489*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_drain: %d", (int) body_len); 490*1624Smax.romanov@nginx.com 491*1624Smax.romanov@nginx.com while (body_len > 0) { 492*1624Smax.romanov@nginx.com sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0); 493*1624Smax.romanov@nginx.com if (nxt_slow_path(sent < 0)) { 494*1624Smax.romanov@nginx.com goto fail; 495*1624Smax.romanov@nginx.com } 496*1624Smax.romanov@nginx.com 497*1624Smax.romanov@nginx.com if (nxt_slow_path(sent == 0)) { 498*1624Smax.romanov@nginx.com return NXT_UNIT_AGAIN; 499*1624Smax.romanov@nginx.com } 500*1624Smax.romanov@nginx.com 501*1624Smax.romanov@nginx.com body_str += sent; 502*1624Smax.romanov@nginx.com body_len -= sent; 503*1624Smax.romanov@nginx.com 504*1624Smax.romanov@nginx.com http->send_body_off += sent; 505*1624Smax.romanov@nginx.com http->bytes_sent += sent; 506*1624Smax.romanov@nginx.com } 507*1624Smax.romanov@nginx.com 508*1624Smax.romanov@nginx.com Py_CLEAR(http->send_body); 509*1624Smax.romanov@nginx.com 510*1624Smax.romanov@nginx.com future = http->send_future; 511*1624Smax.romanov@nginx.com http->send_future = NULL; 512*1624Smax.romanov@nginx.com 513*1624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str, Py_None, 514*1624Smax.romanov@nginx.com NULL); 515*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 516*1624Smax.romanov@nginx.com nxt_unit_req_alert(http->req, "'set_result' call failed"); 517*1624Smax.romanov@nginx.com nxt_python_print_exception(); 518*1624Smax.romanov@nginx.com } 519*1624Smax.romanov@nginx.com 520*1624Smax.romanov@nginx.com Py_XDECREF(res); 521*1624Smax.romanov@nginx.com Py_DECREF(future); 522*1624Smax.romanov@nginx.com 523*1624Smax.romanov@nginx.com return NXT_UNIT_OK; 524*1624Smax.romanov@nginx.com 525*1624Smax.romanov@nginx.com fail: 526*1624Smax.romanov@nginx.com 527*1624Smax.romanov@nginx.com exc = PyObject_CallFunctionObjArgs(PyExc_RuntimeError, 528*1624Smax.romanov@nginx.com nxt_py_failed_to_send_body_str, 529*1624Smax.romanov@nginx.com NULL); 530*1624Smax.romanov@nginx.com if (nxt_slow_path(exc == NULL)) { 531*1624Smax.romanov@nginx.com nxt_unit_req_alert(http->req, "RuntimeError create failed"); 532*1624Smax.romanov@nginx.com nxt_python_print_exception(); 533*1624Smax.romanov@nginx.com 534*1624Smax.romanov@nginx.com exc = Py_None; 535*1624Smax.romanov@nginx.com Py_INCREF(exc); 536*1624Smax.romanov@nginx.com } 537*1624Smax.romanov@nginx.com 538*1624Smax.romanov@nginx.com future = http->send_future; 539*1624Smax.romanov@nginx.com http->send_future = NULL; 540*1624Smax.romanov@nginx.com 541*1624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_set_exception_str, exc, 542*1624Smax.romanov@nginx.com NULL); 543*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 544*1624Smax.romanov@nginx.com nxt_unit_req_alert(http->req, "'set_exception' call failed"); 545*1624Smax.romanov@nginx.com nxt_python_print_exception(); 546*1624Smax.romanov@nginx.com } 547*1624Smax.romanov@nginx.com 548*1624Smax.romanov@nginx.com Py_XDECREF(res); 549*1624Smax.romanov@nginx.com Py_DECREF(future); 550*1624Smax.romanov@nginx.com Py_DECREF(exc); 551*1624Smax.romanov@nginx.com 552*1624Smax.romanov@nginx.com return NXT_UNIT_ERROR; 553*1624Smax.romanov@nginx.com } 554*1624Smax.romanov@nginx.com 555*1624Smax.romanov@nginx.com 556*1624Smax.romanov@nginx.com static PyObject * 557*1624Smax.romanov@nginx.com nxt_py_asgi_http_done(PyObject *self, PyObject *future) 558*1624Smax.romanov@nginx.com { 559*1624Smax.romanov@nginx.com int rc; 560*1624Smax.romanov@nginx.com PyObject *res; 561*1624Smax.romanov@nginx.com nxt_py_asgi_http_t *http; 562*1624Smax.romanov@nginx.com 563*1624Smax.romanov@nginx.com http = (nxt_py_asgi_http_t *) self; 564*1624Smax.romanov@nginx.com 565*1624Smax.romanov@nginx.com nxt_unit_req_debug(http->req, "asgi_http_done"); 566*1624Smax.romanov@nginx.com 567*1624Smax.romanov@nginx.com /* 568*1624Smax.romanov@nginx.com * Get Future.result() and it raises an exception, if coroutine exited 569*1624Smax.romanov@nginx.com * with exception. 570*1624Smax.romanov@nginx.com */ 571*1624Smax.romanov@nginx.com res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL); 572*1624Smax.romanov@nginx.com if (nxt_slow_path(res == NULL)) { 573*1624Smax.romanov@nginx.com nxt_unit_req_error(http->req, 574*1624Smax.romanov@nginx.com "Python failed to call 'future.result()'"); 575*1624Smax.romanov@nginx.com nxt_python_print_exception(); 576*1624Smax.romanov@nginx.com 577*1624Smax.romanov@nginx.com rc = NXT_UNIT_ERROR; 578*1624Smax.romanov@nginx.com 579*1624Smax.romanov@nginx.com } else { 580*1624Smax.romanov@nginx.com Py_DECREF(res); 581*1624Smax.romanov@nginx.com 582*1624Smax.romanov@nginx.com rc = NXT_UNIT_OK; 583*1624Smax.romanov@nginx.com } 584*1624Smax.romanov@nginx.com 585*1624Smax.romanov@nginx.com nxt_unit_request_done(http->req, rc); 586*1624Smax.romanov@nginx.com 587*1624Smax.romanov@nginx.com Py_RETURN_NONE; 588*1624Smax.romanov@nginx.com } 589*1624Smax.romanov@nginx.com 590*1624Smax.romanov@nginx.com 591*1624Smax.romanov@nginx.com #endif /* NXT_HAVE_ASGI */ 592