requests_vendor.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # requests_vendor.py -- requests implementation of the AbstractHttpGitClient interface
  2. # Copyright (C) 2022 Eden Shalit <epopcop@gmail.com>
  3. #
  4. # Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
  5. # General Public License as public by the Free Software Foundation; version 2.0
  6. # or (at your option) any later version. You can redistribute it and/or
  7. # modify it under the terms of either of these two licenses.
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. # You should have received a copy of the licenses; if not, see
  16. # <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
  17. # and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
  18. # License, Version 2.0.
  19. """Requests HTTP client support for Dulwich.
  20. To use this implementation as the HTTP implementation in Dulwich, override
  21. the dulwich.client.HttpGitClient attribute:
  22. >>> from dulwich import client as _mod_client
  23. >>> from dulwich.contrib.requests_vendor import RequestsHttpGitClient
  24. >>> _mod_client.HttpGitClient = RequestsHttpGitClient
  25. This implementation is experimental and does not have any tests.
  26. """
  27. from io import BytesIO
  28. from requests import Session
  29. from ..client import (
  30. AbstractHttpGitClient,
  31. HTTPProxyUnauthorized,
  32. HTTPUnauthorized,
  33. default_user_agent_string,
  34. )
  35. from ..errors import GitProtocolError, NotGitRepository
  36. class RequestsHttpGitClient(AbstractHttpGitClient):
  37. def __init__(
  38. self, base_url, dumb=None, config=None, username=None, password=None, **kwargs
  39. ) -> None:
  40. self._username = username
  41. self._password = password
  42. self.session = get_session(config)
  43. if username is not None:
  44. self.session.auth = (username, password)
  45. super().__init__(base_url=base_url, dumb=dumb, **kwargs)
  46. def _http_request(self, url, headers=None, data=None, allow_compression=False):
  47. req_headers = self.session.headers.copy()
  48. if headers is not None:
  49. req_headers.update(headers)
  50. if allow_compression:
  51. req_headers["Accept-Encoding"] = "gzip"
  52. else:
  53. req_headers["Accept-Encoding"] = "identity"
  54. if data:
  55. resp = self.session.post(url, headers=req_headers, data=data)
  56. else:
  57. resp = self.session.get(url, headers=req_headers)
  58. if resp.status_code == 404:
  59. raise NotGitRepository
  60. if resp.status_code == 401:
  61. raise HTTPUnauthorized(resp.headers.get("WWW-Authenticate"), url)
  62. if resp.status_code == 407:
  63. raise HTTPProxyUnauthorized(resp.headers.get("Proxy-Authenticate"), url)
  64. if resp.status_code != 200:
  65. raise GitProtocolError(
  66. "unexpected http resp %d for %s" % (resp.status_code, url)
  67. )
  68. # Add required fields as stated in AbstractHttpGitClient._http_request
  69. resp.content_type = resp.headers.get("Content-Type")
  70. resp.redirect_location = ""
  71. if resp.history:
  72. resp.redirect_location = resp.url
  73. read = BytesIO(resp.content).read
  74. return resp, read
  75. def get_session(config):
  76. session = Session()
  77. session.headers.update({"Pragma": "no-cache"})
  78. proxy_server = user_agent = ca_certs = ssl_verify = None
  79. if config is not None:
  80. try:
  81. proxy_server = config.get(b"http", b"proxy")
  82. if isinstance(proxy_server, bytes):
  83. proxy_server = proxy_server.decode()
  84. except KeyError:
  85. pass
  86. try:
  87. user_agent = config.get(b"http", b"useragent")
  88. if isinstance(user_agent, bytes):
  89. user_agent = user_agent.decode()
  90. except KeyError:
  91. pass
  92. try:
  93. ssl_verify = config.get_boolean(b"http", b"sslVerify")
  94. except KeyError:
  95. ssl_verify = True
  96. try:
  97. ca_certs = config.get(b"http", b"sslCAInfo")
  98. if isinstance(ca_certs, bytes):
  99. ca_certs = ca_certs.decode()
  100. except KeyError:
  101. ca_certs = None
  102. if user_agent is None:
  103. user_agent = default_user_agent_string()
  104. session.headers.update({"User-agent": user_agent})
  105. if ca_certs:
  106. session.verify = ca_certs
  107. elif ssl_verify is False:
  108. session.verify = ssl_verify
  109. if proxy_server:
  110. session.proxies.update({"http": proxy_server, "https": proxy_server})
  111. return session