@@ -168,6 +168,11 @@ $echo >> $NXT_MAKEFILE
|
|||||||
|
|
||||||
NXT_PYTHON_MODULE_SRCS=" \
|
NXT_PYTHON_MODULE_SRCS=" \
|
||||||
src/python/nxt_python.c \
|
src/python/nxt_python.c \
|
||||||
|
src/python/nxt_python_asgi.c \
|
||||||
|
src/python/nxt_python_asgi_http.c \
|
||||||
|
src/python/nxt_python_asgi_lifespan.c \
|
||||||
|
src/python/nxt_python_asgi_str.c \
|
||||||
|
src/python/nxt_python_asgi_websocket.c \
|
||||||
src/python/nxt_python_wsgi.c \
|
src/python/nxt_python_wsgi.c \
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|||||||
@@ -15,14 +15,6 @@
|
|||||||
#include NXT_PYTHON_MOUNTS_H
|
#include NXT_PYTHON_MOUNTS_H
|
||||||
|
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION == 3
|
|
||||||
#define PyString_FromStringAndSize(str, size) \
|
|
||||||
PyUnicode_DecodeLatin1((str), (size), "strict")
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define PyUnicode_InternInPlace PyString_InternInPlace
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static nxt_int_t nxt_python_start(nxt_task_t *task,
|
static nxt_int_t nxt_python_start(nxt_task_t *task,
|
||||||
nxt_process_data_t *data);
|
nxt_process_data_t *data);
|
||||||
static void nxt_python_atexit(void);
|
static void nxt_python_atexit(void);
|
||||||
@@ -56,7 +48,7 @@ static char *nxt_py_home;
|
|||||||
static nxt_int_t
|
static nxt_int_t
|
||||||
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc, asgi;
|
||||||
char *nxt_py_module;
|
char *nxt_py_module;
|
||||||
size_t len;
|
size_t len;
|
||||||
PyObject *obj, *pypath, *module;
|
PyObject *obj, *pypath, *module;
|
||||||
@@ -226,7 +218,15 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
|||||||
|
|
||||||
python_init.shm_limit = data->app->shm_limit;
|
python_init.shm_limit = data->app->shm_limit;
|
||||||
|
|
||||||
|
asgi = nxt_python_asgi_check(nxt_py_application);
|
||||||
|
|
||||||
|
if (asgi) {
|
||||||
|
rc = nxt_python_asgi_init(task, &python_init);
|
||||||
|
|
||||||
|
} else {
|
||||||
rc = nxt_python_wsgi_init(task, &python_init);
|
rc = nxt_python_wsgi_init(task, &python_init);
|
||||||
|
}
|
||||||
|
|
||||||
if (nxt_slow_path(rc == NXT_ERROR)) {
|
if (nxt_slow_path(rc == NXT_ERROR)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -236,7 +236,12 @@ nxt_python_start(nxt_task_t *task, nxt_process_data_t *data)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asgi) {
|
||||||
|
rc = nxt_python_asgi_run(unit_ctx);
|
||||||
|
|
||||||
|
} else {
|
||||||
rc = nxt_python_wsgi_run(unit_ctx);
|
rc = nxt_python_wsgi_run(unit_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
nxt_unit_done(unit_ctx);
|
nxt_unit_done(unit_ctx);
|
||||||
|
|
||||||
@@ -300,6 +305,7 @@ static void
|
|||||||
nxt_python_atexit(void)
|
nxt_python_atexit(void)
|
||||||
{
|
{
|
||||||
nxt_python_wsgi_done();
|
nxt_python_wsgi_done();
|
||||||
|
nxt_python_asgi_done();
|
||||||
|
|
||||||
Py_XDECREF(nxt_py_stderr_flush);
|
Py_XDECREF(nxt_py_stderr_flush);
|
||||||
Py_XDECREF(nxt_py_application);
|
Py_XDECREF(nxt_py_application);
|
||||||
|
|||||||
@@ -8,9 +8,32 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include <nxt_main.h>
|
||||||
#include <nxt_unit.h>
|
#include <nxt_unit.h>
|
||||||
|
|
||||||
|
|
||||||
|
#if PY_MAJOR_VERSION == 3
|
||||||
|
#define NXT_PYTHON_BYTES_TYPE "bytestring"
|
||||||
|
|
||||||
|
#define PyString_FromStringAndSize(str, size) \
|
||||||
|
PyUnicode_DecodeLatin1((str), (size), "strict")
|
||||||
|
#define PyString_AS_STRING PyUnicode_DATA
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define NXT_PYTHON_BYTES_TYPE "string"
|
||||||
|
|
||||||
|
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
|
||||||
|
#define PyBytes_Check PyString_Check
|
||||||
|
#define PyBytes_GET_SIZE PyString_GET_SIZE
|
||||||
|
#define PyBytes_AS_STRING PyString_AS_STRING
|
||||||
|
#define PyUnicode_InternInPlace PyString_InternInPlace
|
||||||
|
#define PyUnicode_AsUTF8 PyString_AS_STRING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5
|
||||||
|
#define NXT_HAVE_ASGI 1
|
||||||
|
#endif
|
||||||
|
|
||||||
extern PyObject *nxt_py_application;
|
extern PyObject *nxt_py_application;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -18,6 +41,7 @@ typedef struct {
|
|||||||
PyObject **object_p;
|
PyObject **object_p;
|
||||||
} nxt_python_string_t;
|
} nxt_python_string_t;
|
||||||
|
|
||||||
|
|
||||||
nxt_int_t nxt_python_init_strings(nxt_python_string_t *pstr);
|
nxt_int_t nxt_python_init_strings(nxt_python_string_t *pstr);
|
||||||
void nxt_python_done_strings(nxt_python_string_t *pstr);
|
void nxt_python_done_strings(nxt_python_string_t *pstr);
|
||||||
|
|
||||||
@@ -27,5 +51,10 @@ nxt_int_t nxt_python_wsgi_init(nxt_task_t *task, nxt_unit_init_t *init);
|
|||||||
int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
|
int nxt_python_wsgi_run(nxt_unit_ctx_t *ctx);
|
||||||
void nxt_python_wsgi_done(void);
|
void nxt_python_wsgi_done(void);
|
||||||
|
|
||||||
|
int nxt_python_asgi_check(PyObject *obj);
|
||||||
|
nxt_int_t nxt_python_asgi_init(nxt_task_t *task, nxt_unit_init_t *init);
|
||||||
|
nxt_int_t nxt_python_asgi_run(nxt_unit_ctx_t *ctx);
|
||||||
|
void nxt_python_asgi_done(void);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NXT_PYTHON_H_INCLUDED_ */
|
#endif /* _NXT_PYTHON_H_INCLUDED_ */
|
||||||
|
|||||||
1227
src/python/nxt_python_asgi.c
Normal file
1227
src/python/nxt_python_asgi.c
Normal file
File diff suppressed because it is too large
Load Diff
60
src/python/nxt_python_asgi.h
Normal file
60
src/python/nxt_python_asgi.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NXT_PYTHON_ASGI_H_INCLUDED_
|
||||||
|
#define _NXT_PYTHON_ASGI_H_INCLUDED_
|
||||||
|
|
||||||
|
|
||||||
|
typedef PyObject * (*nxt_py_asgi_enum_header_cb)(void *ctx, int i,
|
||||||
|
PyObject *name, PyObject *val);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t fields_count;
|
||||||
|
uint32_t fields_size;
|
||||||
|
} nxt_py_asgi_calc_size_ctx_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
nxt_unit_request_info_t *req;
|
||||||
|
uint64_t content_length;
|
||||||
|
} nxt_py_asgi_add_field_ctx_t;
|
||||||
|
|
||||||
|
PyObject *nxt_py_asgi_enum_headers(PyObject *headers,
|
||||||
|
nxt_py_asgi_enum_header_cb cb, void *data);
|
||||||
|
|
||||||
|
PyObject *nxt_py_asgi_calc_size(void *data, int i, PyObject *n, PyObject *v);
|
||||||
|
PyObject *nxt_py_asgi_add_field(void *data, int i, PyObject *n, PyObject *v);
|
||||||
|
|
||||||
|
PyObject *nxt_py_asgi_set_result_soon(nxt_unit_request_info_t *req,
|
||||||
|
PyObject *future, PyObject *result);
|
||||||
|
PyObject *nxt_py_asgi_new_msg(nxt_unit_request_info_t *req, PyObject *type);
|
||||||
|
PyObject *nxt_py_asgi_new_scope(nxt_unit_request_info_t *req, PyObject *type,
|
||||||
|
PyObject *spec_version);
|
||||||
|
|
||||||
|
void nxt_py_asgi_dealloc(PyObject *self);
|
||||||
|
PyObject *nxt_py_asgi_await(PyObject *self);
|
||||||
|
PyObject *nxt_py_asgi_iter(PyObject *self);
|
||||||
|
PyObject *nxt_py_asgi_next(PyObject *self);
|
||||||
|
|
||||||
|
nxt_int_t nxt_py_asgi_http_init(nxt_task_t *task);
|
||||||
|
PyObject *nxt_py_asgi_http_create(nxt_unit_request_info_t *req);
|
||||||
|
void nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req);
|
||||||
|
int nxt_py_asgi_http_drain(nxt_queue_link_t *lnk);
|
||||||
|
|
||||||
|
nxt_int_t nxt_py_asgi_websocket_init(nxt_task_t *task);
|
||||||
|
PyObject *nxt_py_asgi_websocket_create(nxt_unit_request_info_t *req);
|
||||||
|
void nxt_py_asgi_websocket_handler(nxt_unit_websocket_frame_t *ws);
|
||||||
|
void nxt_py_asgi_websocket_close_handler(nxt_unit_request_info_t *req);
|
||||||
|
|
||||||
|
nxt_int_t nxt_py_asgi_lifespan_startup(nxt_task_t *task);
|
||||||
|
nxt_int_t nxt_py_asgi_lifespan_shutdown(void);
|
||||||
|
|
||||||
|
extern PyObject *nxt_py_loop_run_until_complete;
|
||||||
|
extern PyObject *nxt_py_loop_create_future;
|
||||||
|
extern PyObject *nxt_py_loop_create_task;
|
||||||
|
|
||||||
|
extern nxt_queue_t nxt_py_asgi_drain_queue;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _NXT_PYTHON_ASGI_H_INCLUDED_ */
|
||||||
591
src/python/nxt_python_asgi_http.c
Normal file
591
src/python/nxt_python_asgi_http.c
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <python/nxt_python.h>
|
||||||
|
|
||||||
|
#if (NXT_HAVE_ASGI)
|
||||||
|
|
||||||
|
#include <nxt_main.h>
|
||||||
|
#include <nxt_unit.h>
|
||||||
|
#include <nxt_unit_request.h>
|
||||||
|
#include <python/nxt_python_asgi.h>
|
||||||
|
#include <python/nxt_python_asgi_str.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
nxt_unit_request_info_t *req;
|
||||||
|
nxt_queue_link_t link;
|
||||||
|
PyObject *receive_future;
|
||||||
|
PyObject *send_future;
|
||||||
|
uint64_t content_length;
|
||||||
|
uint64_t bytes_sent;
|
||||||
|
int complete;
|
||||||
|
PyObject *send_body;
|
||||||
|
Py_ssize_t send_body_off;
|
||||||
|
} nxt_py_asgi_http_t;
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *nxt_py_asgi_http_receive(PyObject *self, PyObject *none);
|
||||||
|
static PyObject *nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http);
|
||||||
|
static PyObject *nxt_py_asgi_http_send(PyObject *self, PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http,
|
||||||
|
PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http,
|
||||||
|
PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_http_done(PyObject *self, PyObject *future);
|
||||||
|
|
||||||
|
|
||||||
|
static PyMethodDef nxt_py_asgi_http_methods[] = {
|
||||||
|
{ "receive", nxt_py_asgi_http_receive, METH_NOARGS, 0 },
|
||||||
|
{ "send", nxt_py_asgi_http_send, METH_O, 0 },
|
||||||
|
{ "_done", nxt_py_asgi_http_done, METH_O, 0 },
|
||||||
|
{ NULL, NULL, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyAsyncMethods nxt_py_asgi_async_methods = {
|
||||||
|
.am_await = nxt_py_asgi_await,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject nxt_py_asgi_http_type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
|
||||||
|
.tp_name = "unit._asgi_http",
|
||||||
|
.tp_basicsize = sizeof(nxt_py_asgi_http_t),
|
||||||
|
.tp_dealloc = nxt_py_asgi_dealloc,
|
||||||
|
.tp_as_async = &nxt_py_asgi_async_methods,
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
.tp_doc = "unit ASGI HTTP request object",
|
||||||
|
.tp_iter = nxt_py_asgi_iter,
|
||||||
|
.tp_iternext = nxt_py_asgi_next,
|
||||||
|
.tp_methods = nxt_py_asgi_http_methods,
|
||||||
|
};
|
||||||
|
|
||||||
|
static Py_ssize_t nxt_py_asgi_http_body_buf_size = 32 * 1024 * 1024;
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_py_asgi_http_init(nxt_task_t *task)
|
||||||
|
{
|
||||||
|
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_http_type) != 0)) {
|
||||||
|
nxt_alert(task, "Python failed to initialize the 'http' type object");
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NXT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
nxt_py_asgi_http_create(nxt_unit_request_info_t *req)
|
||||||
|
{
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
|
||||||
|
http = PyObject_New(nxt_py_asgi_http_t, &nxt_py_asgi_http_type);
|
||||||
|
|
||||||
|
if (nxt_fast_path(http != NULL)) {
|
||||||
|
http->req = req;
|
||||||
|
http->receive_future = NULL;
|
||||||
|
http->send_future = NULL;
|
||||||
|
http->content_length = -1;
|
||||||
|
http->bytes_sent = 0;
|
||||||
|
http->complete = 0;
|
||||||
|
http->send_body = NULL;
|
||||||
|
http->send_body_off = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (PyObject *) http;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_receive(PyObject *self, PyObject *none)
|
||||||
|
{
|
||||||
|
PyObject *msg, *future;
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
nxt_unit_request_info_t *req;
|
||||||
|
|
||||||
|
http = (nxt_py_asgi_http_t *) self;
|
||||||
|
req = http->req;
|
||||||
|
|
||||||
|
nxt_unit_req_debug(req, "asgi_http_receive");
|
||||||
|
|
||||||
|
msg = nxt_py_asgi_http_read_msg(http);
|
||||||
|
if (nxt_slow_path(msg == NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
|
||||||
|
if (nxt_slow_path(future == NULL)) {
|
||||||
|
nxt_unit_req_alert(req, "Python failed to create Future object");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
Py_DECREF(msg);
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to create Future object");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg != Py_None) {
|
||||||
|
return nxt_py_asgi_set_result_soon(req, future, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
http->receive_future = future;
|
||||||
|
Py_INCREF(http->receive_future);
|
||||||
|
|
||||||
|
Py_DECREF(msg);
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_read_msg(nxt_py_asgi_http_t *http)
|
||||||
|
{
|
||||||
|
char *body_buf;
|
||||||
|
ssize_t read_res;
|
||||||
|
PyObject *msg, *body;
|
||||||
|
Py_ssize_t size;
|
||||||
|
nxt_unit_request_info_t *req;
|
||||||
|
|
||||||
|
req = http->req;
|
||||||
|
|
||||||
|
size = req->content_length;
|
||||||
|
|
||||||
|
if (size > nxt_py_asgi_http_body_buf_size) {
|
||||||
|
size = nxt_py_asgi_http_body_buf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 0) {
|
||||||
|
body = PyBytes_FromStringAndSize(NULL, size);
|
||||||
|
if (nxt_slow_path(body == NULL)) {
|
||||||
|
nxt_unit_req_alert(req, "Python failed to create body byte string");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to create Bytes object");
|
||||||
|
}
|
||||||
|
|
||||||
|
body_buf = PyBytes_AS_STRING(body);
|
||||||
|
|
||||||
|
read_res = nxt_unit_request_read(req, body_buf, size);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
body = NULL;
|
||||||
|
read_res = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_res > 0 || read_res == size) {
|
||||||
|
msg = nxt_py_asgi_new_msg(req, nxt_py_http_request_str);
|
||||||
|
if (nxt_slow_path(msg == NULL)) {
|
||||||
|
Py_XDECREF(body);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SET_ITEM(dict, key, value) \
|
||||||
|
if (nxt_slow_path(PyDict_SetItem(dict, nxt_py_ ## key ## _str, value) \
|
||||||
|
== -1)) \
|
||||||
|
{ \
|
||||||
|
nxt_unit_req_alert(req, \
|
||||||
|
"Python failed to set '" #dict "." #key "' item"); \
|
||||||
|
PyErr_SetString(PyExc_RuntimeError, \
|
||||||
|
"Python failed to set '" #dict "." #key "' item"); \
|
||||||
|
goto fail; \
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body != NULL) {
|
||||||
|
SET_ITEM(msg, body, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->content_length > 0) {
|
||||||
|
SET_ITEM(msg, more_body, Py_True)
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SET_ITEM
|
||||||
|
|
||||||
|
Py_XDECREF(body);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(body);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
|
||||||
|
Py_DECREF(msg);
|
||||||
|
Py_XDECREF(body);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_send(PyObject *self, PyObject *dict)
|
||||||
|
{
|
||||||
|
PyObject *type;
|
||||||
|
const char *type_str;
|
||||||
|
Py_ssize_t type_len;
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
|
||||||
|
static const nxt_str_t response_start = nxt_string("http.response.start");
|
||||||
|
static const nxt_str_t response_body = nxt_string("http.response.body");
|
||||||
|
|
||||||
|
http = (nxt_py_asgi_http_t *) self;
|
||||||
|
|
||||||
|
type = PyDict_GetItem(dict, nxt_py_type_str);
|
||||||
|
if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) {
|
||||||
|
nxt_unit_req_error(http->req, "asgi_http_send: "
|
||||||
|
"'type' is not a unicode string");
|
||||||
|
return PyErr_Format(PyExc_TypeError, "'type' is not a unicode string");
|
||||||
|
}
|
||||||
|
|
||||||
|
type_str = PyUnicode_AsUTF8AndSize(type, &type_len);
|
||||||
|
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_send type is '%.*s'",
|
||||||
|
(int) type_len, type_str);
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) response_start.length
|
||||||
|
&& memcmp(type_str, response_start.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
return nxt_py_asgi_http_response_start(http, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) response_body.length
|
||||||
|
&& memcmp(type_str, response_body.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
return nxt_py_asgi_http_response_body(http, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
nxt_unit_req_error(http->req, "asgi_http_send: unexpected 'type': '%.*s'",
|
||||||
|
(int) type_len, type_str);
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_AssertionError, "unexpected 'type': '%U'", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_response_start(nxt_py_asgi_http_t *http, PyObject *dict)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
PyObject *status, *headers, *res;
|
||||||
|
nxt_py_asgi_calc_size_ctx_t calc_size_ctx;
|
||||||
|
nxt_py_asgi_add_field_ctx_t add_field_ctx;
|
||||||
|
|
||||||
|
status = PyDict_GetItem(dict, nxt_py_status_str);
|
||||||
|
if (nxt_slow_path(status == NULL || !PyLong_Check(status))) {
|
||||||
|
nxt_unit_req_error(http->req, "asgi_http_response_start: "
|
||||||
|
"'status' is not an integer");
|
||||||
|
return PyErr_Format(PyExc_TypeError, "'status' is not an integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
calc_size_ctx.fields_size = 0;
|
||||||
|
calc_size_ctx.fields_count = 0;
|
||||||
|
|
||||||
|
headers = PyDict_GetItem(dict, nxt_py_headers_str);
|
||||||
|
if (headers != NULL) {
|
||||||
|
res = nxt_py_asgi_enum_headers(headers, nxt_py_asgi_calc_size,
|
||||||
|
&calc_size_ctx);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = nxt_unit_response_init(http->req, PyLong_AsLong(status),
|
||||||
|
calc_size_ctx.fields_count,
|
||||||
|
calc_size_ctx.fields_size);
|
||||||
|
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to allocate response object");
|
||||||
|
}
|
||||||
|
|
||||||
|
add_field_ctx.req = http->req;
|
||||||
|
add_field_ctx.content_length = -1;
|
||||||
|
|
||||||
|
if (headers != NULL) {
|
||||||
|
res = nxt_py_asgi_enum_headers(headers, nxt_py_asgi_add_field,
|
||||||
|
&add_field_ctx);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
http->content_length = add_field_ctx.content_length;
|
||||||
|
|
||||||
|
Py_INCREF(http);
|
||||||
|
return (PyObject *) http;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
char *body_str;
|
||||||
|
ssize_t sent;
|
||||||
|
PyObject *body, *more_body, *future;
|
||||||
|
Py_ssize_t body_len, body_off;
|
||||||
|
|
||||||
|
body = PyDict_GetItem(dict, nxt_py_body_str);
|
||||||
|
if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) {
|
||||||
|
return PyErr_Format(PyExc_TypeError, "'body' is not a byte string");
|
||||||
|
}
|
||||||
|
|
||||||
|
more_body = PyDict_GetItem(dict, nxt_py_more_body_str);
|
||||||
|
if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) {
|
||||||
|
return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_slow_path(http->complete)) {
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"Unexpected ASGI message 'http.response.body' "
|
||||||
|
"sent, after response already completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_slow_path(http->send_future != NULL)) {
|
||||||
|
return PyErr_Format(PyExc_RuntimeError, "Concurrent send");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body != NULL) {
|
||||||
|
body_str = PyBytes_AS_STRING(body);
|
||||||
|
body_len = PyBytes_GET_SIZE(body);
|
||||||
|
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_response_body: %d, %d",
|
||||||
|
(int) body_len, (more_body == Py_True) );
|
||||||
|
|
||||||
|
if (nxt_slow_path(http->bytes_sent + body_len
|
||||||
|
> http->content_length))
|
||||||
|
{
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"Response content longer than Content-Length");
|
||||||
|
}
|
||||||
|
|
||||||
|
body_off = 0;
|
||||||
|
|
||||||
|
while (body_len > 0) {
|
||||||
|
sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0);
|
||||||
|
if (nxt_slow_path(sent < 0)) {
|
||||||
|
return PyErr_Format(PyExc_RuntimeError, "failed to send body");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_slow_path(sent == 0)) {
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_response_body: "
|
||||||
|
"out of shared memory, %d",
|
||||||
|
(int) body_len);
|
||||||
|
|
||||||
|
future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
|
||||||
|
if (nxt_slow_path(future == NULL)) {
|
||||||
|
nxt_unit_req_alert(http->req,
|
||||||
|
"Python failed to create Future object");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to create Future object");
|
||||||
|
}
|
||||||
|
|
||||||
|
http->send_body = body;
|
||||||
|
Py_INCREF(http->send_body);
|
||||||
|
http->send_body_off = body_off;
|
||||||
|
|
||||||
|
nxt_queue_insert_tail(&nxt_py_asgi_drain_queue, &http->link);
|
||||||
|
|
||||||
|
http->send_future = future;
|
||||||
|
Py_INCREF(http->send_future);
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
body_str += sent;
|
||||||
|
body_len -= sent;
|
||||||
|
body_off += sent;
|
||||||
|
http->bytes_sent += sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_response_body: 0, %d",
|
||||||
|
(more_body == Py_True) );
|
||||||
|
|
||||||
|
if (!nxt_unit_response_is_sent(http->req)) {
|
||||||
|
rc = nxt_unit_response_send(http->req);
|
||||||
|
if (nxt_slow_path(rc != NXT_UNIT_OK)) {
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to send response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (more_body == NULL || more_body == Py_False) {
|
||||||
|
http->complete = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(http);
|
||||||
|
return (PyObject *) http;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
nxt_py_asgi_http_data_handler(nxt_unit_request_info_t *req)
|
||||||
|
{
|
||||||
|
PyObject *msg, *future, *res;
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
|
||||||
|
http = req->data;
|
||||||
|
|
||||||
|
nxt_unit_req_debug(req, "asgi_http_data_handler");
|
||||||
|
|
||||||
|
if (http->receive_future == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = nxt_py_asgi_http_read_msg(http);
|
||||||
|
if (nxt_slow_path(msg == NULL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg == Py_None) {
|
||||||
|
Py_DECREF(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
Py_DECREF(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
nxt_py_asgi_http_drain(nxt_queue_link_t *lnk)
|
||||||
|
{
|
||||||
|
char *body_str;
|
||||||
|
ssize_t sent;
|
||||||
|
PyObject *future, *exc, *res;
|
||||||
|
Py_ssize_t body_len;
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
|
||||||
|
http = nxt_container_of(lnk, nxt_py_asgi_http_t, link);
|
||||||
|
|
||||||
|
body_str = PyBytes_AS_STRING(http->send_body) + http->send_body_off;
|
||||||
|
body_len = PyBytes_GET_SIZE(http->send_body) - http->send_body_off;
|
||||||
|
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_drain: %d", (int) body_len);
|
||||||
|
|
||||||
|
while (body_len > 0) {
|
||||||
|
sent = nxt_unit_response_write_nb(http->req, body_str, body_len, 0);
|
||||||
|
if (nxt_slow_path(sent < 0)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_slow_path(sent == 0)) {
|
||||||
|
return NXT_UNIT_AGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
body_str += sent;
|
||||||
|
body_len -= sent;
|
||||||
|
|
||||||
|
http->send_body_off += sent;
|
||||||
|
http->bytes_sent += sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_CLEAR(http->send_body);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
return NXT_UNIT_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
|
||||||
|
exc = PyObject_CallFunctionObjArgs(PyExc_RuntimeError,
|
||||||
|
nxt_py_failed_to_send_body_str,
|
||||||
|
NULL);
|
||||||
|
if (nxt_slow_path(exc == NULL)) {
|
||||||
|
nxt_unit_req_alert(http->req, "RuntimeError create failed");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
exc = Py_None;
|
||||||
|
Py_INCREF(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
future = http->send_future;
|
||||||
|
http->send_future = NULL;
|
||||||
|
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_set_exception_str, exc,
|
||||||
|
NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_req_alert(http->req, "'set_exception' call failed");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(res);
|
||||||
|
Py_DECREF(future);
|
||||||
|
Py_DECREF(exc);
|
||||||
|
|
||||||
|
return NXT_UNIT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_http_done(PyObject *self, PyObject *future)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
PyObject *res;
|
||||||
|
nxt_py_asgi_http_t *http;
|
||||||
|
|
||||||
|
http = (nxt_py_asgi_http_t *) self;
|
||||||
|
|
||||||
|
nxt_unit_req_debug(http->req, "asgi_http_done");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get Future.result() and it raises an exception, if coroutine exited
|
||||||
|
* with exception.
|
||||||
|
*/
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_req_error(http->req,
|
||||||
|
"Python failed to call 'future.result()'");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
rc = NXT_UNIT_ERROR;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Py_DECREF(res);
|
||||||
|
|
||||||
|
rc = NXT_UNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nxt_unit_request_done(http->req, rc);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NXT_HAVE_ASGI */
|
||||||
505
src/python/nxt_python_asgi_lifespan.c
Normal file
505
src/python/nxt_python_asgi_lifespan.c
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <python/nxt_python.h>
|
||||||
|
|
||||||
|
#if (NXT_HAVE_ASGI)
|
||||||
|
|
||||||
|
#include <nxt_main.h>
|
||||||
|
#include <python/nxt_python_asgi.h>
|
||||||
|
#include <python/nxt_python_asgi_str.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
int disabled;
|
||||||
|
int startup_received;
|
||||||
|
int startup_sent;
|
||||||
|
int shutdown_received;
|
||||||
|
int shutdown_sent;
|
||||||
|
int shutdown_called;
|
||||||
|
PyObject *startup_future;
|
||||||
|
PyObject *shutdown_future;
|
||||||
|
PyObject *receive_future;
|
||||||
|
} nxt_py_asgi_lifespan_t;
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_send_startup(
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan,
|
||||||
|
int v, int *sent, PyObject **future);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_send_shutdown(
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan, int v, PyObject *dict);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan);
|
||||||
|
static PyObject *nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future);
|
||||||
|
|
||||||
|
|
||||||
|
static nxt_py_asgi_lifespan_t *nxt_py_lifespan;
|
||||||
|
|
||||||
|
static PyMethodDef nxt_py_asgi_lifespan_methods[] = {
|
||||||
|
{ "receive", nxt_py_asgi_lifespan_receive, METH_NOARGS, 0 },
|
||||||
|
{ "send", nxt_py_asgi_lifespan_send, METH_O, 0 },
|
||||||
|
{ "_done", nxt_py_asgi_lifespan_done, METH_O, 0 },
|
||||||
|
{ NULL, NULL, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyAsyncMethods nxt_py_asgi_async_methods = {
|
||||||
|
.am_await = nxt_py_asgi_await,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject nxt_py_asgi_lifespan_type = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
|
||||||
|
.tp_name = "unit._asgi_lifespan",
|
||||||
|
.tp_basicsize = sizeof(nxt_py_asgi_lifespan_t),
|
||||||
|
.tp_dealloc = nxt_py_asgi_dealloc,
|
||||||
|
.tp_as_async = &nxt_py_asgi_async_methods,
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||||
|
.tp_doc = "unit ASGI Lifespan object",
|
||||||
|
.tp_iter = nxt_py_asgi_iter,
|
||||||
|
.tp_iternext = nxt_py_asgi_next,
|
||||||
|
.tp_methods = nxt_py_asgi_lifespan_methods,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_py_asgi_lifespan_startup(nxt_task_t *task)
|
||||||
|
{
|
||||||
|
PyObject *scope, *res, *py_task, *receive, *send, *done;
|
||||||
|
nxt_int_t rc;
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan;
|
||||||
|
|
||||||
|
if (nxt_slow_path(PyType_Ready(&nxt_py_asgi_lifespan_type) != 0)) {
|
||||||
|
nxt_alert(task,
|
||||||
|
"Python failed to initialize the 'asgi_lifespan' type object");
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan = PyObject_New(nxt_py_asgi_lifespan_t, &nxt_py_asgi_lifespan_type);
|
||||||
|
if (nxt_slow_path(lifespan == NULL)) {
|
||||||
|
nxt_alert(task, "Python failed to create lifespan object");
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = NXT_ERROR;
|
||||||
|
|
||||||
|
receive = PyObject_GetAttrString((PyObject *) lifespan, "receive");
|
||||||
|
if (nxt_slow_path(receive == NULL)) {
|
||||||
|
nxt_alert(task, "Python failed to get 'receive' method");
|
||||||
|
goto release_lifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
send = PyObject_GetAttrString((PyObject *) lifespan, "send");
|
||||||
|
if (nxt_slow_path(receive == NULL)) {
|
||||||
|
nxt_alert(task, "Python failed to get 'send' method");
|
||||||
|
goto release_receive;
|
||||||
|
}
|
||||||
|
|
||||||
|
done = PyObject_GetAttrString((PyObject *) lifespan, "_done");
|
||||||
|
if (nxt_slow_path(receive == NULL)) {
|
||||||
|
nxt_alert(task, "Python failed to get '_done' method");
|
||||||
|
goto release_send;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan->startup_future = PyObject_CallObject(nxt_py_loop_create_future,
|
||||||
|
NULL);
|
||||||
|
if (nxt_slow_path(lifespan->startup_future == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Python failed to create Future object");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
goto release_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan->disabled = 0;
|
||||||
|
lifespan->startup_received = 0;
|
||||||
|
lifespan->startup_sent = 0;
|
||||||
|
lifespan->shutdown_received = 0;
|
||||||
|
lifespan->shutdown_sent = 0;
|
||||||
|
lifespan->shutdown_called = 0;
|
||||||
|
lifespan->shutdown_future = NULL;
|
||||||
|
lifespan->receive_future = NULL;
|
||||||
|
|
||||||
|
scope = nxt_py_asgi_new_scope(NULL, nxt_py_lifespan_str, nxt_py_2_0_str);
|
||||||
|
if (nxt_slow_path(scope == NULL)) {
|
||||||
|
goto release_future;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = PyObject_CallFunctionObjArgs(nxt_py_application,
|
||||||
|
scope, receive, send, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_log(task, NXT_LOG_ERR, "Python failed to call the application");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
goto release_scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nxt_slow_path(!PyCoro_CheckExact(res))) {
|
||||||
|
nxt_log(task, NXT_LOG_ERR,
|
||||||
|
"Application result type is not a coroutine");
|
||||||
|
Py_DECREF(res);
|
||||||
|
goto release_scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
py_task = PyObject_CallFunctionObjArgs(nxt_py_loop_create_task, res, NULL);
|
||||||
|
if (nxt_slow_path(py_task == NULL)) {
|
||||||
|
nxt_log(task, NXT_LOG_ERR, "Python failed to call the create_task");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
Py_DECREF(res);
|
||||||
|
goto release_scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
|
||||||
|
res = PyObject_CallMethodObjArgs(py_task, nxt_py_add_done_callback_str,
|
||||||
|
done, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_log(task, NXT_LOG_ERR,
|
||||||
|
"Python failed to call 'task.add_done_callback'");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
goto release_task;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
|
||||||
|
res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete,
|
||||||
|
lifespan->startup_future, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_alert(task, "Python failed to call loop.run_until_complete");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
goto release_task;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
|
||||||
|
if (lifespan->startup_sent == 1 || lifespan->disabled) {
|
||||||
|
nxt_py_lifespan = lifespan;
|
||||||
|
Py_INCREF(nxt_py_lifespan);
|
||||||
|
|
||||||
|
rc = NXT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_task:
|
||||||
|
Py_DECREF(py_task);
|
||||||
|
release_scope:
|
||||||
|
Py_DECREF(scope);
|
||||||
|
release_future:
|
||||||
|
Py_CLEAR(lifespan->startup_future);
|
||||||
|
release_done:
|
||||||
|
Py_DECREF(done);
|
||||||
|
release_send:
|
||||||
|
Py_DECREF(send);
|
||||||
|
release_receive:
|
||||||
|
Py_DECREF(receive);
|
||||||
|
release_lifespan:
|
||||||
|
Py_DECREF(lifespan);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_py_asgi_lifespan_shutdown(void)
|
||||||
|
{
|
||||||
|
PyObject *msg, *future, *res;
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan;
|
||||||
|
|
||||||
|
if (nxt_slow_path(nxt_py_lifespan == NULL || nxt_py_lifespan->disabled)) {
|
||||||
|
return NXT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan = nxt_py_lifespan;
|
||||||
|
lifespan->shutdown_called = 1;
|
||||||
|
|
||||||
|
if (lifespan->receive_future != NULL) {
|
||||||
|
future = lifespan->receive_future;
|
||||||
|
lifespan->receive_future = NULL;
|
||||||
|
|
||||||
|
msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
|
||||||
|
|
||||||
|
if (nxt_fast_path(msg != NULL)) {
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
|
||||||
|
msg, NULL);
|
||||||
|
Py_XDECREF(res);
|
||||||
|
Py_DECREF(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifespan->shutdown_sent) {
|
||||||
|
return NXT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan->shutdown_future = PyObject_CallObject(nxt_py_loop_create_future,
|
||||||
|
NULL);
|
||||||
|
if (nxt_slow_path(lifespan->shutdown_future == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Python failed to create Future object");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = PyObject_CallFunctionObjArgs(nxt_py_loop_run_until_complete,
|
||||||
|
lifespan->shutdown_future, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Python failed to call loop.run_until_complete");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
return NXT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
Py_CLEAR(lifespan->shutdown_future);
|
||||||
|
|
||||||
|
return NXT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_receive(PyObject *self, PyObject *none)
|
||||||
|
{
|
||||||
|
PyObject *msg, *future;
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan;
|
||||||
|
|
||||||
|
lifespan = (nxt_py_asgi_lifespan_t *) self;
|
||||||
|
|
||||||
|
nxt_unit_debug(NULL, "asgi_lifespan_receive");
|
||||||
|
|
||||||
|
future = PyObject_CallObject(nxt_py_loop_create_future, NULL);
|
||||||
|
if (nxt_slow_path(future == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Python failed to create Future object");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_RuntimeError,
|
||||||
|
"failed to create Future object");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lifespan->startup_received) {
|
||||||
|
lifespan->startup_received = 1;
|
||||||
|
|
||||||
|
msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_startup_str);
|
||||||
|
|
||||||
|
return nxt_py_asgi_set_result_soon(NULL, future, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifespan->shutdown_called && !lifespan->shutdown_received) {
|
||||||
|
lifespan->shutdown_received = 1;
|
||||||
|
|
||||||
|
msg = nxt_py_asgi_new_msg(NULL, nxt_py_lifespan_shutdown_str);
|
||||||
|
|
||||||
|
return nxt_py_asgi_set_result_soon(NULL, future, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(future);
|
||||||
|
lifespan->receive_future = future;
|
||||||
|
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_send(PyObject *self, PyObject *dict)
|
||||||
|
{
|
||||||
|
PyObject *type, *msg;
|
||||||
|
const char *type_str;
|
||||||
|
Py_ssize_t type_len;
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan;
|
||||||
|
|
||||||
|
static const nxt_str_t startup_complete
|
||||||
|
= nxt_string("lifespan.startup.complete");
|
||||||
|
static const nxt_str_t startup_failed
|
||||||
|
= nxt_string("lifespan.startup.failed");
|
||||||
|
static const nxt_str_t shutdown_complete
|
||||||
|
= nxt_string("lifespan.shutdown.complete");
|
||||||
|
static const nxt_str_t shutdown_failed
|
||||||
|
= nxt_string("lifespan.shutdown.failed");
|
||||||
|
|
||||||
|
lifespan = (nxt_py_asgi_lifespan_t *) self;
|
||||||
|
|
||||||
|
type = PyDict_GetItem(dict, nxt_py_type_str);
|
||||||
|
if (nxt_slow_path(type == NULL || !PyUnicode_Check(type))) {
|
||||||
|
nxt_unit_error(NULL,
|
||||||
|
"asgi_lifespan_send: 'type' is not a unicode string");
|
||||||
|
return PyErr_Format(PyExc_TypeError,
|
||||||
|
"'type' is not a unicode string");
|
||||||
|
}
|
||||||
|
|
||||||
|
type_str = PyUnicode_AsUTF8AndSize(type, &type_len);
|
||||||
|
|
||||||
|
nxt_unit_debug(NULL, "asgi_lifespan_send type is '%.*s'",
|
||||||
|
(int) type_len, type_str);
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) startup_complete.length
|
||||||
|
&& memcmp(type_str, startup_complete.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
return nxt_py_asgi_lifespan_send_startup(lifespan, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) startup_failed.length
|
||||||
|
&& memcmp(type_str, startup_failed.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
msg = PyDict_GetItem(dict, nxt_py_message_str);
|
||||||
|
return nxt_py_asgi_lifespan_send_startup(lifespan, 1, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) shutdown_complete.length
|
||||||
|
&& memcmp(type_str, shutdown_complete.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
return nxt_py_asgi_lifespan_send_shutdown(lifespan, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_len == (Py_ssize_t) shutdown_failed.length
|
||||||
|
&& memcmp(type_str, shutdown_failed.start, type_len) == 0)
|
||||||
|
{
|
||||||
|
msg = PyDict_GetItem(dict, nxt_py_message_str);
|
||||||
|
return nxt_py_asgi_lifespan_send_shutdown(lifespan, 1, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nxt_py_asgi_lifespan_disable(lifespan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_send_startup(nxt_py_asgi_lifespan_t *lifespan, int v,
|
||||||
|
PyObject *message)
|
||||||
|
{
|
||||||
|
const char *message_str;
|
||||||
|
Py_ssize_t message_len;
|
||||||
|
|
||||||
|
if (nxt_slow_path(v != 0)) {
|
||||||
|
nxt_unit_error(NULL, "Application startup failed");
|
||||||
|
|
||||||
|
if (nxt_fast_path(message != NULL && PyUnicode_Check(message))) {
|
||||||
|
message_str = PyUnicode_AsUTF8AndSize(message, &message_len);
|
||||||
|
|
||||||
|
nxt_unit_error(NULL, "%.*s", (int) message_len, message_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nxt_py_asgi_lifespan_send_(lifespan, v,
|
||||||
|
&lifespan->startup_sent,
|
||||||
|
&lifespan->startup_future);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_send_(nxt_py_asgi_lifespan_t *lifespan, int v, int *sent,
|
||||||
|
PyObject **pfuture)
|
||||||
|
{
|
||||||
|
PyObject *future, *res;
|
||||||
|
|
||||||
|
if (*sent) {
|
||||||
|
return nxt_py_asgi_lifespan_disable(lifespan);
|
||||||
|
}
|
||||||
|
|
||||||
|
*sent = 1 + v;
|
||||||
|
|
||||||
|
if (*pfuture != NULL) {
|
||||||
|
future = *pfuture;
|
||||||
|
*pfuture = NULL;
|
||||||
|
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
|
||||||
|
Py_None, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
|
||||||
|
return nxt_py_asgi_lifespan_disable(lifespan);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(res);
|
||||||
|
Py_DECREF(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_INCREF(lifespan);
|
||||||
|
|
||||||
|
return (PyObject *) lifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_disable(nxt_py_asgi_lifespan_t *lifespan)
|
||||||
|
{
|
||||||
|
nxt_unit_warn(NULL, "Got invalid state transition on lifespan protocol");
|
||||||
|
|
||||||
|
lifespan->disabled = 1;
|
||||||
|
|
||||||
|
return PyErr_Format(PyExc_AssertionError,
|
||||||
|
"Got invalid state transition on lifespan protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_send_shutdown(nxt_py_asgi_lifespan_t *lifespan, int v,
|
||||||
|
PyObject *message)
|
||||||
|
{
|
||||||
|
return nxt_py_asgi_lifespan_send_(lifespan, v,
|
||||||
|
&lifespan->shutdown_sent,
|
||||||
|
&lifespan->shutdown_future);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
nxt_py_asgi_lifespan_done(PyObject *self, PyObject *future)
|
||||||
|
{
|
||||||
|
PyObject *res;
|
||||||
|
nxt_py_asgi_lifespan_t *lifespan;
|
||||||
|
|
||||||
|
nxt_unit_debug(NULL, "asgi_lifespan_done");
|
||||||
|
|
||||||
|
lifespan = (nxt_py_asgi_lifespan_t *) self;
|
||||||
|
|
||||||
|
if (lifespan->startup_sent == 0) {
|
||||||
|
lifespan->disabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get Future.result() and it raises an exception, if coroutine exited
|
||||||
|
* with exception.
|
||||||
|
*/
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_result_str, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_log(NULL, NXT_UNIT_LOG_INFO,
|
||||||
|
"ASGI Lifespan processing exception");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(res);
|
||||||
|
|
||||||
|
if (lifespan->startup_future != NULL) {
|
||||||
|
future = lifespan->startup_future;
|
||||||
|
lifespan->startup_future = NULL;
|
||||||
|
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
|
||||||
|
Py_None, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(res);
|
||||||
|
Py_DECREF(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifespan->shutdown_future != NULL) {
|
||||||
|
future = lifespan->shutdown_future;
|
||||||
|
lifespan->shutdown_future = NULL;
|
||||||
|
|
||||||
|
res = PyObject_CallMethodObjArgs(future, nxt_py_set_result_str,
|
||||||
|
Py_None, NULL);
|
||||||
|
if (nxt_slow_path(res == NULL)) {
|
||||||
|
nxt_unit_alert(NULL, "Failed to call 'future.set_result'");
|
||||||
|
nxt_python_print_exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_XDECREF(res);
|
||||||
|
Py_DECREF(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NXT_HAVE_ASGI */
|
||||||
141
src/python/nxt_python_asgi_str.c
Normal file
141
src/python/nxt_python_asgi_str.c
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <python/nxt_python.h>
|
||||||
|
|
||||||
|
#if (NXT_HAVE_ASGI)
|
||||||
|
|
||||||
|
#include <nxt_main.h>
|
||||||
|
#include <python/nxt_python_asgi_str.h>
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *nxt_py_1_0_str;
|
||||||
|
PyObject *nxt_py_1_1_str;
|
||||||
|
PyObject *nxt_py_2_0_str;
|
||||||
|
PyObject *nxt_py_2_1_str;
|
||||||
|
PyObject *nxt_py_3_0_str;
|
||||||
|
PyObject *nxt_py_add_done_callback_str;
|
||||||
|
PyObject *nxt_py_asgi_str;
|
||||||
|
PyObject *nxt_py_bad_state_str;
|
||||||
|
PyObject *nxt_py_body_str;
|
||||||
|
PyObject *nxt_py_bytes_str;
|
||||||
|
PyObject *nxt_py_client_str;
|
||||||
|
PyObject *nxt_py_code_str;
|
||||||
|
PyObject *nxt_py_done_str;
|
||||||
|
PyObject *nxt_py_exception_str;
|
||||||
|
PyObject *nxt_py_failed_to_send_body_str;
|
||||||
|
PyObject *nxt_py_headers_str;
|
||||||
|
PyObject *nxt_py_http_str;
|
||||||
|
PyObject *nxt_py_http_disconnect_str;
|
||||||
|
PyObject *nxt_py_http_request_str;
|
||||||
|
PyObject *nxt_py_http_version_str;
|
||||||
|
PyObject *nxt_py_https_str;
|
||||||
|
PyObject *nxt_py_lifespan_str;
|
||||||
|
PyObject *nxt_py_lifespan_shutdown_str;
|
||||||
|
PyObject *nxt_py_lifespan_startup_str;
|
||||||
|
PyObject *nxt_py_method_str;
|
||||||
|
PyObject *nxt_py_message_str;
|
||||||
|
PyObject *nxt_py_message_too_big_str;
|
||||||
|
PyObject *nxt_py_more_body_str;
|
||||||
|
PyObject *nxt_py_path_str;
|
||||||
|
PyObject *nxt_py_query_string_str;
|
||||||
|
PyObject *nxt_py_raw_path_str;
|
||||||
|
PyObject *nxt_py_result_str;
|
||||||
|
PyObject *nxt_py_root_path_str;
|
||||||
|
PyObject *nxt_py_scheme_str;
|
||||||
|
PyObject *nxt_py_server_str;
|
||||||
|
PyObject *nxt_py_set_exception_str;
|
||||||
|
PyObject *nxt_py_set_result_str;
|
||||||
|
PyObject *nxt_py_spec_version_str;
|
||||||
|
PyObject *nxt_py_status_str;
|
||||||
|
PyObject *nxt_py_subprotocol_str;
|
||||||
|
PyObject *nxt_py_subprotocols_str;
|
||||||
|
PyObject *nxt_py_text_str;
|
||||||
|
PyObject *nxt_py_type_str;
|
||||||
|
PyObject *nxt_py_version_str;
|
||||||
|
PyObject *nxt_py_websocket_str;
|
||||||
|
PyObject *nxt_py_websocket_accept_str;
|
||||||
|
PyObject *nxt_py_websocket_close_str;
|
||||||
|
PyObject *nxt_py_websocket_connect_str;
|
||||||
|
PyObject *nxt_py_websocket_disconnect_str;
|
||||||
|
PyObject *nxt_py_websocket_receive_str;
|
||||||
|
PyObject *nxt_py_websocket_send_str;
|
||||||
|
PyObject *nxt_py_ws_str;
|
||||||
|
PyObject *nxt_py_wss_str;
|
||||||
|
|
||||||
|
static nxt_python_string_t nxt_py_asgi_strings[] = {
|
||||||
|
{ nxt_string("1.0"), &nxt_py_1_0_str },
|
||||||
|
{ nxt_string("1.1"), &nxt_py_1_1_str },
|
||||||
|
{ nxt_string("2.0"), &nxt_py_2_0_str },
|
||||||
|
{ nxt_string("2.1"), &nxt_py_2_1_str },
|
||||||
|
{ nxt_string("3.0"), &nxt_py_3_0_str },
|
||||||
|
{ nxt_string("add_done_callback"), &nxt_py_add_done_callback_str },
|
||||||
|
{ nxt_string("asgi"), &nxt_py_asgi_str },
|
||||||
|
{ nxt_string("bad state"), &nxt_py_bad_state_str },
|
||||||
|
{ nxt_string("body"), &nxt_py_body_str },
|
||||||
|
{ nxt_string("bytes"), &nxt_py_bytes_str },
|
||||||
|
{ nxt_string("client"), &nxt_py_client_str },
|
||||||
|
{ nxt_string("code"), &nxt_py_code_str },
|
||||||
|
{ nxt_string("done"), &nxt_py_done_str },
|
||||||
|
{ nxt_string("exception"), &nxt_py_exception_str },
|
||||||
|
{ nxt_string("failed to send body"), &nxt_py_failed_to_send_body_str },
|
||||||
|
{ nxt_string("headers"), &nxt_py_headers_str },
|
||||||
|
{ nxt_string("http"), &nxt_py_http_str },
|
||||||
|
{ nxt_string("http.disconnect"), &nxt_py_http_disconnect_str },
|
||||||
|
{ nxt_string("http.request"), &nxt_py_http_request_str },
|
||||||
|
{ nxt_string("http_version"), &nxt_py_http_version_str },
|
||||||
|
{ nxt_string("https"), &nxt_py_https_str },
|
||||||
|
{ nxt_string("lifespan"), &nxt_py_lifespan_str },
|
||||||
|
{ nxt_string("lifespan.shutdown"), &nxt_py_lifespan_shutdown_str },
|
||||||
|
{ nxt_string("lifespan.startup"), &nxt_py_lifespan_startup_str },
|
||||||
|
{ nxt_string("message"), &nxt_py_message_str },
|
||||||
|
{ nxt_string("message too big"), &nxt_py_message_too_big_str },
|
||||||
|
{ nxt_string("method"), &nxt_py_method_str },
|
||||||
|
{ nxt_string("more_body"), &nxt_py_more_body_str },
|
||||||
|
{ nxt_string("path"), &nxt_py_path_str },
|
||||||
|
{ nxt_string("query_string"), &nxt_py_query_string_str },
|
||||||
|
{ nxt_string("raw_path"), &nxt_py_raw_path_str },
|
||||||
|
{ nxt_string("result"), &nxt_py_result_str },
|
||||||
|
{ nxt_string("root_path"), &nxt_py_root_path_str }, // not used
|
||||||
|
{ nxt_string("scheme"), &nxt_py_scheme_str },
|
||||||
|
{ nxt_string("server"), &nxt_py_server_str },
|
||||||
|
{ nxt_string("set_exception"), &nxt_py_set_exception_str },
|
||||||
|
{ nxt_string("set_result"), &nxt_py_set_result_str },
|
||||||
|
{ nxt_string("spec_version"), &nxt_py_spec_version_str },
|
||||||
|
{ nxt_string("status"), &nxt_py_status_str },
|
||||||
|
{ nxt_string("subprotocol"), &nxt_py_subprotocol_str },
|
||||||
|
{ nxt_string("subprotocols"), &nxt_py_subprotocols_str },
|
||||||
|
{ nxt_string("text"), &nxt_py_text_str },
|
||||||
|
{ nxt_string("type"), &nxt_py_type_str },
|
||||||
|
{ nxt_string("version"), &nxt_py_version_str },
|
||||||
|
{ nxt_string("websocket"), &nxt_py_websocket_str },
|
||||||
|
{ nxt_string("websocket.accept"), &nxt_py_websocket_accept_str },
|
||||||
|
{ nxt_string("websocket.close"), &nxt_py_websocket_close_str },
|
||||||
|
{ nxt_string("websocket.connect"), &nxt_py_websocket_connect_str },
|
||||||
|
{ nxt_string("websocket.disconnect"), &nxt_py_websocket_disconnect_str },
|
||||||
|
{ nxt_string("websocket.receive"), &nxt_py_websocket_receive_str },
|
||||||
|
{ nxt_string("websocket.send"), &nxt_py_websocket_send_str },
|
||||||
|
{ nxt_string("ws"), &nxt_py_ws_str },
|
||||||
|
{ nxt_string("wss"), &nxt_py_wss_str },
|
||||||
|
{ nxt_null_string, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t
|
||||||
|
nxt_py_asgi_str_init(void)
|
||||||
|
{
|
||||||
|
return nxt_python_init_strings(nxt_py_asgi_strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
nxt_py_asgi_str_done(void)
|
||||||
|
{
|
||||||
|
nxt_python_done_strings(nxt_py_asgi_strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NXT_HAVE_ASGI */
|
||||||
69
src/python/nxt_python_asgi_str.h
Normal file
69
src/python/nxt_python_asgi_str.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) NGINX, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NXT_PYTHON_ASGI_STR_H_INCLUDED_
|
||||||
|
#define _NXT_PYTHON_ASGI_STR_H_INCLUDED_
|
||||||
|
|
||||||
|
|
||||||
|
extern PyObject *nxt_py_1_0_str;
|
||||||
|
extern PyObject *nxt_py_1_1_str;
|
||||||
|
extern PyObject *nxt_py_2_0_str;
|
||||||
|
extern PyObject *nxt_py_2_1_str;
|
||||||
|
extern PyObject *nxt_py_3_0_str;
|
||||||
|
extern PyObject *nxt_py_add_done_callback_str;
|
||||||
|
extern PyObject *nxt_py_asgi_str;
|
||||||
|
extern PyObject *nxt_py_bad_state_str;
|
||||||
|
extern PyObject *nxt_py_body_str;
|
||||||
|
extern PyObject *nxt_py_bytes_str;
|
||||||
|
extern PyObject *nxt_py_client_str;
|
||||||
|
extern PyObject *nxt_py_code_str;
|
||||||
|
extern PyObject *nxt_py_done_str;
|
||||||
|
extern PyObject *nxt_py_exception_str;
|
||||||
|
extern PyObject *nxt_py_failed_to_send_body_str;
|
||||||
|
extern PyObject *nxt_py_headers_str;
|
||||||
|
extern PyObject *nxt_py_http_str;
|
||||||
|
extern PyObject *nxt_py_http_disconnect_str;
|
||||||
|
extern PyObject *nxt_py_http_request_str;
|
||||||
|
extern PyObject *nxt_py_http_version_str;
|
||||||
|
extern PyObject *nxt_py_https_str;
|
||||||
|
extern PyObject *nxt_py_lifespan_str;
|
||||||
|
extern PyObject *nxt_py_lifespan_shutdown_str;
|
||||||
|
extern PyObject *nxt_py_lifespan_startup_str;
|
||||||
|
extern PyObject *nxt_py_method_str;
|
||||||
|
extern PyObject *nxt_py_message_str;
|
||||||
|
extern PyObject *nxt_py_message_too_big_str;
|
||||||
|
extern PyObject *nxt_py_more_body_str;
|
||||||
|
extern PyObject *nxt_py_path_str;
|
||||||
|
extern PyObject *nxt_py_query_string_str;
|
||||||
|
extern PyObject *nxt_py_result_str;
|
||||||
|
extern PyObject *nxt_py_raw_path_str;
|
||||||
|
extern PyObject *nxt_py_root_path_str;
|
||||||
|
extern PyObject *nxt_py_scheme_str;
|
||||||
|
extern PyObject *nxt_py_server_str;
|
||||||
|
extern PyObject *nxt_py_set_exception_str;
|
||||||
|
extern PyObject *nxt_py_set_result_str;
|
||||||
|
extern PyObject *nxt_py_spec_version_str;
|
||||||
|
extern PyObject *nxt_py_status_str;
|
||||||
|
extern PyObject *nxt_py_subprotocol_str;
|
||||||
|
extern PyObject *nxt_py_subprotocols_str;
|
||||||
|
extern PyObject *nxt_py_text_str;
|
||||||
|
extern PyObject *nxt_py_type_str;
|
||||||
|
extern PyObject *nxt_py_version_str;
|
||||||
|
extern PyObject *nxt_py_websocket_str;
|
||||||
|
extern PyObject *nxt_py_websocket_accept_str;
|
||||||
|
extern PyObject *nxt_py_websocket_close_str;
|
||||||
|
extern PyObject *nxt_py_websocket_connect_str;
|
||||||
|
extern PyObject *nxt_py_websocket_disconnect_str;
|
||||||
|
extern PyObject *nxt_py_websocket_receive_str;
|
||||||
|
extern PyObject *nxt_py_websocket_send_str;
|
||||||
|
extern PyObject *nxt_py_ws_str;
|
||||||
|
extern PyObject *nxt_py_wss_str;
|
||||||
|
|
||||||
|
|
||||||
|
nxt_int_t nxt_py_asgi_str_init(void);
|
||||||
|
void nxt_py_asgi_str_done(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _NXT_PYTHON_ASGI_STR_H_INCLUDED_ */
|
||||||
1084
src/python/nxt_python_asgi_websocket.c
Normal file
1084
src/python/nxt_python_asgi_websocket.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -38,24 +38,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION == 3
|
|
||||||
#define NXT_PYTHON_BYTES_TYPE "bytestring"
|
|
||||||
|
|
||||||
#define PyString_FromStringAndSize(str, size) \
|
|
||||||
PyUnicode_DecodeLatin1((str), (size), "strict")
|
|
||||||
#define PyString_AS_STRING PyUnicode_DATA
|
|
||||||
|
|
||||||
#else
|
|
||||||
#define NXT_PYTHON_BYTES_TYPE "string"
|
|
||||||
|
|
||||||
#define PyBytes_FromStringAndSize PyString_FromStringAndSize
|
|
||||||
#define PyBytes_Check PyString_Check
|
|
||||||
#define PyBytes_GET_SIZE PyString_GET_SIZE
|
|
||||||
#define PyBytes_AS_STRING PyString_AS_STRING
|
|
||||||
#define PyUnicode_InternInPlace PyString_InternInPlace
|
|
||||||
#define PyUnicode_AsUTF8 PyString_AS_STRING
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t;
|
typedef struct nxt_python_run_ctx_s nxt_python_run_ctx_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user