81 def restore(self): |
83 def restore(self): |
82 val = self.ui.fout.getvalue() |
84 val = self.ui.fout.getvalue() |
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) |
107 return [] |
165 return [] |
108 elif isinstance(rsp, wireproto.streamres): |
166 elif isinstance(rsp, wireproto.streamres): |
109 if rsp.reader: |
167 if rsp.reader: |
110 gen = iter(lambda: rsp.reader.read(32768), '') |
168 gen = iter(lambda: rsp.reader.read(32768), '') |
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() |
121 rsp = '%d\n%s' % (rsp.res, val) |
185 rsp = '%d\n%s' % (rsp.res, val) |
122 req.respond(HTTP_OK, HGTYPE, body=rsp) |
186 req.respond(HTTP_OK, HGTYPE, body=rsp) |