Show More
@@ -25,6 +25,7 b' SimpleHG middleware for handling mercuri' | |||
|
25 | 25 | |
|
26 | 26 | import logging |
|
27 | 27 | import urlparse |
|
28 | import urllib | |
|
28 | 29 | |
|
29 | 30 | from rhodecode.lib import utils |
|
30 | 31 | from rhodecode.lib.ext_json import json |
@@ -51,24 +52,94 b' class SimpleHg(simplevcs.SimpleVCS):' | |||
|
51 | 52 | 'getbundle': 'pull', |
|
52 | 53 | 'stream_out': 'pull', |
|
53 | 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 | 72 | 'unbundle': 'push', |
|
55 | 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 | 121 | def _get_action(self, environ): |
|
59 | 122 | """ |
|
60 | 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 | 126 | :param environ: |
|
64 | 127 | """ |
|
128 | default = 'push' | |
|
65 | 129 | query = urlparse.parse_qs(environ['QUERY_STRING'], |
|
66 | 130 | keep_blank_values=True) |
|
131 | ||
|
67 | 132 | if 'cmd' in query: |
|
68 | 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 | 144 | def _create_wsgi_app(self, repo_path, repo_name, config): |
|
74 | 145 | return self.scm_app.create_hg_wsgi_app( |
@@ -47,11 +47,14 b' def get_environ(url):' | |||
|
47 | 47 | ('/foo/bar?cmd=pushkey&key=tip', 'push'), |
|
48 | 48 | ('/foo/bar?cmd=listkeys&key=tip', 'pull'), |
|
49 | 49 | ('/foo/bar?cmd=changegroup&key=tip', 'pull'), |
|
50 | # Edge case: unknown argument: assume pull | |
|
51 |
('/foo/bar?cmd= |
|
|
52 |
('/foo/bar?cmd= |
|
|
50 | ('/foo/bar?cmd=hello', 'pull'), | |
|
51 | ('/foo/bar?cmd=batch', 'push'), | |
|
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 | 56 | # Edge case: not cmd argument |
|
54 |
('/foo/bar?key=tip', 'pu |
|
|
57 | ('/foo/bar?key=tip', 'push'), | |
|
55 | 58 | ]) |
|
56 | 59 | def test_get_action(url, expected_action, request_stub): |
|
57 | 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 | 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 | 94 | 'url, expected_repo_name', |
|
64 | 95 | [ |
|
65 | 96 | ('/foo?cmd=unbundle&key=tip', 'foo'), |
General Comments 0
You need to be logged in to leave comments.
Login now