requests_vendor.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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 dulwich.client import AbstractHttpGitClient, HTTPUnauthorized, HTTPProxyUnauthorized, default_user_agent_string
  30. from dulwich.errors import NotGitRepository, GitProtocolError
  31. class RequestsHttpGitClient(AbstractHttpGitClient):
  32. def __init__(
  33. self,
  34. base_url,
  35. dumb=None,
  36. config=None,
  37. username=None,
  38. password=None,
  39. **kwargs
  40. ):
  41. self._username = username
  42. self._password = password
  43. self.session = get_session(config)
  44. if username is not None:
  45. self.session.auth = (username, password)
  46. super(RequestsHttpGitClient, self).__init__(
  47. base_url=base_url, dumb=dumb, **kwargs)
  48. def _http_request(self, url, headers=None, data=None, allow_compression=False):
  49. req_headers = self.session.headers.copy()
  50. if headers is not None:
  51. req_headers.update(headers)
  52. if allow_compression:
  53. req_headers["Accept-Encoding"] = "gzip"
  54. else:
  55. req_headers["Accept-Encoding"] = "identity"
  56. if data:
  57. resp = self.session.post(url, headers=req_headers, data=data)
  58. else:
  59. resp = self.session.get(url, headers=req_headers)
  60. if resp.status_code == 404:
  61. raise NotGitRepository()
  62. if resp.status_code == 401:
  63. raise HTTPUnauthorized(resp.headers.get("WWW-Authenticate"), url)
  64. if resp.status_code == 407:
  65. raise HTTPProxyUnauthorized(resp.headers.get("Proxy-Authenticate"), url)
  66. if resp.status_code != 200:
  67. raise GitProtocolError(
  68. "unexpected http resp %d for %s" % (resp.status_code, url)
  69. )
  70. # Add required fields as stated in AbstractHttpGitClient._http_request
  71. resp.content_type = resp.headers.get("Content-Type")
  72. resp.redirect_location = ""
  73. if resp.history:
  74. resp.redirect_location = resp.url
  75. read = BytesIO(resp.content).read
  76. return resp, read
  77. def get_session(config):
  78. session = Session()
  79. session.headers.update({"Pragma": "no-cache"})
  80. proxy_server = user_agent = ca_certs = ssl_verify = None
  81. if config is not None:
  82. try:
  83. proxy_server = config.get(b"http", b"proxy")
  84. if isinstance(proxy_server, bytes):
  85. proxy_server = proxy_server.decode()
  86. except KeyError:
  87. pass
  88. try:
  89. user_agent = config.get(b"http", b"useragent")
  90. if isinstance(user_agent, bytes):
  91. user_agent = user_agent.decode()
  92. except KeyError:
  93. pass
  94. try:
  95. ssl_verify = config.get_boolean(b"http", b"sslVerify")
  96. except KeyError:
  97. ssl_verify = True
  98. try:
  99. ca_certs = config.get(b"http", b"sslCAInfo")
  100. if isinstance(ca_certs, bytes):
  101. ca_certs = ca_certs.decode()
  102. except KeyError:
  103. ca_certs = None
  104. if user_agent is None:
  105. user_agent = default_user_agent_string()
  106. session.headers.update({"User-agent": user_agent})
  107. if ca_certs:
  108. session.verify = ca_certs
  109. elif ssl_verify is False:
  110. session.verify = ssl_verify
  111. if proxy_server:
  112. session.proxies.update({
  113. "http": proxy_server,
  114. "https": proxy_server
  115. })
  116. return session