Show More
@@ -1629,6 +1629,18 b' Controls generic server settings.' | |||
|
1629 | 1629 | |
|
1630 | 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 | 1644 | ``smtp`` |
|
1633 | 1645 | -------- |
|
1634 | 1646 |
@@ -8,6 +8,7 b'' | |||
|
8 | 8 | from __future__ import absolute_import |
|
9 | 9 | |
|
10 | 10 | import cgi |
|
11 | import struct | |
|
11 | 12 | |
|
12 | 13 | from .common import ( |
|
13 | 14 | HTTP_OK, |
@@ -23,6 +24,7 b' urlerr = util.urlerr' | |||
|
23 | 24 | urlreq = util.urlreq |
|
24 | 25 | |
|
25 | 26 | HGTYPE = 'application/mercurial-0.1' |
|
27 | HGTYPE2 = 'application/mercurial-0.2' | |
|
26 | 28 | HGERRTYPE = 'application/hg-error' |
|
27 | 29 | |
|
28 | 30 | def decodevaluefromheaders(req, headerprefix): |
@@ -83,24 +85,80 b' class webproto(wireproto.abstractserverp' | |||
|
83 | 85 | self.ui.ferr, self.ui.fout = self.oldio |
|
84 | 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 | 88 | def _client(self): |
|
94 | 89 | return 'remote:%s:%s:%s' % ( |
|
95 | 90 | self.req.env.get('wsgi.url_scheme') or 'http', |
|
96 | 91 | urlreq.quote(self.req.env.get('REMOTE_HOST', '')), |
|
97 | 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 | 141 | def iscmd(cmd): |
|
100 | 142 | return cmd in wireproto.commands |
|
101 | 143 | |
|
102 | 144 | def call(repo, req, cmd): |
|
103 | 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 | 162 | rsp = wireproto.dispatch(repo, p, cmd) |
|
105 | 163 | if isinstance(rsp, str): |
|
106 | 164 | req.respond(HTTP_OK, HGTYPE, body=rsp) |
@@ -111,10 +169,16 b' def call(repo, req, cmd):' | |||
|
111 | 169 | else: |
|
112 | 170 | gen = rsp.gen |
|
113 | 171 | |
|
114 | if rsp.v1compressible: | |
|
115 | gen = p.compresschunks(gen) | |
|
172 | # This code for compression should not be streamres specific. It | |
|
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 | 182 | return gen |
|
119 | 183 | elif isinstance(rsp, wireproto.pushres): |
|
120 | 184 | val = p.restore() |
@@ -35,6 +35,13 b" if '--json' in sys.argv:" | |||
|
35 | 35 | sys.argv.remove('--json') |
|
36 | 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 | 45 | tag = None |
|
39 | 46 | def request(host, path, show): |
|
40 | 47 | assert not path.startswith('/'), path |
@@ -42,6 +49,8 b' def request(host, path, show):' | |||
|
42 | 49 | headers = {} |
|
43 | 50 | if tag: |
|
44 | 51 | headers['If-None-Match'] = tag |
|
52 | if hgproto: | |
|
53 | headers['X-HgProto-1'] = hgproto | |
|
45 | 54 | |
|
46 | 55 | conn = httplib.HTTPConnection(host) |
|
47 | 56 | conn.request("GET", '/' + path, None, headers) |
@@ -42,3 +42,126 b' Order of engines can also change' | |||
|
42 | 42 | compression=none,zlib |
|
43 | 43 | |
|
44 | 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