56
109
# TODO: Better way of representing the body for commands that take it,
57
110
# and allow it to be streamed into the server.
59
def __init__(self, backing_transport):
112
def __init__(self, backing_transport, commands):
115
:param backing_transport: a Transport to handle requests for.
116
:param commands: a registry mapping command names to SmartServerRequest
117
subclasses. e.g. bzrlib.transport.smart.vfs.vfs_commands.
60
119
self._backing_transport = backing_transport
61
self._converted_command = False
62
self.finished_reading = False
120
self._commands = commands
63
121
self._body_bytes = ''
64
122
self.response = None
123
self.finished_reading = False
66
126
def accept_body(self, bytes):
69
This should be overriden for each command that desired body data to
70
handle the right format of that data. I.e. plain bytes, a bundle etc.
72
The deserialisation into that format should be done in the Protocol
73
object. Set self.desired_body_format to the format your method will
127
"""Accept body data."""
129
# TODO: This should be overriden for each command that desired body data
130
# to handle the right format of that data, i.e. plain bytes, a bundle,
131
# etc. The deserialisation into that format should be done in the
76
134
# default fallback is to accumulate bytes.
77
135
self._body_bytes += bytes
79
def _end_of_body_handler(self):
80
"""An unimplemented end of body handler."""
81
raise NotImplementedError(self._end_of_body_handler)
84
"""Answer a version request with my version."""
85
return SmartServerResponse(('ok', '1'))
87
def do_has(self, relpath):
88
r = self._backing_transport.has(relpath) and 'yes' or 'no'
89
return SmartServerResponse((r,))
91
def do_get(self, relpath):
92
backing_bytes = self._backing_transport.get_bytes(relpath)
93
return SmartServerResponse(('ok',), backing_bytes)
95
def _deserialise_optional_mode(self, mode):
96
# XXX: FIXME this should be on the protocol object.
102
def do_append(self, relpath, mode):
103
self._converted_command = True
104
self._relpath = relpath
105
self._mode = self._deserialise_optional_mode(mode)
106
self._end_of_body_handler = self._handle_do_append_end
108
def _handle_do_append_end(self):
109
old_length = self._backing_transport.append_bytes(
110
self._relpath, self._body_bytes, self._mode)
111
self.response = SmartServerResponse(('appended', '%d' % old_length))
113
def do_delete(self, relpath):
114
self._backing_transport.delete(relpath)
116
def do_iter_files_recursive(self, relpath):
117
transport = self._backing_transport.clone(relpath)
118
filenames = transport.iter_files_recursive()
119
return SmartServerResponse(('names',) + tuple(filenames))
121
def do_list_dir(self, relpath):
122
filenames = self._backing_transport.list_dir(relpath)
123
return SmartServerResponse(('names',) + tuple(filenames))
125
def do_mkdir(self, relpath, mode):
126
self._backing_transport.mkdir(relpath,
127
self._deserialise_optional_mode(mode))
129
def do_move(self, rel_from, rel_to):
130
self._backing_transport.move(rel_from, rel_to)
132
def do_put(self, relpath, mode):
133
self._converted_command = True
134
self._relpath = relpath
135
self._mode = self._deserialise_optional_mode(mode)
136
self._end_of_body_handler = self._handle_do_put
138
def _handle_do_put(self):
139
self._backing_transport.put_bytes(self._relpath,
140
self._body_bytes, self._mode)
141
self.response = SmartServerResponse(('ok',))
143
def _deserialise_offsets(self, text):
144
# XXX: FIXME this should be on the protocol object.
146
for line in text.split('\n'):
149
start, length = line.split(',')
150
offsets.append((int(start), int(length)))
153
def do_put_non_atomic(self, relpath, mode, create_parent, dir_mode):
154
self._converted_command = True
155
self._end_of_body_handler = self._handle_put_non_atomic
156
self._relpath = relpath
157
self._dir_mode = self._deserialise_optional_mode(dir_mode)
158
self._mode = self._deserialise_optional_mode(mode)
159
# a boolean would be nicer XXX
160
self._create_parent = (create_parent == 'T')
162
def _handle_put_non_atomic(self):
163
self._backing_transport.put_bytes_non_atomic(self._relpath,
166
create_parent_dir=self._create_parent,
167
dir_mode=self._dir_mode)
168
self.response = SmartServerResponse(('ok',))
170
def do_readv(self, relpath):
171
self._converted_command = True
172
self._end_of_body_handler = self._handle_readv_offsets
173
self._relpath = relpath
175
137
def end_of_body(self):
176
138
"""No more body data will be received."""
177
self._run_handler_code(self._end_of_body_handler, (), {})
139
self._run_handler_code(self._command.do_body, (self._body_bytes,), {})
178
140
# cannot read after this.
179
141
self.finished_reading = True
181
def _handle_readv_offsets(self):
182
"""accept offsets for a readv request."""
183
offsets = self._deserialise_offsets(self._body_bytes)
184
backing_bytes = ''.join(bytes for offset, bytes in
185
self._backing_transport.readv(self._relpath, offsets))
186
self.response = SmartServerResponse(('readv',), backing_bytes)
188
def do_rename(self, rel_from, rel_to):
189
self._backing_transport.rename(rel_from, rel_to)
191
def do_rmdir(self, relpath):
192
self._backing_transport.rmdir(relpath)
194
def do_stat(self, relpath):
195
stat = self._backing_transport.stat(relpath)
196
return SmartServerResponse(('stat', str(stat.st_size), oct(stat.st_mode)))
198
def do_get_bundle(self, path, revision_id):
199
# open transport relative to our base
200
t = self._backing_transport.clone(path)
201
control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
202
repo = control.open_repository()
203
tmpf = tempfile.TemporaryFile()
204
base_revision = revision.NULL_REVISION
205
write_bundle(repo, revision_id, base_revision, tmpf)
207
return SmartServerResponse((), tmpf.read())
209
143
def dispatch_command(self, cmd, args):
210
144
"""Deprecated compatibility method.""" # XXX XXX
211
func = getattr(self, 'do_' + cmd, None)
146
command = self._commands.get(cmd)
213
148
raise errors.SmartProtocolError("bad request %r" % (cmd,))
214
self._run_handler_code(func, args, {})
149
self._command = command(self._backing_transport)
150
self._run_handler_code(self._command.execute, args, {})
216
152
def _run_handler_code(self, callable, args, kwargs):
217
153
"""Run some handler specific code 'callable'.
202
class HelloRequest(SmartServerRequest):
203
"""Answer a version request with my version."""
206
return SmartServerResponse(('ok', '1'))
209
class GetBundleRequest(SmartServerRequest):
210
"""Get a bundle of from the null revision to the specified revision."""
212
def do(self, path, revision_id):
213
# open transport relative to our base
214
t = self._backing_transport.clone(path)
215
control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
216
repo = control.open_repository()
217
tmpf = tempfile.TemporaryFile()
218
base_revision = revision.NULL_REVISION
219
write_bundle(repo, revision_id, base_revision, tmpf)
221
return SmartServerResponse((), tmpf.read())
224
class SmartServerIsReadonly(SmartServerRequest):
225
# XXX: this request method belongs somewhere else.
228
if self._backing_transport.is_readonly():
232
return SmartServerResponse((answer,))
235
request_handlers = registry.Registry()
236
request_handlers.register_lazy(
237
'append', 'bzrlib.smart.vfs', 'AppendRequest')
238
request_handlers.register_lazy(
239
'delete', 'bzrlib.smart.vfs', 'DeleteRequest')
240
request_handlers.register_lazy(
241
'get', 'bzrlib.smart.vfs', 'GetRequest')
242
request_handlers.register_lazy(
243
'get_bundle', 'bzrlib.smart.request', 'GetBundleRequest')
244
request_handlers.register_lazy(
245
'has', 'bzrlib.smart.vfs', 'HasRequest')
246
request_handlers.register_lazy(
247
'hello', 'bzrlib.smart.request', 'HelloRequest')
248
request_handlers.register_lazy(
249
'iter_files_recursive', 'bzrlib.smart.vfs', 'IterFilesRecursiveRequest')
250
request_handlers.register_lazy(
251
'list_dir', 'bzrlib.smart.vfs', 'ListDirRequest')
252
request_handlers.register_lazy(
253
'mkdir', 'bzrlib.smart.vfs', 'MkdirRequest')
254
request_handlers.register_lazy(
255
'move', 'bzrlib.smart.vfs', 'MoveRequest')
256
request_handlers.register_lazy(
257
'put', 'bzrlib.smart.vfs', 'PutRequest')
258
request_handlers.register_lazy(
259
'put_non_atomic', 'bzrlib.smart.vfs', 'PutNonAtomicRequest')
260
request_handlers.register_lazy(
261
'readv', 'bzrlib.smart.vfs', 'ReadvRequest')
262
request_handlers.register_lazy(
263
'rename', 'bzrlib.smart.vfs', 'RenameRequest')
264
request_handlers.register_lazy(
265
'rmdir', 'bzrlib.smart.vfs', 'RmdirRequest')
266
request_handlers.register_lazy(
267
'stat', 'bzrlib.smart.vfs', 'StatRequest')