Mercurial > kallithea
changeset 6926:32440c07a085
auth: consume request body before responding 401 or 403 during authentication
In order to work correctly with reverse proxies like Apache, the application
needs to consume the whole body before returning and closing the connection.
Otherwise the reverse proxy may complain about a broken pipe.
For example, if the client sends a lot of data and kallithea doesn't read all
that data before sending 401, the connection will be closed before the reverse
proxy has sent all the data. In this case an apache reverse proxy will fail
with a broken pipe error.
This is not necessary for all wsgi servers. Waitress automatically buffers (and
therefore reads) all the data and uwsgi has a 'post-buffering' option to do the
same. But AFAIK there is no way to push to a password protected hg repository
when using gunicorn without this changeset.
author | domruf <dominikruf@gmail.com> |
---|---|
date | Mon, 11 Sep 2017 21:16:49 +0200 |
parents | 58713c2ebfff |
children | 557fb1f85ca1 |
files | kallithea/lib/base.py |
diffstat | 1 files changed, 10 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/lib/base.py Wed Sep 20 23:08:35 2017 +0200 +++ b/kallithea/lib/base.py Mon Sep 11 21:16:49 2017 +0200 @@ -191,8 +191,14 @@ self.authfunc = authfunc self._rc_auth_http_code = auth_http_code - def build_authentication(self): + def build_authentication(self, environ): head = paste.httpheaders.WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm) + # Consume the whole body before sending a response + try: + request_body_size = int(environ.get('CONTENT_LENGTH', 0)) + except (ValueError): + request_body_size = 0 + environ['wsgi.input'].read(request_body_size) if self._rc_auth_http_code and self._rc_auth_http_code == '403': # return 403 if alternative http return code is specified in # Kallithea config @@ -202,17 +208,17 @@ def authenticate(self, environ): authorization = paste.httpheaders.AUTHORIZATION(environ) if not authorization: - return self.build_authentication() + return self.build_authentication(environ) (authmeth, auth) = authorization.split(' ', 1) if 'basic' != authmeth.lower(): - return self.build_authentication() + return self.build_authentication(environ) auth = auth.strip().decode('base64') _parts = auth.split(':', 1) if len(_parts) == 2: username, password = _parts if self.authfunc(username, password, environ) is not None: return username - return self.build_authentication() + return self.build_authentication(environ) __call__ = authenticate