Show More
@@ -25,6 +25,7 b' SimpleHG middleware for handling mercuri' | |||||
25 |
|
25 | |||
26 | import logging |
|
26 | import logging | |
27 | import urlparse |
|
27 | import urlparse | |
|
28 | import urllib | |||
28 |
|
29 | |||
29 | from rhodecode.lib import utils |
|
30 | from rhodecode.lib import utils | |
30 | from rhodecode.lib.ext_json import json |
|
31 | from rhodecode.lib.ext_json import json | |
@@ -51,24 +52,94 b' class SimpleHg(simplevcs.SimpleVCS):' | |||||
51 | 'getbundle': 'pull', |
|
52 | 'getbundle': 'pull', | |
52 | 'stream_out': 'pull', |
|
53 | 'stream_out': 'pull', | |
53 | 'listkeys': 'pull', |
|
54 | 'listkeys': 'pull', | |
|
55 | 'between': 'pull', | |||
|
56 | 'branchmap': 'pull', | |||
|
57 | 'branches': 'pull', | |||
|
58 | 'clonebundles': 'pull', | |||
|
59 | 'capabilities': 'pull', | |||
|
60 | 'debugwireargs': 'pull', | |||
|
61 | 'heads': 'pull', | |||
|
62 | 'lookup': 'pull', | |||
|
63 | 'hello': 'pull', | |||
|
64 | 'known': 'pull', | |||
|
65 | ||||
|
66 | # largefiles | |||
|
67 | 'putlfile': 'push', | |||
|
68 | 'getlfile': 'pull', | |||
|
69 | 'statlfile': 'pull', | |||
|
70 | 'lheads': 'pull', | |||
|
71 | ||||
54 | 'unbundle': 'push', |
|
72 | 'unbundle': 'push', | |
55 | 'pushkey': 'push', |
|
73 | 'pushkey': 'push', | |
56 | } |
|
74 | } | |
57 |
|
75 | |||
|
76 | @classmethod | |||
|
77 | def _get_xarg_headers(cls, environ): | |||
|
78 | i = 1 | |||
|
79 | chunks = [] # gather chunks stored in multiple 'hgarg_N' | |||
|
80 | while True: | |||
|
81 | head = environ.get('HTTP_X_HGARG_{}'.format(i)) | |||
|
82 | if not head: | |||
|
83 | break | |||
|
84 | i += 1 | |||
|
85 | chunks.append(urllib.unquote_plus(head)) | |||
|
86 | full_arg = ''.join(chunks) | |||
|
87 | pref = 'cmds=' | |||
|
88 | if full_arg.startswith(pref): | |||
|
89 | # strip the cmds= header defining our batch commands | |||
|
90 | full_arg = full_arg[len(pref):] | |||
|
91 | cmds = full_arg.split(';') | |||
|
92 | return cmds | |||
|
93 | ||||
|
94 | @classmethod | |||
|
95 | def _get_batch_cmd(cls, environ): | |||
|
96 | """ | |||
|
97 | Handle batch command send commands. Those are ';' separated commands | |||
|
98 | sent by batch command that server needs to execute. We need to extract | |||
|
99 | those, and map them to our ACTION_MAPPING to get all push/pull commands | |||
|
100 | specified in the batch | |||
|
101 | """ | |||
|
102 | default = 'push' | |||
|
103 | batch_cmds = [] | |||
|
104 | try: | |||
|
105 | cmds = cls._get_xarg_headers(environ) | |||
|
106 | for pair in cmds: | |||
|
107 | parts = pair.split(' ', 1) | |||
|
108 | if len(parts) != 2: | |||
|
109 | continue | |||
|
110 | # entry should be in a format `key ARGS` | |||
|
111 | cmd, args = parts | |||
|
112 | action = cls._ACTION_MAPPING.get(cmd, default) | |||
|
113 | batch_cmds.append(action) | |||
|
114 | except Exception: | |||
|
115 | log.exception('Failed to extract batch commands operations') | |||
|
116 | ||||
|
117 | # in case we failed, (e.g malformed data) assume it's PUSH sub-command | |||
|
118 | # for safety | |||
|
119 | return batch_cmds or [default] | |||
|
120 | ||||
58 | def _get_action(self, environ): |
|
121 | def _get_action(self, environ): | |
59 | """ |
|
122 | """ | |
60 | Maps mercurial request commands into a pull or push command. |
|
123 | Maps mercurial request commands into a pull or push command. | |
61 |
In case of unknown/unexpected data, it returns 'pu |
|
124 | In case of unknown/unexpected data, it returns 'push' to be safe. | |
62 |
|
125 | |||
63 | :param environ: |
|
126 | :param environ: | |
64 | """ |
|
127 | """ | |
|
128 | default = 'push' | |||
65 | query = urlparse.parse_qs(environ['QUERY_STRING'], |
|
129 | query = urlparse.parse_qs(environ['QUERY_STRING'], | |
66 | keep_blank_values=True) |
|
130 | keep_blank_values=True) | |
|
131 | ||||
67 | if 'cmd' in query: |
|
132 | if 'cmd' in query: | |
68 | cmd = query['cmd'][0] |
|
133 | cmd = query['cmd'][0] | |
69 | return self._ACTION_MAPPING.get(cmd, 'pull') |
|
134 | if cmd == 'batch': | |
|
135 | cmds = self._get_batch_cmd(environ) | |||
|
136 | if 'push' in cmds: | |||
|
137 | return 'push' | |||
|
138 | else: | |||
|
139 | return 'pull' | |||
|
140 | return self._ACTION_MAPPING.get(cmd, default) | |||
70 |
|
141 | |||
71 |
return |
|
142 | return default | |
72 |
|
143 | |||
73 | def _create_wsgi_app(self, repo_path, repo_name, config): |
|
144 | def _create_wsgi_app(self, repo_path, repo_name, config): | |
74 | return self.scm_app.create_hg_wsgi_app( |
|
145 | return self.scm_app.create_hg_wsgi_app( |
@@ -47,11 +47,14 b' def get_environ(url):' | |||||
47 | ('/foo/bar?cmd=pushkey&key=tip', 'push'), |
|
47 | ('/foo/bar?cmd=pushkey&key=tip', 'push'), | |
48 | ('/foo/bar?cmd=listkeys&key=tip', 'pull'), |
|
48 | ('/foo/bar?cmd=listkeys&key=tip', 'pull'), | |
49 | ('/foo/bar?cmd=changegroup&key=tip', 'pull'), |
|
49 | ('/foo/bar?cmd=changegroup&key=tip', 'pull'), | |
50 | # Edge case: unknown argument: assume pull |
|
50 | ('/foo/bar?cmd=hello', 'pull'), | |
51 |
('/foo/bar?cmd= |
|
51 | ('/foo/bar?cmd=batch', 'push'), | |
52 |
('/foo/bar?cmd= |
|
52 | ('/foo/bar?cmd=putlfile', 'push'), | |
|
53 | # Edge case: unknown argument: assume push | |||
|
54 | ('/foo/bar?cmd=unknown&key=tip', 'push'), | |||
|
55 | ('/foo/bar?cmd=&key=tip', 'push'), | |||
53 | # Edge case: not cmd argument |
|
56 | # Edge case: not cmd argument | |
54 |
('/foo/bar?key=tip', 'pu |
|
57 | ('/foo/bar?key=tip', 'push'), | |
55 | ]) |
|
58 | ]) | |
56 | def test_get_action(url, expected_action, request_stub): |
|
59 | def test_get_action(url, expected_action, request_stub): | |
57 | app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''}, |
|
60 | app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''}, | |
@@ -60,6 +63,34 b' def test_get_action(url, expected_action' | |||||
60 |
|
63 | |||
61 |
|
64 | |||
62 | @pytest.mark.parametrize( |
|
65 | @pytest.mark.parametrize( | |
|
66 | 'environ, expected_xargs, expected_batch', | |||
|
67 | [ | |||
|
68 | ({}, | |||
|
69 | [''], ['push']), | |||
|
70 | ||||
|
71 | ({'HTTP_X_HGARG_1': ''}, | |||
|
72 | [''], ['push']), | |||
|
73 | ||||
|
74 | ({'HTTP_X_HGARG_1': 'cmds=listkeys+namespace%3Dphases'}, | |||
|
75 | ['listkeys namespace=phases'], ['pull']), | |||
|
76 | ||||
|
77 | ({'HTTP_X_HGARG_1': 'cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'}, | |||
|
78 | ['pushkey namespace=bookmarks,key=bm,old=,new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'], ['push']), | |||
|
79 | ||||
|
80 | ({'HTTP_X_HGARG_1': 'namespace=phases'}, | |||
|
81 | ['namespace=phases'], ['push']), | |||
|
82 | ||||
|
83 | ]) | |||
|
84 | def test_xarg_and_batch_commands(environ, expected_xargs, expected_batch): | |||
|
85 | app = simplehg.SimpleHg | |||
|
86 | ||||
|
87 | result = app._get_xarg_headers(environ) | |||
|
88 | result_batch = app._get_batch_cmd(environ) | |||
|
89 | assert expected_xargs == result | |||
|
90 | assert expected_batch == result_batch | |||
|
91 | ||||
|
92 | ||||
|
93 | @pytest.mark.parametrize( | |||
63 | 'url, expected_repo_name', |
|
94 | 'url, expected_repo_name', | |
64 | [ |
|
95 | [ | |
65 | ('/foo?cmd=unbundle&key=tip', 'foo'), |
|
96 | ('/foo?cmd=unbundle&key=tip', 'foo'), |
General Comments 0
You need to be logged in to leave comments.
Login now