##// END OF EJS Templates
http-app: adds some logging.
marcink -
r1983:409b1039 default
parent child Browse files
Show More
@@ -1,158 +1,161 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Implementation of the scm_app interface using raw HTTP communication.
23 23 """
24 24
25 25 import base64
26 26 import logging
27 27 import urlparse
28 28 import wsgiref.util
29 29
30 30 import msgpack
31 31 import requests
32 32 import webob.request
33 33
34 34 import rhodecode
35 35
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 def create_git_wsgi_app(repo_path, repo_name, config):
41 41 url = _vcs_streaming_url() + 'git/'
42 42 return VcsHttpProxy(url, repo_path, repo_name, config)
43 43
44 44
45 45 def create_hg_wsgi_app(repo_path, repo_name, config):
46 46 url = _vcs_streaming_url() + 'hg/'
47 47 return VcsHttpProxy(url, repo_path, repo_name, config)
48 48
49 49
50 50 def _vcs_streaming_url():
51 51 template = 'http://{}/stream/'
52 52 return template.format(rhodecode.CONFIG['vcs.server'])
53 53
54 54
55 55 # TODO: johbo: Avoid the global.
56 56 session = requests.Session()
57 57 # Requests speedup, avoid reading .netrc and similar
58 58 session.trust_env = False
59 59
60 60 # prevent urllib3 spawning our logs.
61 61 logging.getLogger("requests.packages.urllib3.connectionpool").setLevel(
62 62 logging.WARNING)
63 63
64 64
65 65 class VcsHttpProxy(object):
66 66 """
67 67 A WSGI application which proxies vcs requests.
68 68
69 69 The goal is to shuffle the data around without touching it. The only
70 70 exception is the extra data from the config object which we send to the
71 71 server as well.
72 72 """
73 73
74 74 def __init__(self, url, repo_path, repo_name, config):
75 75 """
76 76 :param str url: The URL of the VCSServer to call.
77 77 """
78 78 self._url = url
79 79 self._repo_name = repo_name
80 80 self._repo_path = repo_path
81 81 self._config = config
82 82 log.debug(
83 83 "Creating VcsHttpProxy for repo %s, url %s",
84 84 repo_name, url)
85 85
86 86 def __call__(self, environ, start_response):
87 87 config = msgpack.packb(self._config)
88 88 request = webob.request.Request(environ)
89 89 request_headers = request.headers
90 90 request_headers.update({
91 91 # TODO: johbo: Remove this, rely on URL path only
92 92 'X-RC-Repo-Name': self._repo_name,
93 93 'X-RC-Repo-Path': self._repo_path,
94 94 'X-RC-Path-Info': environ['PATH_INFO'],
95 95 # TODO: johbo: Avoid encoding and put this into payload?
96 96 'X-RC-Repo-Config': base64.b64encode(config),
97 97 'X-RC-Locked-Status-Code': rhodecode.CONFIG.get('lock_ret_code')
98 98 })
99 99
100 100 method = environ['REQUEST_METHOD']
101 101
102 102 # Preserve the query string
103 103 url = self._url
104 104 url = urlparse.urljoin(url, self._repo_name)
105 105 if environ.get('QUERY_STRING'):
106 106 url += '?' + environ['QUERY_STRING']
107 107
108 log.debug('http-app: preparing request to: %s', url)
108 109 response = session.request(
109 method, url,
110 method,
111 url,
110 112 data=_maybe_stream_request(environ),
111 113 headers=request_headers,
112 114 stream=True)
113 115
116 log.debug('http-app: got vcsserver response: %s', response)
114 117 # Preserve the headers of the response, except hop_by_hop ones
115 118 response_headers = [
116 119 (h, v) for h, v in response.headers.items()
117 120 if not wsgiref.util.is_hop_by_hop(h)
118 121 ]
119 122
120 123 # Build status argument for start_reponse callable.
121 124 status = '{status_code} {reason_phrase}'.format(
122 125 status_code=response.status_code,
123 126 reason_phrase=response.reason)
124 127
125 128 start_response(status, response_headers)
126 129 return _maybe_stream_response(response)
127 130
128 131
129 132 def _is_request_chunked(environ):
130 133 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
131 134 return stream
132 135
133 136
134 137 def _maybe_stream_request(environ):
135 138 path = environ['PATH_INFO']
136 139 stream = _is_request_chunked(environ)
137 140 log.debug('handling request `%s` with stream support: %s', path, stream)
138 141
139 142 if stream:
140 143 return environ['wsgi.input']
141 144 else:
142 145 return environ['wsgi.input'].read()
143 146
144 147
145 148 def _maybe_stream_response(response):
146 149 """
147 150 Try to generate chunks from the response if it is chunked.
148 151 """
149 152 stream = _is_chunked(response)
150 153 log.debug('returning response with stream: %s', stream)
151 154 if stream:
152 155 return response.raw.read_chunked()
153 156 else:
154 157 return [response.content]
155 158
156 159
157 160 def _is_chunked(response):
158 161 return response.headers.get('Transfer-Encoding', '') == 'chunked'
General Comments 0
You need to be logged in to leave comments. Login now