# HG changeset patch # User domruf # Date 1505157409 -7200 # Node ID 32440c07a0857ec5d9093e3e587bec70e363e95e # Parent 58713c2ebfff82815c9e2dccac3250b0218b9f9e 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. diff -r 58713c2ebfff -r 32440c07a085 kallithea/lib/base.py --- 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