1 2 /* 3 * Copyright (C) NGINX, Inc. 4 */ 5 6 7 #include <python/nxt_python.h> 8 9 #if (NXT_HAVE_ASGI) 10 11 #include <nxt_main.h> 12 #include <nxt_unit.h> 13 #include <nxt_unit_request.h> 14 #include <nxt_unit_response.h> 15 #include <python/nxt_python_asgi.h> 16 #include <python/nxt_python_asgi_str.h> 17 18 19 static PyObject *nxt_python_asgi_get_func(PyObject *obj); 20 static int nxt_python_asgi_ctx_data_alloc(void **pdata, int main); 21 static void nxt_python_asgi_ctx_data_free(void *data); 22 static int nxt_python_asgi_startup(void *data); 23 static int nxt_python_asgi_run(nxt_unit_ctx_t *ctx); 24 25 static void nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, 26 nxt_unit_port_t *port); 27 static void nxt_py_asgi_request_handler(nxt_unit_request_info_t *req); 28 static void nxt_py_asgi_close_handler(nxt_unit_request_info_t *req); 29 30 static PyObject *nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req); 31 static PyObject *nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, 32 uint16_t port); 33 static PyObject *nxt_py_asgi_create_header(nxt_unit_field_t *f); 34 static PyObject *nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f); 35 36 static int nxt_python_asgi_ready(nxt_unit_ctx_t *ctx); 37 38 static int nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); 39 static void nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port); 40 static void nxt_py_asgi_quit(nxt_unit_ctx_t *ctx); 41 static void nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx); 42 43 static PyObject *nxt_py_asgi_port_read(PyObject *self, PyObject *args); 44 static void nxt_python_asgi_done(void); 45 46 static PyObject *nxt_py_port_read; 47 static nxt_unit_port_t *nxt_py_shared_port; 48 49 static PyMethodDef nxt_py_port_read_method = 50 {"unit_port_read", nxt_py_asgi_port_read, METH_VARARGS, ""}; 51 52 static nxt_python_proto_t nxt_py_asgi_proto = { 53 .ctx_data_alloc = nxt_python_asgi_ctx_data_alloc, 54 .ctx_data_free = nxt_python_asgi_ctx_data_free, 55 .startup = nxt_python_asgi_startup, 56 .run = nxt_python_asgi_run, 57 .ready = nxt_python_asgi_ready, 58 .done = nxt_python_asgi_done, 59 }; 60 61 #define NXT_UNIT_HASH_WS_PROTOCOL 0xED0A 62 63 64 int 65 nxt_python_asgi_check(PyObject *obj) 66 { 67 int res; 68 PyObject *func; 69 PyCodeObject *code; 70 71 func = nxt_python_asgi_get_func(obj); 72 73 if (func == NULL) { 74 return 0; 75 } 76 77 code = (PyCodeObject *) PyFunction_GET_CODE(func); 78 79 nxt_unit_debug(NULL, "asgi_check: callable is %sa coroutine function with " 80 "%d argument(s)", 81 (code->co_flags & CO_COROUTINE) != 0 ? "" : "not ", 82 code->co_argcount); 83 84 res = (code->co_flags & CO_COROUTINE) != 0 || code->co_argcount == 1; 85 86 Py_DECREF(func); 87 88 return res; 89 } 90 91 92 static PyObject * 93 nxt_python_asgi_get_func(PyObject *obj) 94 { 95 PyObject *call; 96 97 if (PyFunction_Check(obj)) { 98 Py_INCREF(obj); 99 return obj; 100 } 101 102 if (PyMethod_Check(obj)) { 103 obj = PyMethod_GET_FUNCTION(obj); 104 105 Py_INCREF(obj); 106 return obj; 107 } 108 109 call = PyObject_GetAttrString(obj, "__call__"); 110 111 if (call == NULL) { 112 return NULL; 113 } 114 115 if (PyFunction_Check(call)) { 116 return call; 117 } 118 119 if (PyMethod_Check(call)) { 120 obj = PyMethod_GET_FUNCTION(call); 121 122 Py_INCREF(obj); 123 Py_DECREF(call); 124 125 return obj; 126 } 127 128 Py_DECREF(call); 129 130 return NULL; 131 } 132 133 134 int 135 nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 136 { 137 PyObject *func; 138 nxt_int_t i; 139 PyCodeObject *code; 140 141 nxt_unit_debug(NULL, "asgi_init"); 142 143 if (nxt_slow_path(nxt_py_asgi_str_init() != NXT_UNIT_OK)) { 144 nxt_unit_alert(NULL, "Python failed to init string objects"); 145 return NXT_UNIT_ERROR; 146 } 147 148 nxt_py_port_read = PyCFunction_New(&nxt_py_port_read_method, NULL); 149 if (nxt_slow_path(nxt_py_port_read == NULL)) { 150 nxt_unit_alert(NULL, 151 "Python failed to initialize the 'port_read' function"); 152 return NXT_UNIT_ERROR; 153 } 154 155 if (nxt_slow_path(nxt_py_asgi_http_init() == NXT_UNIT_ERROR)) { 156 return NXT_UNIT_ERROR; 157 } 158 159 if (nxt_slow_path(nxt_py_asgi_websocket_init() == NXT_UNIT_ERROR)) { 160 return NXT_UNIT_ERROR; 161 } 162 163 for (i = 0; i < nxt_py_targets->count; i++) { 164 func = nxt_python_asgi_get_func(nxt_py_targets->target[i].application); 165 if (nxt_slow_path(func == NULL)) { 166 nxt_unit_alert(NULL, "Python cannot find function for callable"); 167 return NXT_UNIT_ERROR; 168 } 169 170 code = (PyCodeObject *) PyFunction_GET_CODE(func); 171 172 if ((code->co_flags & CO_COROUTINE) == 0) { 173 nxt_unit_debug(NULL, "asgi: callable is not a coroutine function " 174 "switching to legacy mode"); 175 nxt_py_targets->target[i].asgi_legacy = 1; 176 } 177 178 Py_DECREF(func); 179 } 180 181 init->callbacks.request_handler = nxt_py_asgi_request_handler; 182 init->callbacks.data_handler = nxt_py_asgi_http_data_handler; 183 init->callbacks.websocket_handler = nxt_py_asgi_websocket_handler; 184 init->callbacks.close_handler = nxt_py_asgi_close_handler; 185 init->callbacks.quit = nxt_py_asgi_quit; 186 init->callbacks.shm_ack_handler = nxt_py_asgi_shm_ack_handler; 187 init->callbacks.add_port = nxt_py_asgi_add_port; 188 init->callbacks.remove_port = nxt_py_asgi_remove_port; 189 190 *proto = nxt_py_asgi_proto; 191 192 return NXT_UNIT_OK; 193 } 194 195 196 static int 197 nxt_python_asgi_ctx_data_alloc(void **pdata, int main) 198 { 199 uint32_t i; 200 PyObject *asyncio, *loop, *event_loop, *obj; 201 const char *event_loop_func; 202 nxt_py_asgi_ctx_data_t *ctx_data; 203 204 ctx_data = nxt_unit_malloc(NULL, sizeof(nxt_py_asgi_ctx_data_t)); 205 if (nxt_slow_path(ctx_data == NULL)) { 206 nxt_unit_alert(NULL, "Failed to allocate context data"); 207 return NXT_UNIT_ERROR; 208 } 209 210 memset(ctx_data, 0, sizeof(nxt_py_asgi_ctx_data_t)); 211 212 nxt_queue_init(&ctx_data->drain_queue); 213 214 struct { 215 const char *key; 216 PyObject **handler; 217 218 } handlers[] = { 219 { "create_task", &ctx_data->loop_create_task }, 220 { "add_reader", &ctx_data->loop_add_reader }, 221 { "remove_reader", &ctx_data->loop_remove_reader }, 222 { "call_soon", &ctx_data->loop_call_soon }, 223 { "run_until_complete", &ctx_data->loop_run_until_complete }, 224 { "create_future", &ctx_data->loop_create_future }, 225 }; 226 227 loop = NULL; 228 229 asyncio = PyImport_ImportModule("asyncio"); 230 if (nxt_slow_path(asyncio == NULL)) { 231 nxt_unit_alert(NULL, "Python failed to import module 'asyncio'"); 232 nxt_python_print_exception(); 233 goto fail; 234 } 235 236 event_loop_func = main ? "get_event_loop" : "new_event_loop"; 237 238 event_loop = PyDict_GetItemString(PyModule_GetDict(asyncio), 239 event_loop_func); 240 if (nxt_slow_path(event_loop == NULL)) { 241 nxt_unit_alert(NULL, 242 "Python failed to get '%s' from module 'asyncio'", 243 event_loop_func); 244 goto fail; 245 } 246 247 if (nxt_slow_path(PyCallable_Check(event_loop) == 0)) { 248 nxt_unit_alert(NULL, 249 "'asyncio.%s' is not a callable object", 250 event_loop_func); 251 goto fail; 252 } 253 254 loop = PyObject_CallObject(event_loop, NULL); 255 if (nxt_slow_path(loop == NULL)) { 256 nxt_unit_alert(NULL, "Python failed to call 'asyncio.%s'", 257 event_loop_func); 258 goto fail; 259 } 260 261 for (i = 0; i < nxt_nitems(handlers); i++) { 262 obj = PyObject_GetAttrString(loop, handlers[i].key); 263 if (nxt_slow_path(obj == NULL)) { 264 nxt_unit_alert(NULL, "Python failed to get 'loop.%s'", 265 handlers[i].key); 266 goto fail; 267 } 268 269 *handlers[i].handler = obj; 270 271 if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 272 nxt_unit_alert(NULL, "'loop.%s' is not a callable object", 273 handlers[i].key); 274 goto fail; 275 } 276 } 277 278 obj = PyObject_CallObject(ctx_data->loop_create_future, NULL); 279 if (nxt_slow_path(obj == NULL)) { 280 nxt_unit_alert(NULL, "Python failed to create Future "); 281 nxt_python_print_exception(); 282 goto fail; 283 } 284 285 ctx_data->quit_future = obj; 286 287 obj = PyObject_GetAttrString(ctx_data->quit_future, "set_result"); 288 if (nxt_slow_path(obj == NULL)) { 289 nxt_unit_alert(NULL, "Python failed to get 'future.set_result'"); 290 goto fail; 291 } 292 293 ctx_data->quit_future_set_result = obj; 294 295 if (nxt_slow_path(PyCallable_Check(obj) == 0)) { 296 nxt_unit_alert(NULL, "'future.set_result' is not a callable object"); 297 goto fail; 298 } 299 300 Py_DECREF(loop); 301 Py_DECREF(asyncio); 302 303 *pdata = ctx_data; 304 305 return NXT_UNIT_OK; 306 307 fail: 308 309 nxt_python_asgi_ctx_data_free(ctx_data); 310 311 Py_XDECREF(loop); 312 Py_XDECREF(asyncio); 313 314 return NXT_UNIT_ERROR; 315 } 316 317 318 static void 319 nxt_python_asgi_ctx_data_free(void *data) 320 { 321 nxt_py_asgi_ctx_data_t *ctx_data; 322 323 ctx_data = data; 324 325 Py_XDECREF(ctx_data->loop_run_until_complete); 326 Py_XDECREF(ctx_data->loop_create_future); 327 Py_XDECREF(ctx_data->loop_create_task); 328 Py_XDECREF(ctx_data->loop_call_soon); 329 Py_XDECREF(ctx_data->loop_add_reader); 330 Py_XDECREF(ctx_data->loop_remove_reader); 331 Py_XDECREF(ctx_data->quit_future); 332 Py_XDECREF(ctx_data->quit_future_set_result); 333 334 nxt_unit_free(NULL, ctx_data); 335 } 336 337 338 static int 339 nxt_python_asgi_startup(void *data) 340 { 341 return nxt_py_asgi_lifespan_startup(data); 342 } 343 344 345 static int 346 nxt_python_asgi_run(nxt_unit_ctx_t *ctx) 347 { 348 PyObject *res; 349 nxt_py_asgi_ctx_data_t *ctx_data; 350 351 ctx_data = ctx->data; 352 353 res = PyObject_CallFunctionObjArgs(ctx_data->loop_run_until_complete, 354 ctx_data->quit_future, NULL); 355 if (nxt_slow_path(res == NULL)) { 356 nxt_unit_alert(ctx, "Python failed to call loop.run_until_complete"); 357 nxt_python_print_exception(); 358 359 return NXT_UNIT_ERROR; 360 } 361 362 Py_DECREF(res); 363 364 nxt_py_asgi_remove_reader(ctx, nxt_py_shared_port); 365 nxt_py_asgi_remove_reader(ctx, ctx_data->port); 366 367 if (ctx_data->port != NULL) { 368 ctx_data->port->data = NULL; 369 ctx_data->port = NULL; 370 } 371 372 nxt_py_asgi_lifespan_shutdown(ctx); 373 374 return NXT_UNIT_OK; 375 } 376 377 378 static void 379 nxt_py_asgi_remove_reader(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 380 { 381 PyObject *res, *fd; 382 nxt_py_asgi_ctx_data_t *ctx_data; 383 384 if (port == NULL || port->in_fd == -1) { 385 return; 386 } 387 388 ctx_data = ctx->data; 389 390 nxt_unit_debug(ctx, "asgi_remove_reader %d %p", port->in_fd, port); 391 392 fd = PyLong_FromLong(port->in_fd); 393 if (nxt_slow_path(fd == NULL)) { 394 nxt_unit_alert(ctx, "Python failed to create Long object"); 395 nxt_python_print_exception(); 396 397 return; 398 } 399 400 res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, fd, NULL); 401 if (nxt_slow_path(res == NULL)) { 402 nxt_unit_alert(ctx, "Python failed to remove_reader"); 403 nxt_python_print_exception(); 404 405 } else { 406 Py_DECREF(res); 407 } 408 409 Py_DECREF(fd); 410 } 411 412 413 static void 414 nxt_py_asgi_request_handler(nxt_unit_request_info_t *req) 415 { 416 PyObject *scope, *res, *task, *receive, *send, *done, *asgi; 417 PyObject *stage2; 418 nxt_python_target_t *target; 419 nxt_py_asgi_ctx_data_t *ctx_data; 420 421 if (req->request->websocket_handshake) { 422 asgi = nxt_py_asgi_websocket_create(req); 423 424 } else { 425 asgi = nxt_py_asgi_http_create(req); 426 } 427 428 if (nxt_slow_path(asgi == NULL)) { 429 nxt_unit_req_alert(req, "Python failed to create asgi object"); 430 nxt_unit_request_done(req, NXT_UNIT_ERROR); 431 432 return; 433 } 434 435 receive = PyObject_GetAttrString(asgi, "receive"); 436 if (nxt_slow_path(receive == NULL)) { 437 nxt_unit_req_alert(req, "Python failed to get 'receive' method"); 438 nxt_unit_request_done(req, NXT_UNIT_ERROR); 439 440 goto release_asgi; 441 } 442 443 send = PyObject_GetAttrString(asgi, "send"); 444 if (nxt_slow_path(receive == NULL)) { 445 nxt_unit_req_alert(req, "Python failed to get 'send' method"); 446 nxt_unit_request_done(req, NXT_UNIT_ERROR); 447 448 goto release_receive; 449 } 450 451 done = PyObject_GetAttrString(asgi, "_done"); 452 if (nxt_slow_path(receive == NULL)) { 453 nxt_unit_req_alert(req, "Python failed to get '_done' method"); 454 nxt_unit_request_done(req, NXT_UNIT_ERROR); 455 456 goto release_send; 457 } 458 459 scope = nxt_py_asgi_create_http_scope(req); 460 if (nxt_slow_path(scope == NULL)) { 461 nxt_unit_request_done(req, NXT_UNIT_ERROR); 462 463 goto release_done; 464 } 465 466 req->data = asgi; 467 target = &nxt_py_targets->target[req->request->app_target]; 468 469 if (!target->asgi_legacy) { 470 nxt_unit_req_debug(req, "Python call ASGI 3.0 application"); 471 472 res = PyObject_CallFunctionObjArgs(target->application, 473 scope, receive, send, NULL); 474 475 } else { 476 nxt_unit_req_debug(req, "Python call legacy application"); 477 478 res = PyObject_CallFunctionObjArgs(target->application, scope, NULL); 479 480 if (nxt_slow_path(res == NULL)) { 481 nxt_unit_req_error(req, "Python failed to call legacy app stage1"); 482 nxt_python_print_exception(); 483 nxt_unit_request_done(req, NXT_UNIT_ERROR); 484 485 goto release_scope; 486 } 487 488 if (nxt_slow_path(PyCallable_Check(res) == 0)) { 489 nxt_unit_req_error(req, 490 "Legacy ASGI application returns not a callable"); 491 nxt_unit_request_done(req, NXT_UNIT_ERROR); 492 493 Py_DECREF(res); 494 495 goto release_scope; 496 } 497 498 stage2 = res; 499 500 res = PyObject_CallFunctionObjArgs(stage2, receive, send, NULL); 501 502 Py_DECREF(stage2); 503 } 504 505 if (nxt_slow_path(res == NULL)) { 506 nxt_unit_req_error(req, "Python failed to call the application"); 507 nxt_python_print_exception(); 508 nxt_unit_request_done(req, NXT_UNIT_ERROR); 509 510 goto release_scope; 511 } 512 513 if (nxt_slow_path(!PyCoro_CheckExact(res))) { 514 nxt_unit_req_error(req, "Application result type is not a coroutine"); 515 nxt_unit_request_done(req, NXT_UNIT_ERROR); 516 517 Py_DECREF(res); 518 519 goto release_scope; 520 } 521 522 ctx_data = req->ctx->data; 523 524 task = PyObject_CallFunctionObjArgs(ctx_data->loop_create_task, res, NULL); 525 if (nxt_slow_path(task == NULL)) { 526 nxt_unit_req_error(req, "Python failed to call the create_task"); 527 nxt_python_print_exception(); 528 nxt_unit_request_done(req, NXT_UNIT_ERROR); 529 530 Py_DECREF(res); 531 532 goto release_scope; 533 } 534 535 Py_DECREF(res); 536 537 res = PyObject_CallMethodObjArgs(task, nxt_py_add_done_callback_str, done, 538 NULL); 539 if (nxt_slow_path(res == NULL)) { 540 nxt_unit_req_error(req, 541 "Python failed to call 'task.add_done_callback'"); 542 nxt_python_print_exception(); 543 nxt_unit_request_done(req, NXT_UNIT_ERROR); 544 545 goto release_task; 546 } 547 548 Py_DECREF(res); 549 release_task: 550 Py_DECREF(task); 551 release_scope: 552 Py_DECREF(scope); 553 release_done: 554 Py_DECREF(done); 555 release_send: 556 Py_DECREF(send); 557 release_receive: 558 Py_DECREF(receive); 559 release_asgi: 560 Py_DECREF(asgi); 561 } 562 563 564 static void 565 nxt_py_asgi_close_handler(nxt_unit_request_info_t *req) 566 { 567 if (req->request->websocket_handshake) { 568 nxt_py_asgi_websocket_close_handler(req); 569 570 } else { 571 nxt_py_asgi_http_close_handler(req); 572 } 573 } 574 575 576 static PyObject * 577 nxt_py_asgi_create_http_scope(nxt_unit_request_info_t *req) 578 { 579 char *p, *target, *query; 580 uint32_t target_length, i; 581 PyObject *scope, *v, *type, *scheme; 582 PyObject *headers, *header; 583 nxt_unit_field_t *f; 584 nxt_unit_request_t *r; 585 586 static const nxt_str_t ws_protocol = nxt_string("sec-websocket-protocol"); 587 588 #define SET_ITEM(dict, key, value) \ 589 if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \ 590 == -1)) \ 591 { \ 592 nxt_unit_req_alert(req, "Python failed to set '" \ 593 #dict "." #key "' item"); \ 594 goto fail; \ 595 } 596 597 v = NULL; 598 headers = NULL; 599 600 r = req->request; 601 602 if (r->websocket_handshake) { 603 type = nxt_py_websocket_str; 604 scheme = r->tls ? nxt_py_wss_str : nxt_py_ws_str; 605 606 } else { 607 type = nxt_py_http_str; 608 scheme = r->tls ? nxt_py_https_str : nxt_py_http_str; 609 } 610 611 scope = nxt_py_asgi_new_scope(req, type, nxt_py_2_1_str); 612 if (nxt_slow_path(scope == NULL)) { 613 return NULL; 614 } 615 616 p = nxt_unit_sptr_get(&r->version); 617 SET_ITEM(scope, http_version, p[7] == '1' ? nxt_py_1_1_str 618 : nxt_py_1_0_str) 619 SET_ITEM(scope, scheme, scheme) 620 621 v = PyString_FromStringAndSize(nxt_unit_sptr_get(&r->method), 622 r->method_length); 623 if (nxt_slow_path(v == NULL)) { 624 nxt_unit_req_alert(req, "Python failed to create 'method' string"); 625 goto fail; 626 } 627 628 SET_ITEM(scope, method, v) 629 Py_DECREF(v); 630 631 v = PyUnicode_DecodeUTF8(nxt_unit_sptr_get(&r->path), r->path_length, 632 "replace"); 633 if (nxt_slow_path(v == NULL)) { 634 nxt_unit_req_alert(req, "Python failed to create 'path' string"); 635 goto fail; 636 } 637 638 SET_ITEM(scope, path, v) 639 Py_DECREF(v); 640 641 target = nxt_unit_sptr_get(&r->target); 642 query = nxt_unit_sptr_get(&r->query); 643 644 if (r->query.offset != 0) { 645 target_length = query - target - 1; 646 647 } else { 648 target_length = r->target_length; 649 } 650 651 v = PyBytes_FromStringAndSize(target, target_length); 652 if (nxt_slow_path(v == NULL)) { 653 nxt_unit_req_alert(req, "Python failed to create 'raw_path' string"); 654 goto fail; 655 } 656 657 SET_ITEM(scope, raw_path, v) 658 Py_DECREF(v); 659 660 v = PyBytes_FromStringAndSize(query, r->query_length); 661 if (nxt_slow_path(v == NULL)) { 662 nxt_unit_req_alert(req, "Python failed to create 'query' string"); 663 goto fail; 664 } 665 666 SET_ITEM(scope, query_string, v) 667 Py_DECREF(v); 668 669 v = nxt_py_asgi_create_address(&r->remote, r->remote_length, 0); 670 if (nxt_slow_path(v == NULL)) { 671 nxt_unit_req_alert(req, "Python failed to create 'client' pair"); 672 goto fail; 673 } 674 675 SET_ITEM(scope, client, v) 676 Py_DECREF(v); 677 678 v = nxt_py_asgi_create_address(&r->local, r->local_length, 80); 679 if (nxt_slow_path(v == NULL)) { 680 nxt_unit_req_alert(req, "Python failed to create 'server' pair"); 681 goto fail; 682 } 683 684 SET_ITEM(scope, server, v) 685 Py_DECREF(v); 686 687 v = NULL; 688 689 headers = PyTuple_New(r->fields_count); 690 if (nxt_slow_path(headers == NULL)) { 691 nxt_unit_req_alert(req, "Python failed to create 'headers' object"); 692 goto fail; 693 } 694 695 for (i = 0; i < r->fields_count; i++) { 696 f = r->fields + i; 697 698 header = nxt_py_asgi_create_header(f); 699 if (nxt_slow_path(header == NULL)) { 700 nxt_unit_req_alert(req, "Python failed to create 'header' pair"); 701 goto fail; 702 } 703 704 PyTuple_SET_ITEM(headers, i, header); 705 706 if (f->hash == NXT_UNIT_HASH_WS_PROTOCOL 707 && f->name_length == ws_protocol.length 708 && f->value_length > 0 709 && r->websocket_handshake) 710 { 711 v = nxt_py_asgi_create_subprotocols(f); 712 if (nxt_slow_path(v == NULL)) { 713 nxt_unit_req_alert(req, "Failed to create subprotocols"); 714 goto fail; 715 } 716 717 SET_ITEM(scope, subprotocols, v); 718 Py_DECREF(v); 719 } 720 } 721 722 SET_ITEM(scope, headers, headers) 723 Py_DECREF(headers); 724 725 return scope; 726 727 fail: 728 729 Py_XDECREF(v); 730 Py_XDECREF(headers); 731 Py_DECREF(scope); 732 733 return NULL; 734 735 #undef SET_ITEM 736 } 737 738 739 static PyObject * 740 nxt_py_asgi_create_address(nxt_unit_sptr_t *sptr, uint8_t len, uint16_t port) 741 { 742 char *p, *s; 743 PyObject *pair, *v; 744 745 pair = PyTuple_New(2); 746 if (nxt_slow_path(pair == NULL)) { 747 return NULL; 748 } 749 750 p = nxt_unit_sptr_get(sptr); 751 s = memchr(p, ':', len); 752 753 v = PyString_FromStringAndSize(p, s == NULL ? len : s - p); 754 if (nxt_slow_path(v == NULL)) { 755 Py_DECREF(pair); 756 757 return NULL; 758 } 759 760 PyTuple_SET_ITEM(pair, 0, v); 761 762 if (s != NULL) { 763 p += len; 764 v = PyLong_FromString(s + 1, &p, 10); 765 766 } else { 767 v = PyLong_FromLong(port); 768 } 769 770 if (nxt_slow_path(v == NULL)) { 771 Py_DECREF(pair); 772 773 return NULL; 774 } 775 776 PyTuple_SET_ITEM(pair, 1, v); 777 778 return pair; 779 } 780 781 782 static PyObject * 783 nxt_py_asgi_create_header(nxt_unit_field_t *f) 784 { 785 char c, *name; 786 uint8_t pos; 787 PyObject *header, *v; 788 789 header = PyTuple_New(2); 790 if (nxt_slow_path(header == NULL)) { 791 return NULL; 792 } 793 794 name = nxt_unit_sptr_get(&f->name); 795 796 for (pos = 0; pos < f->name_length; pos++) { 797 c = name[pos]; 798 if (c >= 'A' && c <= 'Z') { 799 name[pos] = (c | 0x20); 800 } 801 } 802 803 v = PyBytes_FromStringAndSize(name, f->name_length); 804 if (nxt_slow_path(v == NULL)) { 805 Py_DECREF(header); 806 807 return NULL; 808 } 809 810 PyTuple_SET_ITEM(header, 0, v); 811 812 v = PyBytes_FromStringAndSize(nxt_unit_sptr_get(&f->value), 813 f->value_length); 814 if (nxt_slow_path(v == NULL)) { 815 Py_DECREF(header); 816 817 return NULL; 818 } 819 820 PyTuple_SET_ITEM(header, 1, v); 821 822 return header; 823 } 824 825 826 static PyObject * 827 nxt_py_asgi_create_subprotocols(nxt_unit_field_t *f) 828 { 829 char *v; 830 uint32_t i, n, start; 831 PyObject *res, *proto; 832 833 v = nxt_unit_sptr_get(&f->value); 834 n = 1; 835 836 for (i = 0; i < f->value_length; i++) { 837 if (v[i] == ',') { 838 n++; 839 } 840 } 841 842 res = PyTuple_New(n); 843 if (nxt_slow_path(res == NULL)) { 844 return NULL; 845 } 846 847 n = 0; 848 start = 0; 849 850 for (i = 0; i < f->value_length; ) { 851 if (v[i] != ',') { 852 i++; 853 854 continue; 855 } 856 857 if (i - start > 0) { 858 proto = PyString_FromStringAndSize(v + start, i - start); 859 if (nxt_slow_path(proto == NULL)) { 860 goto fail; 861 } 862 863 PyTuple_SET_ITEM(res, n, proto); 864 865 n++; 866 } 867 868 do { 869 i++; 870 } while (i < f->value_length && v[i] == ' '); 871 872 start = i; 873 } 874 875 if (i - start > 0) { 876 proto = PyString_FromStringAndSize(v + start, i - start); 877 if (nxt_slow_path(proto == NULL)) { 878 goto fail; 879 } 880 881 PyTuple_SET_ITEM(res, n, proto); 882 } 883 884 return res; 885 886 fail: 887 888 Py_DECREF(res); 889 890 return NULL; 891 } 892 893 894 static int 895 nxt_python_asgi_ready(nxt_unit_ctx_t *ctx) 896 { 897 int rc; 898 PyObject *res, *fd, *py_ctx, *py_port; 899 nxt_unit_port_t *port; 900 nxt_py_asgi_ctx_data_t *ctx_data; 901 902 if (nxt_slow_path(nxt_py_shared_port == NULL)) { 903 return NXT_UNIT_ERROR; 904 } 905 906 port = nxt_py_shared_port; 907 908 nxt_unit_debug(ctx, "asgi_ready %d %p %p", port->in_fd, ctx, port); 909 910 ctx_data = ctx->data; 911 912 rc = NXT_UNIT_ERROR; 913 914 fd = PyLong_FromLong(port->in_fd); 915 if (nxt_slow_path(fd == NULL)) { 916 nxt_unit_alert(ctx, "Python failed to create fd"); 917 nxt_python_print_exception(); 918 919 return rc; 920 } 921 922 py_ctx = PyLong_FromVoidPtr(ctx); 923 if (nxt_slow_path(py_ctx == NULL)) { 924 nxt_unit_alert(ctx, "Python failed to create py_ctx"); 925 nxt_python_print_exception(); 926 927 goto clean_fd; 928 } 929 930 py_port = PyLong_FromVoidPtr(port); 931 if (nxt_slow_path(py_port == NULL)) { 932 nxt_unit_alert(ctx, "Python failed to create py_port"); 933 nxt_python_print_exception(); 934 935 goto clean_py_ctx; 936 } 937 938 res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 939 fd, nxt_py_port_read, 940 py_ctx, py_port, NULL); 941 if (nxt_slow_path(res == NULL)) { 942 nxt_unit_alert(ctx, "Python failed to add_reader"); 943 nxt_python_print_exception(); 944 945 } else { 946 Py_DECREF(res); 947 948 rc = NXT_UNIT_OK; 949 } 950 951 Py_DECREF(py_port); 952 953 clean_py_ctx: 954 955 Py_DECREF(py_ctx); 956 957 clean_fd: 958 959 Py_DECREF(fd); 960 961 return rc; 962 } 963 964 965 static int 966 nxt_py_asgi_add_port(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port) 967 { 968 int nb, rc; 969 PyObject *res, *fd, *py_ctx, *py_port; 970 nxt_py_asgi_ctx_data_t *ctx_data; 971 972 if (port->in_fd == -1) { 973 return NXT_UNIT_OK; 974 } 975 976 nb = 1; 977 978 if (nxt_slow_path(ioctl(port->in_fd, FIONBIO, &nb) == -1)) { 979 nxt_unit_alert(ctx, "ioctl(%d, FIONBIO, 0) failed: %s (%d)", 980 port->in_fd, strerror(errno), errno); 981 982 return NXT_UNIT_ERROR; 983 } 984 985 nxt_unit_debug(ctx, "asgi_add_port %d %p %p", port->in_fd, ctx, port); 986 987 if (port->id.id == NXT_UNIT_SHARED_PORT_ID) { 988 nxt_py_shared_port = port; 989 990 return NXT_UNIT_OK; 991 } 992 993 ctx_data = ctx->data; 994 995 ctx_data->port = port; 996 port->data = ctx_data; 997 998 rc = NXT_UNIT_ERROR; 999 1000 fd = PyLong_FromLong(port->in_fd); 1001 if (nxt_slow_path(fd == NULL)) { 1002 nxt_unit_alert(ctx, "Python failed to create fd"); 1003 nxt_python_print_exception(); 1004 1005 return rc; 1006 } 1007 1008 py_ctx = PyLong_FromVoidPtr(ctx); 1009 if (nxt_slow_path(py_ctx == NULL)) { 1010 nxt_unit_alert(ctx, "Python failed to create py_ctx"); 1011 nxt_python_print_exception(); 1012 1013 goto clean_fd; 1014 } 1015 1016 py_port = PyLong_FromVoidPtr(port); 1017 if (nxt_slow_path(py_port == NULL)) { 1018 nxt_unit_alert(ctx, "Python failed to create py_port"); 1019 nxt_python_print_exception(); 1020 1021 goto clean_py_ctx; 1022 } 1023 1024 res = PyObject_CallFunctionObjArgs(ctx_data->loop_add_reader, 1025 fd, nxt_py_port_read, 1026 py_ctx, py_port, NULL); 1027 if (nxt_slow_path(res == NULL)) { 1028 nxt_unit_alert(ctx, "Python failed to add_reader"); 1029 nxt_python_print_exception(); 1030 1031 } else { 1032 Py_DECREF(res); 1033 1034 rc = NXT_UNIT_OK; 1035 } 1036 1037 Py_DECREF(py_port); 1038 1039 clean_py_ctx: 1040 1041 Py_DECREF(py_ctx); 1042 1043 clean_fd: 1044 1045 Py_DECREF(fd); 1046 1047 return rc; 1048 } 1049 1050 1051 static void 1052 nxt_py_asgi_remove_port(nxt_unit_t *lib, nxt_unit_port_t *port) 1053 { 1054 if (port->in_fd == -1) { 1055 return; 1056 } 1057 1058 nxt_unit_debug(NULL, "asgi_remove_port %d %p", port->in_fd, port); 1059 1060 if (nxt_py_shared_port == port) { 1061 nxt_py_shared_port = NULL; 1062 } 1063 } 1064 1065 1066 static void 1067 nxt_py_asgi_quit(nxt_unit_ctx_t *ctx) 1068 { 1069 PyObject *res, *p; 1070 nxt_py_asgi_ctx_data_t *ctx_data; 1071 1072 nxt_unit_debug(ctx, "asgi_quit %p", ctx); 1073 1074 ctx_data = ctx->data; 1075 1076 if (nxt_py_shared_port != NULL) { 1077 p = PyLong_FromLong(nxt_py_shared_port->in_fd); 1078 if (nxt_slow_path(p == NULL)) { 1079 nxt_unit_alert(NULL, "Python failed to create Long"); 1080 nxt_python_print_exception(); 1081 1082 } else { 1083 res = PyObject_CallFunctionObjArgs(ctx_data->loop_remove_reader, 1084 p, NULL); 1085 if (nxt_slow_path(res == NULL)) { 1086 nxt_unit_alert(NULL, "Python failed to remove_reader"); 1087 nxt_python_print_exception(); 1088 1089 } else { 1090 Py_DECREF(res); 1091 } 1092 1093 Py_DECREF(p); 1094 } 1095 } 1096 1097 p = PyLong_FromLong(0); 1098 if (nxt_slow_path(p == NULL)) { 1099 nxt_unit_alert(NULL, "Python failed to create Long"); 1100 nxt_python_print_exception(); 1101 1102 } else { 1103 res = PyObject_CallFunctionObjArgs(ctx_data->quit_future_set_result, 1104 p, NULL); 1105 if (nxt_slow_path(res == NULL)) { 1106 nxt_unit_alert(ctx, "Python failed to set_result"); 1107 nxt_python_print_exception(); 1108 1109 } else { 1110 Py_DECREF(res); 1111 } 1112 1113 Py_DECREF(p); 1114 } 1115 } 1116 1117 1118 static void 1119 nxt_py_asgi_shm_ack_handler(nxt_unit_ctx_t *ctx) 1120 { 1121 int rc; 1122 nxt_queue_link_t *lnk; 1123 nxt_py_asgi_ctx_data_t *ctx_data; 1124 1125 ctx_data = ctx->data; 1126 1127 while (!nxt_queue_is_empty(&ctx_data->drain_queue)) { 1128 lnk = nxt_queue_first(&ctx_data->drain_queue); 1129 1130 rc = nxt_py_asgi_http_drain(lnk); 1131 if (rc == NXT_UNIT_AGAIN) { 1132 return; 1133 } 1134 1135 nxt_queue_remove(lnk); 1136 } 1137 } 1138 1139 1140 static PyObject * 1141 nxt_py_asgi_port_read(PyObject *self, PyObject *args) 1142 { 1143 int rc; 1144 PyObject *arg0, *arg1, *res; 1145 Py_ssize_t n; 1146 nxt_unit_ctx_t *ctx; 1147 nxt_unit_port_t *port; 1148 nxt_py_asgi_ctx_data_t *ctx_data; 1149 1150 n = PyTuple_GET_SIZE(args); 1151 1152 if (n != 2) { 1153 nxt_unit_alert(NULL, 1154 "nxt_py_asgi_port_read: invalid number of arguments %d", 1155 (int) n); 1156 1157 return PyErr_Format(PyExc_TypeError, "invalid number of arguments"); 1158 } 1159 1160 arg0 = PyTuple_GET_ITEM(args, 0); 1161 if (nxt_slow_path(arg0 == NULL || PyLong_Check(arg0) == 0)) { 1162 return PyErr_Format(PyExc_TypeError, 1163 "the first argument is not a long"); 1164 } 1165 1166 ctx = PyLong_AsVoidPtr(arg0); 1167 1168 arg1 = PyTuple_GET_ITEM(args, 1); 1169 if (nxt_slow_path(arg1 == NULL || PyLong_Check(arg1) == 0)) { 1170 return PyErr_Format(PyExc_TypeError, 1171 "the second argument is not a long"); 1172 } 1173 1174 port = PyLong_AsVoidPtr(arg1); 1175 1176 rc = nxt_unit_process_port_msg(ctx, port); 1177 1178 nxt_unit_debug(ctx, "asgi_port_read(%p,%p): %d", ctx, port, rc); 1179 1180 if (nxt_slow_path(rc == NXT_UNIT_ERROR)) { 1181 return PyErr_Format(PyExc_RuntimeError, 1182 "error processing port %d message", port->id.id); 1183 } 1184 1185 if (rc == NXT_UNIT_OK) { 1186 ctx_data = ctx->data; 1187 1188 res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, 1189 nxt_py_port_read, 1190 arg0, arg1, NULL); 1191 if (nxt_slow_path(res == NULL)) { 1192 nxt_unit_alert(ctx, "Python failed to call 'loop.call_soon'"); 1193 nxt_python_print_exception(); 1194 } 1195 1196 Py_XDECREF(res); 1197 } 1198 1199 Py_RETURN_NONE; 1200 } 1201 1202 1203 PyObject * 1204 nxt_py_asgi_enum_headers(PyObject *headers, nxt_py_asgi_enum_header_cb cb, 1205 void *data) 1206 { 1207 int i; 1208 PyObject *iter, *header, *h_iter, *name, *val, *res; 1209 1210 iter = PyObject_GetIter(headers); 1211 if (nxt_slow_path(iter == NULL)) { 1212 return PyErr_Format(PyExc_TypeError, "'headers' is not an iterable"); 1213 } 1214 1215 for (i = 0; /* void */; i++) { 1216 header = PyIter_Next(iter); 1217 if (header == NULL) { 1218 break; 1219 } 1220 1221 h_iter = PyObject_GetIter(header); 1222 if (nxt_slow_path(h_iter == NULL)) { 1223 Py_DECREF(header); 1224 Py_DECREF(iter); 1225 1226 return PyErr_Format(PyExc_TypeError, 1227 "'headers' item #%d is not an iterable", i); 1228 } 1229 1230 name = PyIter_Next(h_iter); 1231 if (nxt_slow_path(name == NULL || !PyBytes_Check(name))) { 1232 Py_XDECREF(name); 1233 Py_DECREF(h_iter); 1234 Py_DECREF(header); 1235 Py_DECREF(iter); 1236 1237 return PyErr_Format(PyExc_TypeError, 1238 "'headers' item #%d 'name' is not a byte string", i); 1239 } 1240 1241 val = PyIter_Next(h_iter); 1242 if (nxt_slow_path(val == NULL || !PyBytes_Check(val))) { 1243 Py_XDECREF(val); 1244 Py_DECREF(h_iter); 1245 Py_DECREF(header); 1246 Py_DECREF(iter); 1247 1248 return PyErr_Format(PyExc_TypeError, 1249 "'headers' item #%d 'value' is not a byte string", i); 1250 } 1251 1252 res = cb(data, i, name, val); 1253 1254 Py_DECREF(name); 1255 Py_DECREF(val); 1256 Py_DECREF(h_iter); 1257 Py_DECREF(header); 1258 1259 if (nxt_slow_path(res == NULL)) { 1260 Py_DECREF(iter); 1261 1262 return NULL; 1263 } 1264 1265 Py_DECREF(res); 1266 } 1267 1268 Py_DECREF(iter); 1269 1270 Py_RETURN_NONE; 1271 } 1272 1273 1274 PyObject * 1275 nxt_py_asgi_calc_size(void *data, int i, PyObject *name, PyObject *val) 1276 { 1277 nxt_py_asgi_calc_size_ctx_t *ctx; 1278 1279 ctx = data; 1280 1281 ctx->fields_count++; 1282 ctx->fields_size += PyBytes_GET_SIZE(name) + PyBytes_GET_SIZE(val); 1283 1284 Py_RETURN_NONE; 1285 } 1286 1287 1288 PyObject * 1289 nxt_py_asgi_add_field(void *data, int i, PyObject *name, PyObject *val) 1290 { 1291 int rc; 1292 char *name_str, *val_str; 1293 uint32_t name_len, val_len; 1294 nxt_off_t content_length; 1295 nxt_unit_request_info_t *req; 1296 nxt_py_asgi_add_field_ctx_t *ctx; 1297 1298 name_str = PyBytes_AS_STRING(name); 1299 name_len = PyBytes_GET_SIZE(name); 1300 1301 val_str = PyBytes_AS_STRING(val); 1302 val_len = PyBytes_GET_SIZE(val); 1303 1304 ctx = data; 1305 req = ctx->req; 1306 1307 rc = nxt_unit_response_add_field(req, name_str, name_len, 1308 val_str, val_len); 1309 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1310 return PyErr_Format(PyExc_RuntimeError, 1311 "failed to add header #%d", i); 1312 } 1313 1314 if (req->response->fields[i].hash == NXT_UNIT_HASH_CONTENT_LENGTH) { 1315 content_length = nxt_off_t_parse((u_char *) val_str, val_len); 1316 if (nxt_slow_path(content_length < 0)) { 1317 nxt_unit_req_error(req, "failed to parse Content-Length " 1318 "value %.*s", (int) val_len, val_str); 1319 1320 return PyErr_Format(PyExc_ValueError, 1321 "Failed to parse Content-Length: '%.*s'", 1322 (int) val_len, val_str); 1323 } 1324 1325 ctx->content_length = content_length; 1326 } 1327 1328 Py_RETURN_NONE; 1329 } 1330 1331 1332 PyObject * 1333 nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req, 1334 nxt_py_asgi_ctx_data_t *ctx_data, PyObject *future, PyObject *result) 1335 { 1336 PyObject *set_result, *res; 1337 1338 if (nxt_slow_path(result == NULL)) { 1339 Py_DECREF(future); 1340 1341 return NULL; 1342 } 1343 1344 set_result = PyObject_GetAttrString(future, "set_result"); 1345 if (nxt_slow_path(set_result == NULL)) { 1346 nxt_unit_req_alert(req, "failed to get 'set_result' for future"); 1347 1348 Py_CLEAR(future); 1349 1350 goto cleanup_result; 1351 } 1352 1353 if (nxt_slow_path(PyCallable_Check(set_result) == 0)) { 1354 nxt_unit_req_alert(req, "'future.set_result' is not a callable"); 1355 1356 Py_CLEAR(future); 1357 1358 goto cleanup; 1359 } 1360 1361 res = PyObject_CallFunctionObjArgs(ctx_data->loop_call_soon, set_result, 1362 result, NULL); 1363 if (nxt_slow_path(res == NULL)) { 1364 nxt_unit_req_alert(req, "Python failed to call 'loop.call_soon'"); 1365 nxt_python_print_exception(); 1366 1367 Py_CLEAR(future); 1368 } 1369 1370 Py_XDECREF(res); 1371 1372 cleanup: 1373 1374 Py_DECREF(set_result); 1375 1376 cleanup_result: 1377 1378 Py_DECREF(result); 1379 1380 return future; 1381 } 1382 1383 1384 PyObject * 1385 nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type) 1386 { 1387 PyObject *msg; 1388 1389 msg = PyDict_New(); 1390 if (nxt_slow_path(msg == NULL)) { 1391 nxt_unit_req_alert(req, "Python failed to create message dict"); 1392 nxt_python_print_exception(); 1393 1394 return PyErr_Format(PyExc_RuntimeError, 1395 "failed to create message dict"); 1396 } 1397 1398 if (nxt_slow_path(PyDict_SetItem(msg, nxt_py_type_str, type) == -1)) { 1399 nxt_unit_req_alert(req, "Python failed to set 'msg.type' item"); 1400 1401 Py_DECREF(msg); 1402 1403 return PyErr_Format(PyExc_RuntimeError, 1404 "failed to set 'msg.type' item"); 1405 } 1406 1407 return msg; 1408 } 1409 1410 1411 PyObject * 1412 nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type, 1413 PyObject *spec_version) 1414 { 1415 PyObject *scope, *asgi; 1416 1417 scope = PyDict_New(); 1418 if (nxt_slow_path(scope == NULL)) { 1419 nxt_unit_req_alert(req, "Python failed to create 'scope' dict"); 1420 nxt_python_print_exception(); 1421 1422 return PyErr_Format(PyExc_RuntimeError, 1423 "failed to create 'scope' dict"); 1424 } 1425 1426 if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_type_str, type) == -1)) { 1427 nxt_unit_req_alert(req, "Python failed to set 'scope.type' item"); 1428 1429 Py_DECREF(scope); 1430 1431 return PyErr_Format(PyExc_RuntimeError, 1432 "failed to set 'scope.type' item"); 1433 } 1434 1435 asgi = PyDict_New(); 1436 if (nxt_slow_path(asgi == NULL)) { 1437 nxt_unit_req_alert(req, "Python failed to create 'asgi' dict"); 1438 nxt_python_print_exception(); 1439 1440 Py_DECREF(scope); 1441 1442 return PyErr_Format(PyExc_RuntimeError, 1443 "failed to create 'asgi' dict"); 1444 } 1445 1446 if (nxt_slow_path(PyDict_SetItem(scope, nxt_py_asgi_str, asgi) == -1)) { 1447 nxt_unit_req_alert(req, "Python failed to set 'scope.asgi' item"); 1448 1449 Py_DECREF(asgi); 1450 Py_DECREF(scope); 1451 1452 return PyErr_Format(PyExc_RuntimeError, 1453 "failed to set 'scope.asgi' item"); 1454 } 1455 1456 if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_version_str, 1457 nxt_py_3_0_str) == -1)) 1458 { 1459 nxt_unit_req_alert(req, "Python failed to set 'asgi.version' item"); 1460 1461 Py_DECREF(asgi); 1462 Py_DECREF(scope); 1463 1464 return PyErr_Format(PyExc_RuntimeError, 1465 "failed to set 'asgi.version' item"); 1466 } 1467 1468 if (nxt_slow_path(PyDict_SetItem(asgi, nxt_py_spec_version_str, 1469 spec_version) == -1)) 1470 { 1471 nxt_unit_req_alert(req, 1472 "Python failed to set 'asgi.spec_version' item"); 1473 1474 Py_DECREF(asgi); 1475 Py_DECREF(scope); 1476 1477 return PyErr_Format(PyExc_RuntimeError, 1478 "failed to set 'asgi.spec_version' item"); 1479 } 1480 1481 Py_DECREF(asgi); 1482 1483 return scope; 1484 } 1485 1486 1487 void 1488 nxt_py_asgi_drain_wait(nxt_unit_request_info_t *req, nxt_queue_link_t *link) 1489 { 1490 nxt_py_asgi_ctx_data_t *ctx_data; 1491 1492 ctx_data = req->ctx->data; 1493 1494 nxt_queue_insert_tail(&ctx_data->drain_queue, link); 1495 } 1496 1497 1498 void 1499 nxt_py_asgi_dealloc(PyObject *self) 1500 { 1501 PyObject_Del(self); 1502 } 1503 1504 1505 PyObject * 1506 nxt_py_asgi_await(PyObject *self) 1507 { 1508 Py_INCREF(self); 1509 return self; 1510 } 1511 1512 1513 PyObject * 1514 nxt_py_asgi_iter(PyObject *self) 1515 { 1516 Py_INCREF(self); 1517 return self; 1518 } 1519 1520 1521 PyObject * 1522 nxt_py_asgi_next(PyObject *self) 1523 { 1524 return NULL; 1525 } 1526 1527 1528 static void 1529 nxt_python_asgi_done(void) 1530 { 1531 nxt_py_asgi_str_done(); 1532 1533 Py_XDECREF(nxt_py_port_read); 1534 } 1535 1536 #else /* !(NXT_HAVE_ASGI) */ 1537 1538 1539 int 1540 nxt_python_asgi_check(PyObject *obj) 1541 { 1542 return 0; 1543 } 1544 1545 1546 int 1547 nxt_python_asgi_init(nxt_unit_init_t *init, nxt_python_proto_t *proto) 1548 { 1549 nxt_unit_alert(NULL, "ASGI not implemented"); 1550 return NXT_UNIT_ERROR; 1551 } 1552 1553 1554 #endif /* NXT_HAVE_ASGI */ 1555