67 |
69 |
68 def __getattr__(self, attr): |
70 def __getattr__(self, attr): |
69 if attr in (r'isatty', r'fileno', r'tell', r'seek'): |
71 if attr in (r'isatty', r'fileno', r'tell', r'seek'): |
70 raise AttributeError(attr) |
72 raise AttributeError(attr) |
71 return getattr(self.out, attr) |
73 return getattr(self.out, attr) |
|
74 |
|
75 class channeledmessage(object): |
|
76 """ |
|
77 Write encoded message and metadata to out in the following format: |
|
78 |
|
79 data length (unsigned int), |
|
80 encoded message and metadata, as a flat key-value dict. |
|
81 """ |
|
82 |
|
83 # teach ui that write() can take **opts |
|
84 structured = True |
|
85 |
|
86 def __init__(self, out, channel, encodename, encodefn): |
|
87 self._cout = channeledoutput(out, channel) |
|
88 self.encoding = encodename |
|
89 self._encodefn = encodefn |
|
90 |
|
91 def write(self, data, **opts): |
|
92 opts = pycompat.byteskwargs(opts) |
|
93 opts[b'data'] = data |
|
94 self._cout.write(self._encodefn(opts)) |
|
95 |
|
96 def __getattr__(self, attr): |
|
97 return getattr(self._cout, attr) |
72 |
98 |
73 class channeledinput(object): |
99 class channeledinput(object): |
74 """ |
100 """ |
75 Read data from in_. |
101 Read data from in_. |
76 |
102 |
154 def __getattr__(self, attr): |
180 def __getattr__(self, attr): |
155 if attr in (r'isatty', r'fileno', r'tell', r'seek'): |
181 if attr in (r'isatty', r'fileno', r'tell', r'seek'): |
156 raise AttributeError(attr) |
182 raise AttributeError(attr) |
157 return getattr(self.in_, attr) |
183 return getattr(self.in_, attr) |
158 |
184 |
|
185 _messageencoders = { |
|
186 b'cbor': lambda v: b''.join(cborutil.streamencode(v)), |
|
187 } |
|
188 |
|
189 def _selectmessageencoder(ui): |
|
190 # experimental config: cmdserver.message-encodings |
|
191 encnames = ui.configlist(b'cmdserver', b'message-encodings') |
|
192 for n in encnames: |
|
193 f = _messageencoders.get(n) |
|
194 if f: |
|
195 return n, f |
|
196 raise error.Abort(b'no supported message encodings: %s' |
|
197 % b' '.join(encnames)) |
|
198 |
159 class server(object): |
199 class server(object): |
160 """ |
200 """ |
161 Listens for commands on fin, runs them and writes the output on a channel |
201 Listens for commands on fin, runs them and writes the output on a channel |
162 based stream to fout. |
202 based stream to fout. |
163 """ |
203 """ |
186 |
226 |
187 self.cerr = channeledoutput(fout, 'e') |
227 self.cerr = channeledoutput(fout, 'e') |
188 self.cout = channeledoutput(fout, 'o') |
228 self.cout = channeledoutput(fout, 'o') |
189 self.cin = channeledinput(fin, fout, 'I') |
229 self.cin = channeledinput(fin, fout, 'I') |
190 self.cresult = channeledoutput(fout, 'r') |
230 self.cresult = channeledoutput(fout, 'r') |
|
231 |
|
232 # TODO: add this to help/config.txt when stabilized |
|
233 # ``channel`` |
|
234 # Use separate channel for structured output. (Command-server only) |
|
235 self.cmsg = None |
|
236 if ui.config(b'ui', b'message-output') == b'channel': |
|
237 encname, encfn = _selectmessageencoder(ui) |
|
238 self.cmsg = channeledmessage(fout, b'm', encname, encfn) |
191 |
239 |
192 self.client = fin |
240 self.client = fin |
193 |
241 |
194 def cleanup(self): |
242 def cleanup(self): |
195 """release and restore resources taken during server session""" |
243 """release and restore resources taken during server session""" |
252 # enforced only if cin is a channel. |
300 # enforced only if cin is a channel. |
253 if not util.safehasattr(self.cin, 'fileno'): |
301 if not util.safehasattr(self.cin, 'fileno'): |
254 ui.setconfig('ui', 'nontty', 'true', 'commandserver') |
302 ui.setconfig('ui', 'nontty', 'true', 'commandserver') |
255 |
303 |
256 req = dispatch.request(args[:], copiedui, self.repo, self.cin, |
304 req = dispatch.request(args[:], copiedui, self.repo, self.cin, |
257 self.cout, self.cerr) |
305 self.cout, self.cerr, self.cmsg) |
258 |
306 |
259 try: |
307 try: |
260 ret = dispatch.dispatch(req) & 255 |
308 ret = dispatch.dispatch(req) & 255 |
261 self.cresult.write(struct.pack('>i', int(ret))) |
309 self.cresult.write(struct.pack('>i', int(ret))) |
262 finally: |
310 finally: |
287 def serve(self): |
335 def serve(self): |
288 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities)) |
336 hellomsg = 'capabilities: ' + ' '.join(sorted(self.capabilities)) |
289 hellomsg += '\n' |
337 hellomsg += '\n' |
290 hellomsg += 'encoding: ' + encoding.encoding |
338 hellomsg += 'encoding: ' + encoding.encoding |
291 hellomsg += '\n' |
339 hellomsg += '\n' |
|
340 if self.cmsg: |
|
341 hellomsg += 'message-encoding: %s\n' % self.cmsg.encoding |
292 hellomsg += 'pid: %d' % procutil.getpid() |
342 hellomsg += 'pid: %d' % procutil.getpid() |
293 if util.safehasattr(os, 'getpgid'): |
343 if util.safehasattr(os, 'getpgid'): |
294 hellomsg += '\n' |
344 hellomsg += '\n' |
295 hellomsg += 'pgid: %d' % os.getpgid(0) |
345 hellomsg += 'pgid: %d' % os.getpgid(0) |
296 |
346 |