Show More
@@ -1629,6 +1629,18 b' Controls generic server settings.' | |||||
1629 |
|
1629 | |||
1630 | This option only impacts the HTTP server. |
|
1630 | This option only impacts the HTTP server. | |
1631 |
|
1631 | |||
|
1632 | ``zstdlevel`` | |||
|
1633 | Integer between ``1`` and ``22`` that controls the zstd compression level | |||
|
1634 | for wire protocol commands. ``1`` is the minimal amount of compression and | |||
|
1635 | ``22`` is the highest amount of compression. | |||
|
1636 | ||||
|
1637 | The default (``3``) should be significantly faster than zlib while likely | |||
|
1638 | delivering better compression ratios. | |||
|
1639 | ||||
|
1640 | This option only impacts the HTTP server. | |||
|
1641 | ||||
|
1642 | See also ``server.zliblevel``. | |||
|
1643 | ||||
1632 | ``smtp`` |
|
1644 | ``smtp`` | |
1633 | -------- |
|
1645 | -------- | |
1634 |
|
1646 |
@@ -8,6 +8,7 b'' | |||||
8 | from __future__ import absolute_import |
|
8 | from __future__ import absolute_import | |
9 |
|
9 | |||
10 | import cgi |
|
10 | import cgi | |
|
11 | import struct | |||
11 |
|
12 | |||
12 | from .common import ( |
|
13 | from .common import ( | |
13 | HTTP_OK, |
|
14 | HTTP_OK, | |
@@ -23,6 +24,7 b' urlerr = util.urlerr' | |||||
23 | urlreq = util.urlreq |
|
24 | urlreq = util.urlreq | |
24 |
|
25 | |||
25 | HGTYPE = 'application/mercurial-0.1' |
|
26 | HGTYPE = 'application/mercurial-0.1' | |
|
27 | HGTYPE2 = 'application/mercurial-0.2' | |||
26 | HGERRTYPE = 'application/hg-error' |
|
28 | HGERRTYPE = 'application/hg-error' | |
27 |
|
29 | |||
28 | def decodevaluefromheaders(req, headerprefix): |
|
30 | def decodevaluefromheaders(req, headerprefix): | |
@@ -83,24 +85,80 b' class webproto(wireproto.abstractserverp' | |||||
83 | self.ui.ferr, self.ui.fout = self.oldio |
|
85 | self.ui.ferr, self.ui.fout = self.oldio | |
84 | return val |
|
86 | return val | |
85 |
|
87 | |||
86 | def compresschunks(self, chunks): |
|
|||
87 | # Don't allow untrusted settings because disabling compression or |
|
|||
88 | # setting a very high compression level could lead to flooding |
|
|||
89 | # the server's network or CPU. |
|
|||
90 | opts = {'level': self.ui.configint('server', 'zliblevel', -1)} |
|
|||
91 | return util.compengines['zlib'].compressstream(chunks, opts) |
|
|||
92 |
|
||||
93 | def _client(self): |
|
88 | def _client(self): | |
94 | return 'remote:%s:%s:%s' % ( |
|
89 | return 'remote:%s:%s:%s' % ( | |
95 | self.req.env.get('wsgi.url_scheme') or 'http', |
|
90 | self.req.env.get('wsgi.url_scheme') or 'http', | |
96 | urlreq.quote(self.req.env.get('REMOTE_HOST', '')), |
|
91 | urlreq.quote(self.req.env.get('REMOTE_HOST', '')), | |
97 | urlreq.quote(self.req.env.get('REMOTE_USER', ''))) |
|
92 | urlreq.quote(self.req.env.get('REMOTE_USER', ''))) | |
98 |
|
93 | |||
|
94 | def responsetype(self, v1compressible=False): | |||
|
95 | """Determine the appropriate response type and compression settings. | |||
|
96 | ||||
|
97 | The ``v1compressible`` argument states whether the response with | |||
|
98 | application/mercurial-0.1 media types should be zlib compressed. | |||
|
99 | ||||
|
100 | Returns a tuple of (mediatype, compengine, engineopts). | |||
|
101 | """ | |||
|
102 | # For now, if it isn't compressible in the old world, it's never | |||
|
103 | # compressible. We can change this to send uncompressed 0.2 payloads | |||
|
104 | # later. | |||
|
105 | if not v1compressible: | |||
|
106 | return HGTYPE, None, None | |||
|
107 | ||||
|
108 | # Determine the response media type and compression engine based | |||
|
109 | # on the request parameters. | |||
|
110 | protocaps = decodevaluefromheaders(self.req, 'X-HgProto').split(' ') | |||
|
111 | ||||
|
112 | if '0.2' in protocaps: | |||
|
113 | # Default as defined by wire protocol spec. | |||
|
114 | compformats = ['zlib', 'none'] | |||
|
115 | for cap in protocaps: | |||
|
116 | if cap.startswith('comp='): | |||
|
117 | compformats = cap[5:].split(',') | |||
|
118 | break | |||
|
119 | ||||
|
120 | # Now find an agreed upon compression format. | |||
|
121 | for engine in wireproto.supportedcompengines(self.ui, self, | |||
|
122 | util.SERVERROLE): | |||
|
123 | if engine.wireprotosupport().name in compformats: | |||
|
124 | opts = {} | |||
|
125 | level = self.ui.configint('server', | |||
|
126 | '%slevel' % engine.name()) | |||
|
127 | if level is not None: | |||
|
128 | opts['level'] = level | |||
|
129 | ||||
|
130 | return HGTYPE2, engine, opts | |||
|
131 | ||||
|
132 | # No mutually supported compression format. Fall back to the | |||
|
133 | # legacy protocol. | |||
|
134 | ||||
|
135 | # Don't allow untrusted settings because disabling compression or | |||
|
136 | # setting a very high compression level could lead to flooding | |||
|
137 | # the server's network or CPU. | |||
|
138 | opts = {'level': self.ui.configint('server', 'zliblevel', -1)} | |||
|
139 | return HGTYPE, util.compengines['zlib'], opts | |||
|
140 | ||||
99 | def iscmd(cmd): |
|
141 | def iscmd(cmd): | |
100 | return cmd in wireproto.commands |
|
142 | return cmd in wireproto.commands | |
101 |
|
143 | |||
102 | def call(repo, req, cmd): |
|
144 | def call(repo, req, cmd): | |
103 | p = webproto(req, repo.ui) |
|
145 | p = webproto(req, repo.ui) | |
|
146 | ||||
|
147 | def genversion2(gen, compress, engine, engineopts): | |||
|
148 | # application/mercurial-0.2 always sends a payload header | |||
|
149 | # identifying the compression engine. | |||
|
150 | name = engine.wireprotosupport().name | |||
|
151 | assert 0 < len(name) < 256 | |||
|
152 | yield struct.pack('B', len(name)) | |||
|
153 | yield name | |||
|
154 | ||||
|
155 | if compress: | |||
|
156 | for chunk in engine.compressstream(gen, opts=engineopts): | |||
|
157 | yield chunk | |||
|
158 | else: | |||
|
159 | for chunk in gen: | |||
|
160 | yield chunk | |||
|
161 | ||||
104 | rsp = wireproto.dispatch(repo, p, cmd) |
|
162 | rsp = wireproto.dispatch(repo, p, cmd) | |
105 | if isinstance(rsp, str): |
|
163 | if isinstance(rsp, str): | |
106 | req.respond(HTTP_OK, HGTYPE, body=rsp) |
|
164 | req.respond(HTTP_OK, HGTYPE, body=rsp) | |
@@ -111,10 +169,16 b' def call(repo, req, cmd):' | |||||
111 | else: |
|
169 | else: | |
112 | gen = rsp.gen |
|
170 | gen = rsp.gen | |
113 |
|
171 | |||
114 | if rsp.v1compressible: |
|
172 | # This code for compression should not be streamres specific. It | |
115 | gen = p.compresschunks(gen) |
|
173 | # is here because we only compress streamres at the moment. | |
|
174 | mediatype, engine, engineopts = p.responsetype(rsp.v1compressible) | |||
116 |
|
175 | |||
117 | req.respond(HTTP_OK, HGTYPE) |
|
176 | if mediatype == HGTYPE and rsp.v1compressible: | |
|
177 | gen = engine.compressstream(gen, engineopts) | |||
|
178 | elif mediatype == HGTYPE2: | |||
|
179 | gen = genversion2(gen, rsp.v1compressible, engine, engineopts) | |||
|
180 | ||||
|
181 | req.respond(HTTP_OK, mediatype) | |||
118 | return gen |
|
182 | return gen | |
119 | elif isinstance(rsp, wireproto.pushres): |
|
183 | elif isinstance(rsp, wireproto.pushres): | |
120 | val = p.restore() |
|
184 | val = p.restore() |
@@ -35,6 +35,13 b" if '--json' in sys.argv:" | |||||
35 | sys.argv.remove('--json') |
|
35 | sys.argv.remove('--json') | |
36 | formatjson = True |
|
36 | formatjson = True | |
37 |
|
37 | |||
|
38 | hgproto = None | |||
|
39 | if '--hgproto' in sys.argv: | |||
|
40 | idx = sys.argv.index('--hgproto') | |||
|
41 | hgproto = sys.argv[idx + 1] | |||
|
42 | sys.argv.pop(idx) | |||
|
43 | sys.argv.pop(idx) | |||
|
44 | ||||
38 | tag = None |
|
45 | tag = None | |
39 | def request(host, path, show): |
|
46 | def request(host, path, show): | |
40 | assert not path.startswith('/'), path |
|
47 | assert not path.startswith('/'), path | |
@@ -42,6 +49,8 b' def request(host, path, show):' | |||||
42 | headers = {} |
|
49 | headers = {} | |
43 | if tag: |
|
50 | if tag: | |
44 | headers['If-None-Match'] = tag |
|
51 | headers['If-None-Match'] = tag | |
|
52 | if hgproto: | |||
|
53 | headers['X-HgProto-1'] = hgproto | |||
45 |
|
54 | |||
46 | conn = httplib.HTTPConnection(host) |
|
55 | conn = httplib.HTTPConnection(host) | |
47 | conn.request("GET", '/' + path, None, headers) |
|
56 | conn.request("GET", '/' + path, None, headers) |
@@ -42,3 +42,126 b' Order of engines can also change' | |||||
42 | compression=none,zlib |
|
42 | compression=none,zlib | |
43 |
|
43 | |||
44 | $ killdaemons.py |
|
44 | $ killdaemons.py | |
|
45 | ||||
|
46 | Start a default server again | |||
|
47 | ||||
|
48 | $ hg -R server serve -p $HGPORT -d --pid-file hg.pid | |||
|
49 | $ cat hg.pid > $DAEMON_PIDS | |||
|
50 | ||||
|
51 | Server should send application/mercurial-0.1 to clients if no Accept is used | |||
|
52 | ||||
|
53 | $ get-with-headers.py --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' - | |||
|
54 | 200 Script output follows | |||
|
55 | content-type: application/mercurial-0.1 | |||
|
56 | date: * (glob) | |||
|
57 | server: * (glob) | |||
|
58 | transfer-encoding: chunked | |||
|
59 | ||||
|
60 | Server should send application/mercurial-0.1 when client says it wants it | |||
|
61 | ||||
|
62 | $ get-with-headers.py --hgproto '0.1' --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' - | |||
|
63 | 200 Script output follows | |||
|
64 | content-type: application/mercurial-0.1 | |||
|
65 | date: * (glob) | |||
|
66 | server: * (glob) | |||
|
67 | transfer-encoding: chunked | |||
|
68 | ||||
|
69 | Server should send application/mercurial-0.2 when client says it wants it | |||
|
70 | ||||
|
71 | $ get-with-headers.py --hgproto '0.2' --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' - | |||
|
72 | 200 Script output follows | |||
|
73 | content-type: application/mercurial-0.2 | |||
|
74 | date: * (glob) | |||
|
75 | server: * (glob) | |||
|
76 | transfer-encoding: chunked | |||
|
77 | ||||
|
78 | $ get-with-headers.py --hgproto '0.1 0.2' --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' - | |||
|
79 | 200 Script output follows | |||
|
80 | content-type: application/mercurial-0.2 | |||
|
81 | date: * (glob) | |||
|
82 | server: * (glob) | |||
|
83 | transfer-encoding: chunked | |||
|
84 | ||||
|
85 | Requesting a compression format that server doesn't support results will fall back to 0.1 | |||
|
86 | ||||
|
87 | $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' - | |||
|
88 | 200 Script output follows | |||
|
89 | content-type: application/mercurial-0.1 | |||
|
90 | date: * (glob) | |||
|
91 | server: * (glob) | |||
|
92 | transfer-encoding: chunked | |||
|
93 | ||||
|
94 | #if zstd | |||
|
95 | zstd is used if available | |||
|
96 | ||||
|
97 | $ get-with-headers.py --hgproto '0.2 comp=zstd' 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
98 | $ f --size --hexdump --bytes 36 --sha1 resp | |||
|
99 | resp: size=248, sha1=4d8d8f87fb82bd542ce52881fdc94f850748 | |||
|
100 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
101 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 73 74 64 |t follows...zstd| | |||
|
102 | 0020: 28 b5 2f fd |(./.| | |||
|
103 | ||||
|
104 | #endif | |||
|
105 | ||||
|
106 | application/mercurial-0.2 is not yet used on non-streaming responses | |||
|
107 | ||||
|
108 | $ get-with-headers.py --hgproto '0.2' 127.0.0.1:$HGPORT '?cmd=heads' - | |||
|
109 | 200 Script output follows | |||
|
110 | content-length: 41 | |||
|
111 | content-type: application/mercurial-0.1 | |||
|
112 | date: * (glob) | |||
|
113 | server: * (glob) | |||
|
114 | ||||
|
115 | e93700bd72895c5addab234c56d4024b487a362f | |||
|
116 | ||||
|
117 | Now test protocol preference usage | |||
|
118 | ||||
|
119 | $ killdaemons.py | |||
|
120 | $ hg --config server.compressionengines=none,zlib -R server serve -p $HGPORT -d --pid-file hg.pid | |||
|
121 | $ cat hg.pid > $DAEMON_PIDS | |||
|
122 | ||||
|
123 | No Accept will send 0.1+zlib, even though "none" is preferred b/c "none" isn't supported on 0.1 | |||
|
124 | ||||
|
125 | $ get-with-headers.py --headeronly 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' Content-Type | |||
|
126 | 200 Script output follows | |||
|
127 | content-type: application/mercurial-0.1 | |||
|
128 | ||||
|
129 | $ get-with-headers.py 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
130 | $ f --size --hexdump --bytes 28 --sha1 resp | |||
|
131 | resp: size=227, sha1=35a4c074da74f32f5440da3cbf04 | |||
|
132 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
133 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x| | |||
|
134 | ||||
|
135 | Explicit 0.1 will send zlib because "none" isn't supported on 0.1 | |||
|
136 | ||||
|
137 | $ get-with-headers.py --hgproto '0.1' 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
138 | $ f --size --hexdump --bytes 28 --sha1 resp | |||
|
139 | resp: size=227, sha1=35a4c074da74f32f5440da3cbf04 | |||
|
140 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
141 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 78 |t follows..x| | |||
|
142 | ||||
|
143 | 0.2 with no compression will get "none" because that is server's preference | |||
|
144 | (spec says ZL and UN are implicitly supported) | |||
|
145 | ||||
|
146 | $ get-with-headers.py --hgproto '0.2' 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
147 | $ f --size --hexdump --bytes 32 --sha1 resp | |||
|
148 | resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83 | |||
|
149 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
150 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none| | |||
|
151 | ||||
|
152 | Client receives server preference even if local order doesn't match | |||
|
153 | ||||
|
154 | $ get-with-headers.py --hgproto '0.2 comp=zlib,none' 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
155 | $ f --size --hexdump --bytes 32 --sha1 resp | |||
|
156 | resp: size=432, sha1=ac931b412ec185a02e0e5bcff98dac83 | |||
|
157 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
158 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 6e 6f 6e 65 |t follows...none| | |||
|
159 | ||||
|
160 | Client receives only supported format even if not server preferred format | |||
|
161 | ||||
|
162 | $ get-with-headers.py --hgproto '0.2 comp=zlib' 127.0.0.1:$HGPORT '?cmd=getbundle&heads=e93700bd72895c5addab234c56d4024b487a362f&common=0000000000000000000000000000000000000000' > resp | |||
|
163 | $ f --size --hexdump --bytes 33 --sha1 resp | |||
|
164 | resp: size=232, sha1=a1c727f0c9693ca15742a75c30419bc36 | |||
|
165 | 0000: 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu| | |||
|
166 | 0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib| | |||
|
167 | 0020: 78 |x| |
General Comments 0
You need to be logged in to leave comments.
Login now