better implementation of start_response()

This commit is contained in:
roberto@precise64
2012-05-02 09:30:37 +02:00
parent 5ccf2254eb
commit c39cf848de
6 changed files with 96 additions and 36 deletions
+2
View File
@@ -127,6 +127,8 @@ struct uwsgi_option uwsgi_python_options[] = {
{"py-auto-reload-ignore", required_argument, 0, "ignore the specified module during auto-reload scan (can be specified multiple times)", uwsgi_opt_add_string_list, &up.auto_reload_ignore, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
#endif
{"start_response-nodelay", no_argument, 0, "send WSGI http headers as soon as possible (PEP violation)", uwsgi_opt_true, &up.start_response_nodelay, 0},
{0, 0, 0, 0, 0, 0, 0},
};
+2
View File
@@ -178,6 +178,7 @@ struct uwsgi_python {
PyObject *after_req_hook_args;
char *pyrun;
int start_response_nodelay;
};
@@ -266,6 +267,7 @@ char *uwsgi_pythonize(char *);
void *uwsgi_python_autoreloader_thread(void *);
int uwsgi_python_manage_exceptions(void);
int uwsgi_python_do_send_headers(struct wsgi_request *);
#ifdef UWSGI_PYPY
#undef UWSGI_MINTERPRETERS
+8 -3
View File
@@ -267,16 +267,21 @@ PyTypeObject uwsgi_InputType = {
PyObject *py_uwsgi_write(PyObject * self, PyObject * args) {
PyObject *data;
char *content;
int len;
size_t content_len;
struct wsgi_request *wsgi_req = current_wsgi_req();
data = PyTuple_GetItem(args, 0);
if (PyString_Check(data)) {
content = PyString_AsString(data);
len = PyString_Size(data);
content_len = PyString_Size(data);
if (content_len > 0 && !wsgi_req->headers_sent) {
if (uwsgi_python_do_send_headers(wsgi_req)) {
return NULL;
}
}
UWSGI_RELEASE_GIL
wsgi_req->response_size = wsgi_req->socket->proto_write(wsgi_req, content, len);
wsgi_req->response_size = wsgi_req->socket->proto_write(wsgi_req, content, content_len);
UWSGI_GET_GIL
// this is a special case for the write callable
// no need to honout write-errors-exception-only
+70 -33
View File
@@ -19,6 +19,11 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
int base = 0;
// avoid double sending of headers
if (wsgi_req->headers_sent) {
return PyErr_Format(PyExc_IOError, "headers already sent");
}
// this must be done before headers management
if (PyTuple_Size(args) > 2) {
exc_info = PyTuple_GetItem(args, 2);
@@ -45,7 +50,7 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
head = PyTuple_GetItem(args, 0);
if (!head) {
goto clear;
return PyErr_Format(PyExc_TypeError, "start_response() takes at least 2 arguments");
}
#ifdef PYTHREE
@@ -53,8 +58,7 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
#else
if (!PyString_Check(head)) {
#endif
uwsgi_log( "http status must be a string !\n");
goto clear;
return PyErr_Format(PyExc_TypeError, "http status must be a string");
}
@@ -101,12 +105,13 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
}
headers = PyTuple_GetItem(args, 1);
if (!headers) goto clear;
if (!headers) {
return PyErr_Format(PyExc_TypeError, "start_response() takes at least 2 arguments");
}
if (!PyList_Check(headers)) {
uwsgi_log( "http headers must be in a python list\n");
goto clear;
return PyErr_Format(PyExc_TypeError, "http headers must be in a python list");
}
wsgi_req->header_cnt = PyList_Size(headers);
@@ -117,20 +122,36 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
j = (i * 4) + base;
head = PyList_GetItem(headers, i);
if (!head) {
PyErr_Print();
goto clear;
}
if (!PyTuple_Check(head)) {
uwsgi_log( "http header must be defined in a tuple !\n");
goto clear;
return PyErr_Format(PyExc_TypeError, "http header must be defined in a tuple");
}
h_key = PyTuple_GetItem(head, 0);
if (!h_key) {
goto clear;
return PyErr_Format(PyExc_TypeError, "http header must be a 2-item tuple");
}
#ifdef PYTHREE
if (!PyUnicode_Check(h_key)) {
#else
if (!PyString_Check(h_key)) {
#endif
return PyErr_Format(PyExc_TypeError, "http header key must be a string");
}
h_value = PyTuple_GetItem(head, 1);
if (!h_value) {
goto clear;
return PyErr_Format(PyExc_TypeError, "http header must be a 2-item tuple");
}
#ifdef PYTHREE
if (!PyUnicode_Check(h_value)) {
#else
if (!PyString_Check(h_value)) {
#endif
return PyErr_Format(PyExc_TypeError, "http header value must be a string");
}
#ifdef PYTHREE
wsgi_req->hvec[j].iov_base = PyBytes_AsString(PyUnicode_AsASCIIString(h_key));
@@ -181,30 +202,13 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) {
wsgi_req->hvec[j].iov_base = nl;
wsgi_req->hvec[j].iov_len = NL_SIZE;
#ifdef __sun__
int remains = j + 1;
int iov_size;
struct iovec* iov_ptr = wsgi_req->hvec;
ssize_t iov_ret;
while(remains) {
iov_size = UMIN(remains, IOV_MAX);
UWSGI_RELEASE_GIL
iov_ret = wsgi_req->socket->proto_writev_header(wsgi_req, iov_ptr, iov_size);
UWSGI_GET_GIL
wsgi_req->headers_size += iov_ret;
iov_ptr += iov_size;
remains -= iov_size;
}
#else
UWSGI_RELEASE_GIL
wsgi_req->headers_size = wsgi_req->socket->proto_writev_header(wsgi_req, wsgi_req->hvec, j + 1);
UWSGI_GET_GIL
#endif
wsgi_req->headers_hvec = j;
if (wsgi_req->write_errors > uwsgi.write_errors_tolerance) {
uwsgi_py_write_set_exception(wsgi_req);
return NULL;
}
if (up.start_response_nodelay) {
if (uwsgi_python_do_send_headers(wsgi_req)) {
return NULL;
}
}
//uwsgi_log("%d %p\n", wsgi_req->poll.fd, up.wsgi_writeout);
Py_INCREF(up.wsgi_writeout);
@@ -216,3 +220,36 @@ clear:
Py_INCREF(Py_None);
return Py_None;
}
int uwsgi_python_do_send_headers(struct wsgi_request *wsgi_req) {
#ifdef __sun__
int remains = wsgi_req->headers_hvec + 1;
int iov_size;
struct iovec* iov_ptr = wsgi_req->hvec;
ssize_t iov_ret;
while(remains) {
iov_size = UMIN(remains, IOV_MAX);
UWSGI_RELEASE_GIL
iov_ret = wsgi_req->socket->proto_writev_header(wsgi_req, iov_ptr, iov_size);
UWSGI_GET_GIL
wsgi_req->headers_size += iov_ret;
iov_ptr += iov_size;
remains -= iov_size;
}
#else
UWSGI_RELEASE_GIL
wsgi_req->headers_size = wsgi_req->socket->proto_writev_header(wsgi_req, wsgi_req->hvec, wsgi_req->headers_hvec + 1);
UWSGI_GET_GIL
#endif
wsgi_req->headers_sent = 1;
if (wsgi_req->write_errors > uwsgi.write_errors_tolerance) {
uwsgi_py_write_set_exception(wsgi_req);
return -1;
}
return 0;
}
+10
View File
@@ -166,6 +166,11 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) {
if (PyString_Check((PyObject *)wsgi_req->async_result)) {
char *content = PyString_AsString(wsgi_req->async_result);
size_t content_len = PyString_Size(wsgi_req->async_result);
if (content_len > 0 && !wsgi_req->headers_sent) {
if (uwsgi_python_do_send_headers(wsgi_req)) {
goto clear;
}
}
UWSGI_RELEASE_GIL
wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len);
UWSGI_GET_GIL
@@ -252,6 +257,11 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) {
if (PyString_Check(pychunk)) {
char *content = PyString_AsString(pychunk);
size_t content_len = PyString_Size(pychunk);
if (content_len > 0 && !wsgi_req->headers_sent) {
if (uwsgi_python_do_send_headers(wsgi_req)) {
goto clear;
}
}
UWSGI_RELEASE_GIL
wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len);
UWSGI_GET_GIL
+4
View File
@@ -935,6 +935,10 @@ struct wsgi_request {
// current socket mapped to request
struct uwsgi_socket *socket;
// check if headers are already sent
int headers_sent;
int headers_hvec;
int body_as_file;
//for generic use
size_t buf_pos;