commit 9049214d1c4b96524e251d65e06da6759c110f12 from: Antoine Lambert date: Wed Jul 05 11:31:04 2023 UTC client: Fix content type check in AbstractHttpGitClient._smart_request Some git servers can send a Content-Type header containing a charset directive. Official git client can successfully clone repositories hosted on such server but dulwich was raising a GitProtocolError as the code to check the header value was not expecting a directive to be present in it. commit - c1c52f895822136ee952226e2ec1bc04d9f794f5 commit + 9049214d1c4b96524e251d65e06da6759c110f12 blob - 9c88a839a5f5c2a7b3f05de27ee04569f1ba59c0 blob + 857de2cde4eff29c68a2e9525413c97a06051ffd --- dulwich/client.py +++ dulwich/client.py @@ -2007,7 +2007,7 @@ class AbstractHttpGitClient(GitClient): if isinstance(data, bytes): headers["Content-Length"] = str(len(data)) resp, read = self._http_request(url, headers, data) - if resp.content_type != result_content_type: + if not resp.content_type.startswith(result_content_type): raise GitProtocolError( "Invalid content-type from server: %s" % resp.content_type ) blob - b6df9ff105208b53328469be5a0300b71f73c885 blob + a50e46458e3f145a0786fbe269a83ff49620f67e --- dulwich/tests/test_client.py +++ dulwich/tests/test_client.py @@ -1144,7 +1144,31 @@ class HttpGitClientTests(TestCase): else: # check also the no redirection case self.assertEqual(processed_url, base_url) + + def test_smart_request_content_type_with_directive_check(self): + from urllib3.response import HTTPResponse + + # we need to mock urllib3.PoolManager as this test will fail + # otherwise without an active internet connection + class PoolManagerMock: + def __init__(self) -> None: + self.headers: Dict[str, str] = {} + + def request(self, method, url, fields=None, headers=None, redirect=True, preload_content=True): + return HTTPResponse( + headers={ + "Content-Type": "application/x-git-upload-pack-result; charset=utf-8" + }, + request_method=method, + request_url=url, + preload_content=preload_content, + status=200, + ) + clone_url = "https://hacktivis.me/git/blog.git/" + client = HttpGitClient(clone_url, pool_manager=PoolManagerMock(), config=None) + self.assertTrue(client._smart_request("git-upload-pack", clone_url, data=None)) + class TCPGitClientTests(TestCase): def test_get_url(self):