##// END OF EJS Templates
fix(pycurl): added missing __all__ imports
super-admin -
r5322:05d560ef default
parent child Browse files
Show More
@@ -1,188 +1,189 b''
1 1 # Copyright (C) 2014-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 """
20 20 Various version Control System version lib (vcs) management abstraction layer
21 21 for Python. Build with server client architecture.
22 22 """
23 23 import io
24 24 import atexit
25 25 import logging
26 26
27 27 import rhodecode
28 28 from rhodecode.lib.str_utils import safe_bytes
29 29 from rhodecode.lib.vcs.conf import settings
30 30 from rhodecode.lib.vcs.backends import get_vcs_instance, get_backend
31 31 from rhodecode.lib.vcs.exceptions import (
32 32 VCSError, RepositoryError, CommitError, VCSCommunicationError)
33 33
34 34 __all__ = [
35 35 'get_vcs_instance', 'get_backend',
36 'VCSError', 'RepositoryError', 'CommitError', 'VCSCommunicationError'
36 'VCSError', 'RepositoryError', 'CommitError', 'VCSCommunicationError',
37 'CurlSession', 'CurlResponse'
37 38 ]
38 39
39 40 log = logging.getLogger(__name__)
40 41
41 42 # The pycurl library directly accesses C API functions and is not patched by
42 43 # gevent. This will potentially lead to deadlocks due to incompatibility to
43 44 # gevent. Therefore we check if gevent is active and import a gevent compatible
44 45 # wrapper in that case.
45 46 try:
46 47 from gevent import monkey
47 48 if monkey.is_module_patched('__builtin__'):
48 49 import geventcurl as pycurl
49 50 log.debug('Using gevent comapatible pycurl: %s', pycurl)
50 51 else:
51 52 import pycurl
52 53 except ImportError:
53 54 import pycurl
54 55
55 56
56 57 def connect_http(server_and_port):
57 58 log.debug('Initialized VCSServer connections to %s.', server_and_port)
58 59
59 60 from rhodecode.lib.vcs import connection, client_http
60 61 from rhodecode.lib.middleware.utils import scm_app
61 62
62 63 session_factory = client_http.ThreadlocalSessionFactory()
63 64
64 65 connection.Git = client_http.RemoteVCSMaker(
65 66 server_and_port, '/git', 'git', session_factory)
66 67 connection.Hg = client_http.RemoteVCSMaker(
67 68 server_and_port, '/hg', 'hg', session_factory)
68 69 connection.Svn = client_http.RemoteVCSMaker(
69 70 server_and_port, '/svn', 'svn', session_factory)
70 71 connection.Service = client_http.ServiceConnection(
71 72 server_and_port, '/_service', session_factory)
72 73
73 74 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
74 75 server_and_port, '/proxy/hg')
75 76 scm_app.GIT_REMOTE_WSGI = client_http.VcsHttpProxy(
76 77 server_and_port, '/proxy/git')
77 78
78 79 @atexit.register
79 80 def free_connection_resources():
80 81 connection.Git = None
81 82 connection.Hg = None
82 83 connection.Svn = None
83 84 connection.Service = None
84 85
85 86
86 87 def connect_vcs(server_and_port, protocol):
87 88 """
88 89 Initializes the connection to the vcs server.
89 90
90 91 :param server_and_port: str, e.g. "localhost:9900"
91 92 :param protocol: str or "http"
92 93 """
93 94 if protocol == 'http':
94 95 connect_http(server_and_port)
95 96 else:
96 97 raise Exception(f'Invalid vcs server protocol "{protocol}"')
97 98
98 99
99 100 class CurlSession(object):
100 101 """
101 102 Modeled so that it provides a subset of the requests interface.
102 103
103 104 This has been created so that it does only provide a minimal API for our
104 105 needs. The parts which it provides are based on the API of the library
105 106 `requests` which allows us to easily benchmark against it.
106 107
107 108 Please have a look at the class :class:`requests.Session` when you extend
108 109 it.
109 110 """
110 111 CURL_UA = f'RhodeCode HTTP {rhodecode.__version__}'
111 112
112 113 def __init__(self):
113 114 curl = pycurl.Curl()
114 115 # TODO: johbo: I did test with 7.19 of libcurl. This version has
115 116 # trouble with 100 - continue being set in the expect header. This
116 117 # can lead to massive performance drops, switching it off here.
117 118
118 119 curl.setopt(curl.TCP_NODELAY, True)
119 120 curl.setopt(curl.PROTOCOLS, curl.PROTO_HTTP)
120 121 curl.setopt(curl.USERAGENT, safe_bytes(self.CURL_UA))
121 122 curl.setopt(curl.SSL_VERIFYPEER, 0)
122 123 curl.setopt(curl.SSL_VERIFYHOST, 0)
123 124 self._curl = curl
124 125
125 126 def post(self, url, data, allow_redirects=False, headers=None):
126 127 headers = headers or {}
127 128 # format is ['header_name1: header_value1', 'header_name2: header_value2'])
128 129 headers_list = [b"Expect:"] + [safe_bytes('{}: {}'.format(k, v)) for k, v in headers.items()]
129 130 response_buffer = io.BytesIO()
130 131
131 132 curl = self._curl
132 133 curl.setopt(curl.URL, url)
133 134 curl.setopt(curl.POST, True)
134 135 curl.setopt(curl.POSTFIELDS, data)
135 136 curl.setopt(curl.FOLLOWLOCATION, allow_redirects)
136 137 curl.setopt(curl.WRITEDATA, response_buffer)
137 138 curl.setopt(curl.HTTPHEADER, headers_list)
138 139 curl.perform()
139 140
140 141 status_code = curl.getinfo(pycurl.HTTP_CODE)
141 142 content_type = curl.getinfo(pycurl.CONTENT_TYPE)
142 143 return CurlResponse(response_buffer, status_code, content_type)
143 144
144 145
145 146 class CurlResponse(object):
146 147 """
147 148 The response of a request, modeled after the requests API.
148 149
149 150 This class provides a subset of the response interface known from the
150 151 library `requests`. It is intentionally kept similar, so that we can use
151 152 `requests` as a drop in replacement for benchmarking purposes.
152 153 """
153 154
154 155 def __init__(self, response_buffer, status_code, content_type=''):
155 156 self._response_buffer = response_buffer
156 157 self._status_code = status_code
157 158 self._content_type = content_type
158 159
159 160 def __repr__(self):
160 161 return f'CurlResponse(code={self._status_code}, content_type={self._content_type})'
161 162
162 163 @property
163 164 def content(self):
164 165 try:
165 166 return self._response_buffer.getvalue()
166 167 finally:
167 168 self._response_buffer.close()
168 169
169 170 @property
170 171 def status_code(self):
171 172 return self._status_code
172 173
173 174 @property
174 175 def content_type(self):
175 176 return self._content_type
176 177
177 178 def iter_content(self, chunk_size):
178 179 self._response_buffer.seek(0)
179 180 while 1:
180 181 chunk = self._response_buffer.read(chunk_size)
181 182 if not chunk:
182 183 break
183 184 yield chunk
184 185
185 186
186 187 def _create_http_rpc_session():
187 188 session = CurlSession()
188 189 return session
General Comments 0
You need to be logged in to leave comments. Login now