diff options
author | Max Romanov <max.romanov@nginx.com> | 2017-06-23 19:20:08 +0300 |
---|---|---|
committer | Max Romanov <max.romanov@nginx.com> | 2017-06-23 19:20:08 +0300 |
commit | fa6582d9ad581451c8406ec2022b5df23676d0bb (patch) | |
tree | 8a5803282adfef732ffa75e302d720697c41916a | |
parent | e7a0634a718ca1f2379f4694c17ef4219f5538fa (diff) | |
download | unit-fa6582d9ad581451c8406ec2022b5df23676d0bb.tar.gz unit-fa6582d9ad581451c8406ec2022b5df23676d0bb.tar.bz2 |
Python app request processing.
Diffstat (limited to '')
-rw-r--r-- | auto/modules/python/conf | 26 | ||||
-rw-r--r-- | auto/modules/python/make | 4 | ||||
-rw-r--r-- | src/nxt_python_wsgi.c | 553 |
3 files changed, 328 insertions, 255 deletions
diff --git a/auto/modules/python/conf b/auto/modules/python/conf index 0d9fb1c9..7ab29857 100644 --- a/auto/modules/python/conf +++ b/auto/modules/python/conf @@ -1,33 +1,29 @@ -# Copyright (C) NGINX, Inc. # Copyright (C) Valentin V. Bartenev +# Copyright (C) NGINX, Inc. -NXT_PYTHON_VERSION=`${NXT_PYTHON} -c \ - 'import sysconfig, sys; \ - sys.stdout.write(sysconfig.get_python_version())'` +NXT_PYTHON_CONFIG="${NXT_PYTHON}-config" -NXT_PYTHON_INCLUDE=`${NXT_PYTHON} -c \ - 'import sysconfig, sys; \ - sys.stdout.write(sysconfig.get_config_var("INCLUDEPY"))'` +NXT_PYTHON_VERSION=`${NXT_PYTHON} -c \ + 'import sys; \ + sys.stdout.write(sys.version[:3])'` -NXT_PYTHON_LIB="-lpython${NXT_PYTHON_VERSION}" +NXT_PYTHON_INCLUDE=`${NXT_PYTHON_CONFIG} --includes` -NXT_PYTHON_LIBS=`${NXT_PYTHON} -c \ - 'import sysconfig, sys; \ - sys.stdout.write(sysconfig.get_config_var("SYSLIBS") \ - + " " + sysconfig.get_config_var("LIBS"))'` +NXT_PYTHON_LIBS=`${NXT_PYTHON_CONFIG} --ldflags` nxt_feature="Python" nxt_feature_name=NXT_HAVE_PYTHON nxt_feature_run=no -nxt_feature_incs="-I${NXT_PYTHON_INCLUDE}" -nxt_feature_libs="$NXT_PYTHON_LIB $NXT_PYTHON_LIBS" +nxt_feature_incs="${NXT_PYTHON_INCLUDE}" +nxt_feature_libs="${NXT_PYTHON_LIBS}" nxt_feature_test="#include <Python.h> int main() { Py_Initialize(); + return 0; }" . auto/feature @@ -51,4 +47,4 @@ NXT_PYTHON_MODULE_SRCS=" \ NXT_MODULES_INIT="$NXT_MODULES_INIT nxt_python_wsgi_init" NXT_MODULES_SRCS="$NXT_MODULES_SRCS $NXT_PYTHON_MODULE_SRCS" -NXT_LIB_AUX_LIBS="$NXT_LIB_AUX_LIBS $NXT_PYTHON_LIB $NXT_PYTHON_LIBS" +NXT_LIB_AUX_LIBS="$NXT_LIB_AUX_LIBS $NXT_PYTHON_LIBS" diff --git a/auto/modules/python/make b/auto/modules/python/make index edb8f0c0..b405a03a 100644 --- a/auto/modules/python/make +++ b/auto/modules/python/make @@ -1,6 +1,6 @@ -# Copyright (C) NGINX, Inc. # Copyright (C) Valentin V. Bartenev +# Copyright (C) NGINX, Inc. $echo >> $NXT_MAKEFILE @@ -15,7 +15,7 @@ do cat << END >> $NXT_MAKEFILE $NXT_BUILD_DIR/$nxt_obj: $nxt_src - \$(CC) -c \$(CFLAGS) \$(NXT_INCS) -I $NXT_PYTHON_INCLUDE \\ + \$(CC) -c \$(CFLAGS) \$(NXT_INCS) $NXT_PYTHON_INCLUDE \\ $NXT_LIB_AUX_CFLAGS \\ -o $NXT_BUILD_DIR/$nxt_obj \\ $nxt_src diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index da2b4d76..70253b2c 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -1,5 +1,6 @@ /* + * Copyright (C) Max Romanov * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. */ @@ -14,6 +15,36 @@ #include <nxt_runtime.h> #include <nxt_application.h> +/* + * According to "PEP 3333 / A Note On String Types" + * [https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types] + * + * WSGI therefore defines two kinds of "string": + * + * - "Native" strings (which are always implemented using the type named str ) + * that are used for request/response headers and metadata + * + * will use PyString_* or corresponding PyUnicode_* functions + * + * - "Bytestrings" (which are implemented using the bytes type in Python 3, and + * str elsewhere), that are used for the bodies of requests and responses + * (e.g. POST/PUT input data and HTML page outputs). + * + * will use PyString_* or corresponding PyBytes_* functions + */ + + +#if PY_MAJOR_VERSION == 3 +#define PyString_FromString PyUnicode_FromString +#define PyString_FromStringAndSize PyUnicode_FromStringAndSize +#else +#define PyBytes_FromString PyString_FromString +#define PyBytes_FromStringAndSize PyString_FromStringAndSize +#define PyBytes_Check PyString_Check +#define PyBytes_GET_SIZE PyString_GET_SIZE +#define PyBytes_AS_STRING PyString_AS_STRING +#endif + typedef struct { PyObject_HEAD @@ -27,11 +58,17 @@ typedef struct { } nxt_py_error_t; -static nxt_int_t nxt_python_init(nxt_thread_t *thr); -static nxt_int_t nxt_python_run(nxt_app_request_t *r); +static nxt_int_t nxt_python_init(nxt_task_t *task); + +static nxt_int_t nxt_python_prepare_msg(nxt_task_t *task, + nxt_app_request_t *r, nxt_app_wmsg_t *msg); + +static nxt_int_t nxt_python_run(nxt_task_t *task, + nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *msg); static PyObject *nxt_python_create_environ(nxt_thread_t *thr); -static PyObject *nxt_python_get_environ(nxt_app_request_t *r); +static PyObject *nxt_python_get_environ(nxt_task_t *task, + nxt_app_rmsg_t *rmsg); static PyObject *nxt_py_start_resp(PyObject *self, PyObject *args); @@ -40,15 +77,26 @@ static PyObject *nxt_py_input_read(nxt_py_input_t *self, PyObject *args); static PyObject *nxt_py_input_readline(nxt_py_input_t *self, PyObject *args); static PyObject *nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args); +typedef struct { + nxt_task_t *task; + nxt_app_rmsg_t *rmsg; + nxt_app_wmsg_t *wmsg; +} nxt_python_run_ctx_t; + +nxt_inline nxt_int_t nxt_python_write(nxt_python_run_ctx_t *ctx, + const u_char *data, size_t len, + nxt_bool_t flush, nxt_bool_t last); + +nxt_inline nxt_int_t nxt_python_write_py_str(nxt_python_run_ctx_t *ctx, + PyObject *str, nxt_bool_t flush, nxt_bool_t last); extern nxt_int_t nxt_python_wsgi_init(nxt_thread_t *thr, nxt_runtime_t *rt); nxt_application_module_t nxt_python_module = { nxt_python_init, - NULL, - NULL, - nxt_python_run, + nxt_python_prepare_msg, + nxt_python_run }; @@ -113,6 +161,9 @@ static PyTypeObject nxt_py_input_type = { 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 3 + 0, /* tp_finalize */ +#endif }; @@ -122,7 +173,9 @@ static PyObject *nxt_py_application; static PyObject *nxt_py_start_resp_obj; static PyObject *nxt_py_environ_ptyp; -static nxt_app_request_t *nxt_app_request; +static nxt_str_t nxt_python_request_body; + +static nxt_python_run_ctx_t *nxt_python_run_ctx; nxt_int_t @@ -254,7 +307,7 @@ fail: static nxt_int_t -nxt_python_init(nxt_thread_t *thr) +nxt_python_init(nxt_task_t *task) { PyObject *module, *obj; @@ -304,22 +357,24 @@ nxt_python_init(nxt_thread_t *thr) module = PyImport_ImportModule(nxt_py_module); if (nxt_slow_path(module == NULL)) { - nxt_log_emerg(thr->log, "Python failed to import module \"%s\"", + nxt_log_emerg(task->log, "Python failed to import module \"%s\"", nxt_py_module); + PyErr_PrintEx(1); return NXT_ERROR; } obj = PyDict_GetItemString(PyModule_GetDict(module), "application"); if (nxt_slow_path(obj == NULL)) { - nxt_log_emerg(thr->log, "Python failed to get \"application\" " - "from module \"%s\"", nxt_py_module); + nxt_log_emerg(task->log, "Python failed to get \"application\" " + "from module \"%s\"", nxt_py_module); goto fail; } if (nxt_slow_path(PyCallable_Check(obj) == 0)) { - nxt_log_emerg(thr->log, "\"application\" in module \"%s\" " - "is not a callable object", nxt_py_module); + nxt_log_emerg(task->log, "\"application\" in module \"%s\" " + "is not a callable object", nxt_py_module); + PyErr_PrintEx(1); goto fail; } @@ -339,15 +394,77 @@ fail: static nxt_int_t -nxt_python_run(nxt_app_request_t *r) +nxt_python_prepare_msg(nxt_task_t *task, nxt_app_request_t *r, + nxt_app_wmsg_t *wmsg) +{ + nxt_int_t rc; + nxt_http_field_t *field; + nxt_app_request_header_t *h; + + static const nxt_str_t prefix = nxt_string("HTTP_"); + static const nxt_str_t eof = nxt_null_string; + + h = &r->header; + +#define RC(S) \ + do { \ + rc = (S); \ + if (nxt_slow_path(rc != NXT_OK)) { \ + goto fail; \ + } \ + } while(0) + +#define NXT_WRITE(N) \ + RC(nxt_app_msg_write_str(task, wmsg, N)) + + /* TODO error handle, async mmap buffer assignment */ + + NXT_WRITE(&h->method); + NXT_WRITE(&h->path); + + if (h->query.start != NULL) { + RC(nxt_app_msg_write_size(task, wmsg, + h->query.start - h->path.start + 1)); + } else { + RC(nxt_app_msg_write_size(task, wmsg, 0)); + } + + NXT_WRITE(&h->version); + + NXT_WRITE(&h->content_type); + NXT_WRITE(&h->content_length); + + nxt_list_each(field, h->fields) { + RC(nxt_app_msg_write_prefixed_upcase(task, wmsg, + &prefix, &field->name)); + NXT_WRITE(&field->value); + + } nxt_list_loop; + + /* end-of-headers mark */ + NXT_WRITE(&eof); + NXT_WRITE(&r->body.preread); + +#undef NXT_WRITE +#undef RC + + return NXT_OK; + +fail: + + return NXT_ERROR; +} + + +static nxt_int_t +nxt_python_run(nxt_task_t *task, nxt_app_rmsg_t *rmsg, nxt_app_wmsg_t *wmsg) { u_char *buf; size_t size; PyObject *result, *iterator, *item, *args, *environ; + nxt_python_run_ctx_t run_ctx = {task, rmsg, wmsg}; - nxt_app_request = r; - - environ = nxt_python_get_environ(r); + environ = nxt_python_get_environ(task, rmsg); if (nxt_slow_path(environ == NULL)) { return NXT_ERROR; @@ -356,11 +473,13 @@ nxt_python_run(nxt_app_request_t *r) args = PyTuple_New(2); if (nxt_slow_path(args == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, + nxt_log_error(NXT_LOG_ERR, task->log, "Python failed to create arguments tuple"); return NXT_ERROR; } + nxt_python_run_ctx = &run_ctx; + PyTuple_SET_ITEM(args, 0, environ); Py_INCREF(nxt_py_start_resp_obj); @@ -370,8 +489,10 @@ nxt_python_run(nxt_app_request_t *r) Py_DECREF(args); + nxt_python_run_ctx = NULL; + if (nxt_slow_path(result == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, + nxt_log_error(NXT_LOG_ERR, task->log, "Python failed to call the application"); PyErr_Print(); return NXT_ERROR; @@ -379,19 +500,21 @@ nxt_python_run(nxt_app_request_t *r) iterator = PyObject_GetIter(result); + /* TODO call result.close() */ + Py_DECREF(result); if (nxt_slow_path(iterator == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, + nxt_log_error(NXT_LOG_ERR, task->log, "the application returned not an iterable object"); return NXT_ERROR; } while((item = PyIter_Next(iterator))) { - if (nxt_slow_path(PyString_Check(item) == 0)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "the application returned not a string object"); + if (nxt_slow_path(PyBytes_Check(item) == 0)) { + nxt_log_error(NXT_LOG_ERR, task->log, + "the application returned not a bytestring object"); Py_DECREF(item); Py_DECREF(iterator); @@ -399,18 +522,22 @@ nxt_python_run(nxt_app_request_t *r) return NXT_ERROR; } - size = PyString_GET_SIZE(item); - buf = (u_char *) PyString_AS_STRING(item); + size = PyBytes_GET_SIZE(item); + buf = (u_char *) PyBytes_AS_STRING(item); - nxt_app_write(r, buf, size); + nxt_debug(task, "nxt_app_write(fake): %d %*s", (int)size, (int)size, + buf); + nxt_python_write(&run_ctx, buf, size, 1, 0); Py_DECREF(item); } Py_DECREF(iterator); + nxt_python_write(&run_ctx, NULL, 0, 1, 1); + if (nxt_slow_path(PyErr_Occurred() != NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, "an application error occurred"); + nxt_log_error(NXT_LOG_ERR, task->log, "an application error occurred"); PyErr_Print(); return NXT_ERROR; } @@ -581,223 +708,127 @@ fail: return NULL; } - -static PyObject * -nxt_python_get_environ(nxt_app_request_t *r) +nxt_inline nxt_int_t +nxt_python_add_env(nxt_task_t *task, PyObject *env, const char *name, + nxt_str_t *v) { - u_char *p, ch, *query; - nxt_str_t *str; - nxt_uint_t i, n; - nxt_app_header_field_t *fld; - - PyObject *environ, *value; - - static const u_char prefix[5] = "HTTP_"; - - static u_char key[256]; - - environ = PyDict_Copy(nxt_py_environ_ptyp); - - if (nxt_slow_path(environ == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"environ\" dictionary"); - return NULL; - } - - value = PyString_FromStringAndSize((char *) r->header.version.start, - r->header.version.length); + PyObject *value; + nxt_int_t rc; + value = PyString_FromStringAndSize((char *) v->start, v->length); if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"SERVER_PROTOCOL\" environ value"); - goto fail; + nxt_log_error(NXT_LOG_ERR, task->log, + "Python failed to create value string \"%V\"", v); + return NXT_ERROR; } - if (nxt_slow_path(PyDict_SetItemString(environ, "SERVER_PROTOCOL", value) + if (nxt_slow_path(PyDict_SetItemString(env, name, value) != 0)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"SERVER_PROTOCOL\" environ value"); - goto fail; + nxt_log_error(NXT_LOG_ERR, task->log, + "Python failed to set the \"%s\" environ value", name); + rc = NXT_ERROR; + } else { + rc = NXT_OK; } Py_DECREF(value); - value = PyString_FromStringAndSize((char *) r->header.method.start, - r->header.method.length); - - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"REQUEST_METHOD\" environ value"); - goto fail; - } - - if (nxt_slow_path(PyDict_SetItemString(environ, "REQUEST_METHOD", value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"REQUEST_METHOD\" environ value"); - goto fail; - } + return rc; +} - Py_DECREF(value); - value = PyString_FromStringAndSize((char *) r->header.path.start, - r->header.path.length); +nxt_inline nxt_int_t +nxt_python_read_add_env(nxt_task_t *task, nxt_app_rmsg_t *rmsg, + PyObject *env, const char *name, nxt_str_t *v) +{ + nxt_int_t rc; - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"REQUEST_URI\" environ value"); - goto fail; + rc = nxt_app_msg_read_str(task, rmsg, v); + if (nxt_slow_path(rc != NXT_OK)) { + return rc; } - if (nxt_slow_path(PyDict_SetItemString(environ, "REQUEST_URI", value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"REQUEST_URI\" environ value"); - goto fail; + if (v->start == NULL) { + return NXT_OK; } - Py_DECREF(value); - - query = nxt_memchr(r->header.path.start, '?', r->header.path.length); + return nxt_python_add_env(task, env, name, v); +} - if (query != NULL) { - value = PyString_FromStringAndSize((char *) r->header.path.start, - query - r->header.path.start); - query++; +static PyObject * +nxt_python_get_environ(nxt_task_t *task, nxt_app_rmsg_t *rmsg) +{ + size_t s; + PyObject *environ; + nxt_int_t rc; + nxt_str_t n, v, path_no_query, query; - } else { - value = PyString_FromStringAndSize((char *) r->header.path.start, - r->header.path.length); - } + environ = PyDict_Copy(nxt_py_environ_ptyp); - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"PATH_INFO\" environ value"); - goto fail; + if (nxt_slow_path(environ == NULL)) { + nxt_log_error(NXT_LOG_ERR, task->log, + "Python failed to create the \"environ\" dictionary"); + return NULL; } - if (nxt_slow_path(PyDict_SetItemString(environ, "PATH_INFO", value) != 0)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"PATH_INFO\" environ value"); - goto fail; - } +#define RC(S) \ + do { \ + rc = (S); \ + if (nxt_slow_path(rc != NXT_OK)) { \ + goto fail; \ + } \ + } while(0) - Py_DECREF(value); +#define NXT_READ(N) \ + RC(nxt_python_read_add_env(task, rmsg, environ, N, &v)) - if (query != NULL) { - value = PyString_FromStringAndSize((char *) query, - r->header.path.start - + r->header.path.length - query); + NXT_READ("REQUEST_METHOD"); + NXT_READ("REQUEST_URI"); - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"QUERY_STRING\" environ value"); - goto fail; - } + path_no_query = v; // assume no query + RC(nxt_app_msg_read_size(task, rmsg, &s)); // query length + 1 + if (s > 0) { + s--; - if (nxt_slow_path(PyDict_SetItemString(environ, "QUERY_STRING", value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"QUERY_STRING\" environ value"); - goto fail; - } + query.start = path_no_query.start + s; + query.length = path_no_query.length - s; - Py_DECREF(value); - } + RC(nxt_python_add_env(task, environ, "QUERY_STRING", &query)); - if (r->header.content_length != NULL) { - str = r->header.content_length; - - value = PyString_FromStringAndSize((char *) str->start, str->length); - - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"CONTENT_LENGTH\" environ value"); - goto fail; + if (s > 0) { + path_no_query.length = s - 1; } - - if (nxt_slow_path(PyDict_SetItemString(environ, "CONTENT_LENGTH", value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"CONTENT_LENGTH\" environ value"); - goto fail; - } - - Py_DECREF(value); } - if (r->header.content_type != NULL) { - str = r->header.content_type; + RC(nxt_python_add_env(task, environ, "PATH_INFO", &path_no_query)); - value = PyString_FromStringAndSize((char *) str->start, str->length); + NXT_READ("SERVER_PROTOCOL"); - if (nxt_slow_path(value == NULL)) { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to create the \"CONTENT_TYPE\" environ value"); - goto fail; - } + NXT_READ("CONTENT_TYPE"); + NXT_READ("CONTENT_LENGTH"); - if (nxt_slow_path(PyDict_SetItemString(environ, "CONTENT_TYPE", value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"CONTENT_TYPE\" environ value"); - goto fail; + while ( (rc = nxt_app_msg_read_nvp(task, rmsg, &n, &v)) == NXT_OK) { + if (nxt_slow_path(n.length == 0)) { + rc = NXT_DONE; + break; } - Py_DECREF(value); + RC(nxt_python_add_env(task, environ, (char *) n.start, &v)); } - nxt_memcpy(key, prefix, sizeof(prefix)); - - for (i = 0; i < r->header.fields_num; i++) { - fld = &r->header.fields[i]; - p = key + sizeof(prefix); - - for (n = 0; n < fld->name.length; n++, p++) { - - ch = fld->name.start[n]; +#undef NXT_READ +#undef RC - if (ch >= 'a' && ch <= 'z') { - *p = ch & ~0x20; - continue; - } - - if (ch == '-') { - *p = '_'; - continue; - } - - *p = ch; - } - - *p = '\0'; - - value = PyString_FromStringAndSize((char *) fld->value.start, - fld->value.length); - - if (nxt_slow_path(PyDict_SetItemString(environ, (char *) key, value) - != 0)) - { - nxt_log_error(NXT_LOG_ERR, r->log, - "Python failed to set the \"%s\" environ value", key); - goto fail; - } - - Py_DECREF(value); + if (rc == NXT_DONE && v.length > 0) { + nxt_python_request_body = v; } return environ; fail: - Py_XDECREF(value); Py_DECREF(environ); return NULL; @@ -807,10 +838,10 @@ fail: static PyObject * nxt_py_start_resp(PyObject *self, PyObject *args) { - u_char *p, buf[4096]; PyObject *headers, *tuple, *string; - nxt_str_t str; + nxt_int_t rc; nxt_uint_t i, n; + nxt_python_run_ctx_t *ctx; static const u_char resp[] = "HTTP/1.1 "; @@ -818,6 +849,9 @@ nxt_py_start_resp(PyObject *self, PyObject *args) = "Server: nginext/0.1\r\n" "Connection: close\r\n"; + static const u_char cr_lf[] = "\r\n"; + static const u_char sc_sp[] = ": "; + n = PyTuple_GET_SIZE(args); if (n < 2 || n > 3) { @@ -826,20 +860,19 @@ nxt_py_start_resp(PyObject *self, PyObject *args) string = PyTuple_GET_ITEM(args, 0); - if (!PyString_Check(string)) { - return PyErr_Format(PyExc_TypeError, - "the first argument is not a string"); - } + ctx = nxt_python_run_ctx; - str.length = PyString_GET_SIZE(string); - str.start = (u_char *) PyString_AS_STRING(string); + nxt_python_write(ctx, resp, sizeof(resp) - 1, 0, 0); - p = nxt_cpymem(buf, resp, sizeof(resp) - 1); - p = nxt_cpymem(p, str.start, str.length); + rc = nxt_python_write_py_str(ctx, string, 0, 0); + if (nxt_slow_path(rc != NXT_OK)) { + return PyErr_Format(PyExc_TypeError, + "failed to write first argument (not a string?)"); + } - *p++ = '\r'; *p++ = '\n'; + nxt_python_write(ctx, cr_lf, sizeof(cr_lf) - 1, 0, 0); - p = nxt_cpymem(p, default_headers, sizeof(default_headers) - 1); + nxt_python_write(ctx, default_headers, sizeof(default_headers) - 1, 0, 0); headers = PyTuple_GET_ITEM(args, 1); @@ -863,36 +896,29 @@ nxt_py_start_resp(PyObject *self, PyObject *args) string = PyTuple_GET_ITEM(tuple, 0); - if (!PyString_Check(string)) { + rc = nxt_python_write_py_str(ctx, string, 0, 0); + if (nxt_slow_path(rc != NXT_OK)) { return PyErr_Format(PyExc_TypeError, - "all response headers names must be strings"); + "failed to write response header name" + " (not a string?)"); } - str.length = PyString_GET_SIZE(string); - str.start = (u_char *) PyString_AS_STRING(string); - - p = nxt_cpymem(p, str.start, str.length); - - *p++ = ':'; *p++ = ' '; + nxt_python_write(ctx, sc_sp, sizeof(sc_sp) - 1, 0, 0); string = PyTuple_GET_ITEM(tuple, 1); - if (!PyString_Check(string)) { + rc = nxt_python_write_py_str(ctx, string, 0, 0); + if (nxt_slow_path(rc != NXT_OK)) { return PyErr_Format(PyExc_TypeError, - "all response headers values must be strings"); + "failed to write response header value" + " (not a string?)"); } - str.length = PyString_GET_SIZE(string); - str.start = (u_char *) PyString_AS_STRING(string); - - p = nxt_cpymem(p, str.start, str.length); - - *p++ = '\r'; *p++ = '\n'; + nxt_python_write(ctx, cr_lf, sizeof(cr_lf) - 1, 0, 0); } - *p++ = '\r'; *p++ = '\n'; - - nxt_app_write(nxt_app_request, buf, p - buf); + /* flush headers */ + nxt_python_write(ctx, cr_lf, sizeof(cr_lf) - 1, 1, 0); return args; } @@ -913,9 +939,7 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) Py_ssize_t size; nxt_uint_t n; - nxt_app_request_t *r = nxt_app_request; - - size = r->body_rest; + size = nxt_python_request_body.length; n = PyTuple_GET_SIZE(args); @@ -937,21 +961,26 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) "the read body size cannot be zero or less"); } - if (size == 0 || size > r->body_rest) { - size = r->body_rest; + if (size == 0 || size > (Py_ssize_t) nxt_python_request_body.length) { + size = nxt_python_request_body.length; } } - body = PyString_FromStringAndSize(NULL, size); + body = PyBytes_FromStringAndSize(NULL, size); if (nxt_slow_path(body == NULL)) { return NULL; } - buf = (u_char *) PyString_AS_STRING(body); + if (size > 0) { + buf = (u_char *) PyBytes_AS_STRING(body); + + nxt_memcpy(buf, nxt_python_request_body.start, size); - if (nxt_app_http_read_body(r, buf, size) != NXT_OK) { - return PyErr_Format(PyExc_IOError, "failed to read body"); + nxt_python_request_body.start += size; + nxt_python_request_body.length -= size; + + /* TODO wait body */ } return body; @@ -961,7 +990,7 @@ nxt_py_input_read(nxt_py_input_t *self, PyObject *args) static PyObject * nxt_py_input_readline(nxt_py_input_t *self, PyObject *args) { - return PyString_FromString(""); + return PyBytes_FromString(""); } @@ -970,3 +999,51 @@ nxt_py_input_readlines(nxt_py_input_t *self, PyObject *args) { return PyList_New(0); } + + +nxt_inline nxt_int_t +nxt_python_write(nxt_python_run_ctx_t *ctx, const u_char *data, size_t len, + nxt_bool_t flush, nxt_bool_t last) +{ + nxt_int_t rc; + + rc = nxt_app_msg_write_raw(ctx->task, ctx->wmsg, data, len); + + if (flush || last) { + rc = nxt_app_msg_flush(ctx->task, ctx->wmsg, last); + } + + return rc; +} + + +nxt_inline nxt_int_t +nxt_python_write_py_str(nxt_python_run_ctx_t *ctx, PyObject *str, + nxt_bool_t flush, nxt_bool_t last) +{ + PyObject *bytes; + nxt_int_t rc; + + rc = NXT_OK; + + if (PyBytes_Check(str)) { + rc = nxt_python_write(ctx, (u_char *) PyBytes_AS_STRING(str), + PyBytes_GET_SIZE(str), flush, last); + } else { + if (!PyUnicode_Check(str)) { + return NXT_ERROR; + } + + bytes = PyUnicode_AsLatin1String(str); + if (nxt_slow_path(bytes == NULL)) { + return NXT_ERROR; + } + + rc = nxt_python_write(ctx, (u_char *) PyBytes_AS_STRING(bytes), + PyBytes_GET_SIZE(bytes), flush, last); + + Py_DECREF(bytes); + } + + return rc; +} |