make auth work better with git-remote-hg

Commit 96b69e7f56c1 · Harrison Erd · 2026-05-05 02:49 -0400

Changeset
96b69e7f56c136ced5b13dc4326d8d8e9ad5c691

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/app.py b/app.py
--- a/app.py
+++ b/app.py
@@ -552,11 +552,14 @@
     AUTH_FAILURES.pop(auth_rate_key(kind, identifier), None)
 
 
-def too_many_requests_response():
+def too_many_requests_response(close_connection=False):
+    headers = {"Retry-After": str(RATE_LIMIT_COOLDOWN_SECONDS)}
+    if close_connection:
+        headers["Connection"] = "close"
     return HTTPResponse(
         "Too many failed attempts. Try again later.\n",
         status=429,
-        headers={"Retry-After": str(RATE_LIMIT_COOLDOWN_SECONDS)},
+        headers=headers,
         content_type="text/plain; charset=utf-8",
     )
 
@@ -2471,7 +2474,7 @@
     return HTTPResponse(
         message + "\n",
         status=401,
-        headers={"WWW-Authenticate": 'Basic realm="HgHost"'},
+        headers={"WWW-Authenticate": 'Basic realm="HgHost"', "Connection": "close"},
         content_type="text/plain; charset=utf-8",
     )
 
@@ -3291,14 +3294,19 @@
     auth_user, auth_error = parse_basic_auth("hg" if is_write else None, clear_on_success=not is_write)
     if is_write:
         if auth_error == "rate_limited":
-            return too_many_requests_response()
+            return too_many_requests_response(close_connection=True)
         if auth_error:
             return basic_auth_challenge("Invalid Mercurial credentials.")
         if not auth_user:
             return basic_auth_challenge()
         if not user_can_maintain_repo(auth_user, repo):
             record_auth_failure("hg", auth_user["username"])
-            return HTTPResponse("Push not authorized for this repository.\n", status=403, content_type="text/plain")
+            return HTTPResponse(
+                "Push not authorized for this repository.\n",
+                status=403,
+                headers={"Connection": "close"},
+                content_type="text/plain; charset=utf-8",
+            )
         clear_auth_failures("hg", auth_user["username"])
 
     return hg_wsgi_response(repo, auth_user)
diff --git a/tests/test_app.py b/tests/test_app.py
--- a/tests/test_app.py
+++ b/tests/test_app.py
@@ -356,6 +356,7 @@
     response = client.post("/hg/alice/demo?cmd=unbundle")
     assert response.status_code == 401
     assert response.header("WWW-Authenticate") == 'Basic realm="HgHost"'
+    assert response.header("Connection") == "close"
 
 
 def test_browser_form_size_limit(isolated_app, monkeypatch):
@@ -388,8 +389,10 @@
     for _ in range(2):
         response = client.post("/hg/alice/demo?cmd=unbundle", headers=basic_auth("alice", "wrong"))
         assert response.status_code == 401
+        assert response.header("Connection") == "close"
     response = client.post("/hg/alice/demo?cmd=unbundle", headers=basic_auth("alice", "wrong"))
     assert response.status_code == 429
+    assert response.header("Connection") == "close"
 
 
 def test_readme_and_file_previews_are_truncated(isolated_app, monkeypatch):
@@ -1118,12 +1121,15 @@
     response = client.post("/hg/alice/demo?cmd=unbundle")
     assert response.status_code == 401
     assert response.header("WWW-Authenticate") == 'Basic realm="HgHost"'
+    assert response.header("Connection") == "close"
     assert "Authentication required." in response.text
 
     response = client.post("/hg/alice/demo?cmd=unbundle", headers=basic_auth("alice", "wrong"))
     assert response.status_code == 401
+    assert response.header("Connection") == "close"
     assert "Invalid Mercurial credentials." in response.text
 
     response = client.post("/hg/alice/demo?cmd=unbundle", headers=basic_auth("bob", "bob-password"))
     assert response.status_code == 403
+    assert response.header("Connection") == "close"
     assert "Push not authorized" in response.text