diff --git a/plugins/php/php_plugin.c b/plugins/php/php_plugin.c index e9be772a..19fe6004 100644 --- a/plugins/php/php_plugin.c +++ b/plugins/php/php_plugin.c @@ -95,7 +95,7 @@ static int sapi_uwsgi_ub_write(const char *str, uint str_length TSRMLS_DC) struct wsgi_request *wsgi_req = (struct wsgi_request *) SG(server_context); wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, (char *) str, str_length); - if (wsgi_req->write_errors > 0) { + if (wsgi_req->write_errors > uwsgi.write_errors_tolerance) { php_handle_aborted_connection(); return -1; } diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 30f4a50f..a235d771 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -41,6 +41,12 @@ #define uwsgi_py_write_set_exception(x) PyErr_SetString(PyExc_IOError, "write error"); #define uwsgi_py_write_exception(x) uwsgi_py_write_set_exception(x); PyErr_Print(); + +#define uwsgi_py_check_write_errors if (wsgi_req->write_errors > 0 && uwsgi.write_errors_exception_only) {\ + uwsgi_py_write_set_exception(wsgi_req);\ + }\ + else if (wsgi_req->write_errors > uwsgi.write_errors_tolerance)\ + PyAPI_FUNC(PyObject *) PyMarshal_WriteObjectToString(PyObject *, int); PyAPI_FUNC(PyObject *) PyMarshal_ReadObjectFromString(char *, Py_ssize_t); diff --git a/plugins/python/web3_subhandler.c b/plugins/python/web3_subhandler.c index 68e2c026..e833fb4a 100644 --- a/plugins/python/web3_subhandler.c +++ b/plugins/python/web3_subhandler.c @@ -172,7 +172,7 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { UWSGI_RELEASE_GIL wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len); UWSGI_GET_GIL - if (wsgi_req->write_errors > 0) { + uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); } goto clear; @@ -215,7 +215,7 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) { UWSGI_RELEASE_GIL wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len); UWSGI_GET_GIL - if (wsgi_req->write_errors > 0) { + uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); Py_DECREF(pychunk); goto clear; diff --git a/plugins/python/wsgi_handlers.c b/plugins/python/wsgi_handlers.c index 6ce21ecc..fa2a8076 100644 --- a/plugins/python/wsgi_handlers.c +++ b/plugins/python/wsgi_handlers.c @@ -278,6 +278,8 @@ PyObject *py_uwsgi_write(PyObject * self, PyObject * args) { UWSGI_RELEASE_GIL wsgi_req->response_size = wsgi_req->socket->proto_write(wsgi_req, content, len); UWSGI_GET_GIL + // this is a special case for the write callable + // no need to honout write-errors-exception-only if (wsgi_req->write_errors > 0) { uwsgi_py_write_set_exception(wsgi_req); return NULL; diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index 82f21258..ce048b59 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -169,7 +169,7 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { UWSGI_RELEASE_GIL wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len); UWSGI_GET_GIL - if (wsgi_req->write_errors > 0) { + uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); } goto clear; @@ -255,7 +255,7 @@ int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) { UWSGI_RELEASE_GIL wsgi_req->response_size += wsgi_req->socket->proto_write(wsgi_req, content, content_len); UWSGI_GET_GIL - if (wsgi_req->write_errors > 0) { + uwsgi_py_check_write_errors { uwsgi_py_write_exception(wsgi_req); Py_DECREF(pychunk); goto clear; diff --git a/tests/badwrites.py b/tests/badwrites.py new file mode 100644 index 00000000..d060f995 --- /dev/null +++ b/tests/badwrites.py @@ -0,0 +1,35 @@ +import time +import sys + +def application(e, sr): + time.sleep(3) + print("3 seconds elapsed") + sr('200 Ok', [('Content-Type', 'text/html')]) + + time.sleep(2) + print("2 seconds elapsed") + + yield "part1" + + try: + time.sleep(2) + except: + print("CLIENT DISCONNECTED !!!") + print("2 seconds elapsed") + + yield "part2" + + try: + time.sleep(2) + except: + print("CLIENT DISCONNECTED !!!") + print("2 seconds elapsed") + + yield "part3" + + time.sleep(2) + print("2 seconds elapsed") + + yield "part4" + + print("end of request") diff --git a/utils.c b/utils.c index 07b15f53..4a8e44a0 100644 --- a/utils.c +++ b/utils.c @@ -1130,6 +1130,11 @@ void sanitize_args() { uwsgi.vacuum = 1; } #endif + + if (uwsgi.write_errors_exception_only) { + uwsgi.ignore_sigpipe = 1; + uwsgi.ignore_write_errors = 1; + } } void env_to_arg(char *src, char *dst) { diff --git a/uwsgi.c b/uwsgi.c index 883aebb8..f1756cfd 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -84,6 +84,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"ignore-sigpipe", no_argument, 0, "do not report (annoying) SIGPIPE", uwsgi_opt_true, &uwsgi.ignore_sigpipe,0}, {"ignore-write-errors", no_argument, 0, "do not report (annoying) write()/writev() errors", uwsgi_opt_true, &uwsgi.ignore_write_errors,0}, + {"write-errors-tolerance", required_argument, 0, "set the maximum number of allowed write errors (default: no tolerance)", uwsgi_opt_set_int, &uwsgi.write_errors_tolerance,0}, + {"write-errors-exception-only", no_argument, 0, "only raise an exception on write errors giving control to the app itself", uwsgi_opt_true, &uwsgi.write_errors_exception_only,0}, {"inherit", required_argument, 0, "use the specified file as config template", uwsgi_opt_load, NULL,0}, {"daemonize", required_argument, 'd', "daemonize uWSGI", uwsgi_opt_set_str, &uwsgi.daemonize, 0}, diff --git a/uwsgi.h b/uwsgi.h index cfe9291f..67377b6e 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -1190,6 +1190,8 @@ struct uwsgi_server { int ignore_sigpipe; int ignore_write_errors; + int write_errors_tolerance; + int write_errors_exception_only; // still working on it char *profiler;