##// END OF EJS Templates
protocol: send application/mercurial-0.2 responses to capable clients...
Gregory Szorc -
r30764:e75463e3 default
parent child Browse files
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