##// END OF EJS Templates
hg: support new clonebundles_manifest write protocol command...
Mads Kiilerich -
r8789:0245e0eb stable
parent child Browse files
Show More
@@ -1,149 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.config.middleware.simplehg
15 kallithea.config.middleware.simplehg
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 SimpleHg middleware for handling Mercurial protocol requests (push/clone etc.).
18 SimpleHg middleware for handling Mercurial protocol requests (push/clone etc.).
19 It's implemented with basic auth function
19 It's implemented with basic auth function
20
20
21 This file was forked by the Kallithea project in July 2014.
21 This file was forked by the Kallithea project in July 2014.
22 Original author and date, and relevant copyright and licensing information is below:
22 Original author and date, and relevant copyright and licensing information is below:
23 :created_on: Apr 28, 2010
23 :created_on: Apr 28, 2010
24 :author: marcink
24 :author: marcink
25 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 :license: GPLv3, see LICENSE.md for more details.
26 :license: GPLv3, see LICENSE.md for more details.
27
27
28 """
28 """
29
29
30
30
31 import logging
31 import logging
32 import os
32 import os
33 import urllib.parse
33 import urllib.parse
34
34
35 import mercurial.hgweb
35 import mercurial.hgweb
36
36
37 from kallithea.controllers import base
37 from kallithea.controllers import base
38 from kallithea.lib.utils import make_ui
38 from kallithea.lib.utils import make_ui
39 from kallithea.lib.utils2 import safe_bytes
39 from kallithea.lib.utils2 import safe_bytes
40
40
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 def get_header_hgarg(environ):
45 def get_header_hgarg(environ):
46 """Decode the special Mercurial encoding of big requests over multiple headers.
46 """Decode the special Mercurial encoding of big requests over multiple headers.
47 >>> get_header_hgarg({})
47 >>> get_header_hgarg({})
48 ''
48 ''
49 >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'})
49 >>> get_header_hgarg({'HTTP_X_HGARG_0': ' ', 'HTTP_X_HGARG_1': 'a','HTTP_X_HGARG_2': '','HTTP_X_HGARG_3': 'b+c %20'})
50 'ab+c %20'
50 'ab+c %20'
51 """
51 """
52 chunks = []
52 chunks = []
53 i = 1
53 i = 1
54 while True:
54 while True:
55 v = environ.get('HTTP_X_HGARG_%d' % i)
55 v = environ.get('HTTP_X_HGARG_%d' % i)
56 if v is None:
56 if v is None:
57 break
57 break
58 chunks.append(v)
58 chunks.append(v)
59 i += 1
59 i += 1
60 return ''.join(chunks)
60 return ''.join(chunks)
61
61
62
62
63 cmd_mapping = {
63 cmd_mapping = {
64 # 'batch' is not in this list - it is handled explicitly
64 # 'batch' is not in this list - it is handled explicitly
65 'between': 'pull',
65 'between': 'pull',
66 'branches': 'pull',
66 'branches': 'pull',
67 'branchmap': 'pull',
67 'branchmap': 'pull',
68 'capabilities': 'pull',
68 'capabilities': 'pull',
69 'changegroup': 'pull',
69 'changegroup': 'pull',
70 'changegroupsubset': 'pull',
70 'changegroupsubset': 'pull',
71 'changesetdata': 'pull',
71 'changesetdata': 'pull',
72 'clonebundles': 'pull',
72 'clonebundles': 'pull',
73 'clonebundles_manifest': 'pull',
73 'debugwireargs': 'pull',
74 'debugwireargs': 'pull',
74 'filedata': 'pull',
75 'filedata': 'pull',
75 'getbundle': 'pull',
76 'getbundle': 'pull',
76 'getlfile': 'pull',
77 'getlfile': 'pull',
77 'heads': 'pull',
78 'heads': 'pull',
78 'hello': 'pull',
79 'hello': 'pull',
79 'known': 'pull',
80 'known': 'pull',
80 'lheads': 'pull',
81 'lheads': 'pull',
81 'listkeys': 'pull',
82 'listkeys': 'pull',
82 'lookup': 'pull',
83 'lookup': 'pull',
83 'manifestdata': 'pull',
84 'manifestdata': 'pull',
84 'narrow_widen': 'pull',
85 'narrow_widen': 'pull',
85 'protocaps': 'pull',
86 'protocaps': 'pull',
86 'statlfile': 'pull',
87 'statlfile': 'pull',
87 'stream_out': 'pull',
88 'stream_out': 'pull',
88 'pushkey': 'push',
89 'pushkey': 'push',
89 'putlfile': 'push',
90 'putlfile': 'push',
90 'unbundle': 'push',
91 'unbundle': 'push',
91 }
92 }
92
93
93
94
94 class SimpleHg(base.BaseVCSController):
95 class SimpleHg(base.BaseVCSController):
95
96
96 scm_alias = 'hg'
97 scm_alias = 'hg'
97
98
98 @classmethod
99 @classmethod
99 def parse_request(cls, environ):
100 def parse_request(cls, environ):
100 http_accept = environ.get('HTTP_ACCEPT', '')
101 http_accept = environ.get('HTTP_ACCEPT', '')
101 if not http_accept.startswith('application/mercurial'):
102 if not http_accept.startswith('application/mercurial'):
102 return None
103 return None
103 path_info = base.get_path_info(environ)
104 path_info = base.get_path_info(environ)
104 if not path_info.startswith('/'): # it must!
105 if not path_info.startswith('/'): # it must!
105 return None
106 return None
106
107
107 class parsed_request(object):
108 class parsed_request(object):
108 repo_name = path_info[1:].rstrip('/')
109 repo_name = path_info[1:].rstrip('/')
109
110
110 query_string = environ['QUERY_STRING']
111 query_string = environ['QUERY_STRING']
111
112
112 action = None
113 action = None
113 for qry in query_string.split('&'):
114 for qry in query_string.split('&'):
114 parts = qry.split('=', 1)
115 parts = qry.split('=', 1)
115 if len(parts) == 2 and parts[0] == 'cmd':
116 if len(parts) == 2 and parts[0] == 'cmd':
116 cmd = parts[1]
117 cmd = parts[1]
117 if cmd == 'batch':
118 if cmd == 'batch':
118 hgarg = get_header_hgarg(environ)
119 hgarg = get_header_hgarg(environ)
119 if not hgarg.startswith('cmds='):
120 if not hgarg.startswith('cmds='):
120 action = 'push' # paranoid and safe
121 action = 'push' # paranoid and safe
121 break
122 break
122 action = 'pull'
123 action = 'pull'
123 for cmd_arg in hgarg[5:].split(';'):
124 for cmd_arg in hgarg[5:].split(';'):
124 cmd, _args = urllib.parse.unquote_plus(cmd_arg).split(' ', 1)
125 cmd, _args = urllib.parse.unquote_plus(cmd_arg).split(' ', 1)
125 op = cmd_mapping.get(cmd, 'push')
126 op = cmd_mapping.get(cmd, 'push')
126 if op != 'pull':
127 if op != 'pull':
127 assert op == 'push'
128 assert op == 'push'
128 action = 'push'
129 action = 'push'
129 break
130 break
130 else:
131 else:
131 action = cmd_mapping.get(cmd, 'push')
132 action = cmd_mapping.get(cmd, 'push')
132 break # only process one cmd
133 break # only process one cmd
133
134
134 return parsed_request
135 return parsed_request
135
136
136 def _make_app(self, parsed_request):
137 def _make_app(self, parsed_request):
137 """
138 """
138 Make an hgweb wsgi application.
139 Make an hgweb wsgi application.
139 """
140 """
140 repo_name = parsed_request.repo_name
141 repo_name = parsed_request.repo_name
141 repo_path = os.path.join(self.basepath, repo_name)
142 repo_path = os.path.join(self.basepath, repo_name)
142 baseui = make_ui(repo_path=repo_path)
143 baseui = make_ui(repo_path=repo_path)
143 hgweb_app = mercurial.hgweb.hgweb(safe_bytes(repo_path), name=safe_bytes(repo_name), baseui=baseui)
144 hgweb_app = mercurial.hgweb.hgweb(safe_bytes(repo_path), name=safe_bytes(repo_name), baseui=baseui)
144
145
145 def wrapper_app(environ, start_response):
146 def wrapper_app(environ, start_response):
146 environ['REPO_NAME'] = repo_name # used by mercurial.hgweb.hgweb
147 environ['REPO_NAME'] = repo_name # used by mercurial.hgweb.hgweb
147 return hgweb_app(environ, start_response)
148 return hgweb_app(environ, start_response)
148
149
149 return wrapper_app
150 return wrapper_app
General Comments 0
You need to be logged in to leave comments. Login now