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