~bzr-pqm/bzr/bzr.dev

1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1
# Copyright (C) 2006 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Smart-server protocol, client and server.
18
19
Requests are sent as a command and list of arguments, followed by optional
20
bulk body data.  Responses are similarly a response and list of arguments,
21
followed by bulk body data. ::
22
23
  SEP := '\001'
24
    Fields are separated by Ctrl-A.
2018.2.25 by Andrew Bennetts
Tweak psuedo-EBNF to be in sync with the code.
25
  BULK_DATA := CHUNK TRAILER
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
26
    Chunks can be repeated as many times as necessary.
27
  CHUNK := CHUNK_LEN CHUNK_BODY
28
  CHUNK_LEN := DIGIT+ NEWLINE
29
    Gives the number of bytes in the following chunk.
30
  CHUNK_BODY := BYTE[chunk_len]
31
  TRAILER := SUCCESS_TRAILER | ERROR_TRAILER
32
  SUCCESS_TRAILER := 'done' NEWLINE
33
  ERROR_TRAILER := 
34
35
Paths are passed across the network.  The client needs to see a namespace that
36
includes any repository that might need to be referenced, and the client needs
37
to know about a root directory beyond which it cannot ascend.
38
39
Servers run over ssh will typically want to be able to access any path the user 
40
can access.  Public servers on the other hand (which might be over http, ssh
41
or tcp) will typically want to restrict access to only a particular directory 
42
and its children, so will want to do a software virtual root at that level.
43
In other words they'll want to rewrite incoming paths to be under that level
44
(and prevent escaping using ../ tricks.)
45
46
URLs that include ~ should probably be passed across to the server verbatim
47
and the server can expand them.  This will proably not be meaningful when 
48
limited to a directory?
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
49
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
50
At the bottom level socket, pipes, HTTP server.  For sockets, we have the idea
51
that you have multiple requests and get a read error because the other side did
52
shutdown.  For pipes we have read pipe which will have a zero read which marks
53
end-of-file.  For HTTP server environment there is not end-of-stream because
54
each request coming into the server is independent.
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
55
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
56
So we need a wrapper around pipes and sockets to seperate out requests from
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
57
substrate and this will give us a single model which is consist for HTTP,
58
sockets and pipes.
59
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
60
Server-side
61
-----------
62
63
 MEDIUM  (factory for protocol, reads bytes & pushes to protocol,
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
64
          uses protocol to detect end-of-request, sends written
65
          bytes to client) e.g. socket, pipe, HTTP request handler.
66
  ^
67
  | bytes.
68
  v
69
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
70
PROTOCOL  (serialization, deserialization)  accepts bytes for one
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
71
          request, decodes according to internal state, pushes
72
          structured data to handler.  accepts structured data from
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
73
          handler and encodes and writes to the medium.  factory for
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
74
          handler.
75
  ^
76
  | structured data
77
  v
78
79
HANDLER   (domain logic) accepts structured data, operates state
80
          machine until the request can be satisfied,
81
          sends structured data to the protocol.
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
82
83
84
Client-side
85
-----------
86
87
 CLIENT             domain logic, accepts domain requests, generated structured
88
                    data, reads structured data from responses and turns into
89
                    domain data.  Sends structured data to the protocol.
90
                    Operates state machines until the request can be delivered
91
                    (e.g. reading from a bundle generated in bzrlib to deliver a
92
                    complete request).
93
94
                    Possibly this should just be RemoteBzrDir, RemoteTransport,
95
                    ...
96
  ^
97
  | structured data
98
  v
99
2018.2.26 by Andrew Bennetts
Changes prompted by j-a-meinel's review.
100
PROTOCOL  (serialization, deserialization)  accepts structured data for one
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
101
          request, encodes and writes to the medium.  Reads bytes from the
102
          medium, decodes and allows the client to read structured data.
103
  ^
104
  | bytes.
105
  v
106
107
 MEDIUM  (accepts bytes from the protocol & delivers to the remote server.
108
          Allows the potocol to read bytes e.g. socket, pipe, HTTP request.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
109
"""
110
111
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
112
# TODO: _translate_error should be on the client, not the transport because
113
#     error coding is wire protocol specific.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
114
115
# TODO: A plain integer from query_version is too simple; should give some
116
# capabilities too?
117
118
# TODO: Server should probably catch exceptions within itself and send them
119
# back across the network.  (But shouldn't catch KeyboardInterrupt etc)
120
# Also needs to somehow report protocol errors like bad requests.  Need to
121
# consider how we'll handle error reporting, e.g. if we get halfway through a
122
# bulk transfer and then something goes wrong.
123
124
# TODO: Standard marker at start of request/response lines?
125
1910.19.13 by Andrew Bennetts
Address various review comments.
126
# TODO: Make each request and response self-validatable, e.g. with checksums.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
127
#
128
# TODO: get/put objects could be changed to gradually read back the data as it
129
# comes across the network
130
#
131
# TODO: What should the server do if it hits an error and has to terminate?
132
#
133
# TODO: is it useful to allow multiple chunks in the bulk data?
134
#
135
# TODO: If we get an exception during transmission of bulk data we can't just
136
# emit the exception because it won't be seen.
1910.19.13 by Andrew Bennetts
Address various review comments.
137
#   John proposes:  I think it would be worthwhile to have a header on each
138
#   chunk, that indicates it is another chunk. Then you can send an 'error'
139
#   chunk as long as you finish the previous chunk.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
140
#
141
# TODO: Clone method on Transport; should work up towards parent directory;
142
# unclear how this should be stored or communicated to the server... maybe
143
# just pass it on all relevant requests?
144
#
145
# TODO: Better name than clone() for changing between directories.  How about
146
# open_dir or change_dir or chdir?
147
#
148
# TODO: Is it really good to have the notion of current directory within the
149
# connection?  Perhaps all Transports should factor out a common connection
150
# from the thing that has the directory context?
151
#
152
# TODO: Pull more things common to sftp and ssh to a higher level.
153
#
154
# TODO: The server that manages a connection should be quite small and retain
155
# minimum state because each of the requests are supposed to be stateless.
156
# Then we can write another implementation that maps to http.
157
#
158
# TODO: What to do when a client connection is garbage collected?  Maybe just
159
# abruptly drop the connection?
160
#
161
# TODO: Server in some cases will need to restrict access to files outside of
162
# a particular root directory.  LocalTransport doesn't do anything to stop you
163
# ascending above the base directory, so we need to prevent paths
164
# containing '..' in either the server or transport layers.  (Also need to
165
# consider what happens if someone creates a symlink pointing outside the 
166
# directory tree...)
167
#
168
# TODO: Server should rebase absolute paths coming across the network to put
169
# them under the virtual root, if one is in use.  LocalTransport currently
170
# doesn't do that; if you give it an absolute path it just uses it.
171
# 
172
# XXX: Arguments can't contain newlines or ascii; possibly we should e.g.
173
# urlescape them instead.  Indeed possibly this should just literally be
174
# http-over-ssh.
175
#
176
# FIXME: This transport, with several others, has imperfect handling of paths
177
# within urls.  It'd probably be better for ".." from a root to raise an error
178
# rather than return the same directory as we do at present.
179
#
180
# TODO: Rather than working at the Transport layer we want a Branch,
181
# Repository or BzrDir objects that talk to a server.
182
#
183
# TODO: Probably want some way for server commands to gradually produce body
184
# data rather than passing it as a string; they could perhaps pass an
185
# iterator-like callback that will gradually yield data; it probably needs a
186
# close() method that will always be closed to do any necessary cleanup.
187
#
188
# TODO: Split the actual smart server from the ssh encoding of it.
189
#
190
# TODO: Perhaps support file-level readwrite operations over the transport
191
# too.
192
#
193
# TODO: SmartBzrDir class, proxying all Branch etc methods across to another
194
# branch doing file-level operations.
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
195
#
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
196
197
from cStringIO import StringIO
198
import os
199
import socket
2309.2.4 by Alexander Belchenko
SmartServerPipeStreamMedium: binary mode should be forced for media
200
import sys
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
201
import tempfile
202
import threading
203
import urllib
204
import urlparse
205
1910.19.13 by Andrew Bennetts
Address various review comments.
206
from bzrlib import (
207
    bzrdir,
208
    errors,
209
    revision,
210
    transport,
211
    trace,
212
    urlutils,
213
    )
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
214
from bzrlib.bundle.serializer import write_bundle
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
215
try:
216
    from bzrlib.transport import ssh
217
except errors.ParamikoNotPresent:
218
    # no paramiko.  SmartSSHClientMedium will break.
219
    pass
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
220
221
# must do this otherwise urllib can't parse the urls properly :(
2190.1.3 by John Arbash Meinel
Implement bzr+http://
222
for scheme in ['ssh', 'bzr', 'bzr+loopback', 'bzr+ssh', 'bzr+http']:
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
223
    transport.register_urlparse_netloc_protocol(scheme)
224
del scheme
225
226
2298.4.3 by Andrew Bennetts
Use a BZR_DEFAULT_PORT constant instead of bare 4155.
227
# Port 4155 is the default port for bzr://, registered with IANA.
228
BZR_DEFAULT_PORT = 4155
229
230
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
231
def _recv_tuple(from_file):
232
    req_line = from_file.readline()
233
    return _decode_tuple(req_line)
234
235
236
def _decode_tuple(req_line):
237
    if req_line == None or req_line == '':
238
        return None
239
    if req_line[-1] != '\n':
1910.19.13 by Andrew Bennetts
Address various review comments.
240
        raise errors.SmartProtocolError("request %r not terminated" % req_line)
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
241
    return tuple(req_line[:-1].split('\x01'))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
242
243
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
244
def _encode_tuple(args):
245
    """Encode the tuple args to a bytestream."""
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
246
    return '\x01'.join(args) + '\n'
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
247
248
249
class SmartProtocolBase(object):
250
    """Methods common to client and server"""
251
2018.2.24 by Andrew Bennetts
Delete some obsolete code and comments.
252
    # TODO: this only actually accomodates a single block; possibly should
253
    # support multiple chunks?
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
254
    def _encode_bulk_data(self, body):
255
        """Encode body as a bulk data chunk."""
256
        return ''.join(('%d\n' % len(body), body, 'done\n'))
257
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
258
    def _serialise_offsets(self, offsets):
259
        """Serialise a readv offset list."""
260
        txt = []
261
        for start, length in offsets:
262
            txt.append('%d,%d' % (start, length))
263
        return '\n'.join(txt)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
264
        
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
265
266
class SmartServerRequestProtocolOne(SmartProtocolBase):
267
    """Server-side encoding and decoding logic for smart version 1."""
268
    
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
269
    def __init__(self, backing_transport, write_func):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
270
        self._backing_transport = backing_transport
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
271
        self.excess_buffer = ''
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
272
        self._finished = False
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
273
        self.in_buffer = ''
274
        self.has_dispatched = False
275
        self.request = None
276
        self._body_decoder = None
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
277
        self._write_func = write_func
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
278
279
    def accept_bytes(self, bytes):
280
        """Take bytes, and advance the internal state machine appropriately.
281
        
282
        :param bytes: must be a byte string
283
        """
284
        assert isinstance(bytes, str)
285
        self.in_buffer += bytes
286
        if not self.has_dispatched:
287
            if '\n' not in self.in_buffer:
288
                # no command line yet
289
                return
290
            self.has_dispatched = True
291
            try:
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
292
                first_line, self.in_buffer = self.in_buffer.split('\n', 1)
293
                first_line += '\n'
294
                req_args = _decode_tuple(first_line)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
295
                self.request = SmartServerRequestHandler(
296
                    self._backing_transport)
297
                self.request.dispatch_command(req_args[0], req_args[1:])
298
                if self.request.finished_reading:
299
                    # trivial request
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
300
                    self.excess_buffer = self.in_buffer
301
                    self.in_buffer = ''
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
302
                    self._send_response(self.request.response.args,
303
                        self.request.response.body)
304
            except KeyboardInterrupt:
305
                raise
306
            except Exception, exception:
307
                # everything else: pass to client, flush, and quit
308
                self._send_response(('error', str(exception)))
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
309
                return
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
310
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
311
        if self.has_dispatched:
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
312
            if self._finished:
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
313
                # nothing to do.XXX: this routine should be a single state 
314
                # machine too.
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
315
                self.excess_buffer += self.in_buffer
316
                self.in_buffer = ''
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
317
                return
318
            if self._body_decoder is None:
319
                self._body_decoder = LengthPrefixedBodyDecoder()
320
            self._body_decoder.accept_bytes(self.in_buffer)
321
            self.in_buffer = self._body_decoder.unused_data
322
            body_data = self._body_decoder.read_pending_data()
323
            self.request.accept_body(body_data)
324
            if self._body_decoder.finished_reading:
325
                self.request.end_of_body()
326
                assert self.request.finished_reading, \
327
                    "no more body, request not finished"
328
            if self.request.response is not None:
329
                self._send_response(self.request.response.args,
330
                    self.request.response.body)
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
331
                self.excess_buffer = self.in_buffer
332
                self.in_buffer = ''
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
333
            else:
334
                assert not self.request.finished_reading, \
335
                    "no response and we have finished reading."
336
337
    def _send_response(self, args, body=None):
338
        """Send a smart server response down the output stream."""
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
339
        assert not self._finished, 'response already sent'
340
        self._finished = True
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
341
        self._write_func(_encode_tuple(args))
342
        if body is not None:
343
            assert isinstance(body, str), 'body must be a str'
344
            bytes = self._encode_bulk_data(body)
345
            self._write_func(bytes)
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
346
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
347
    def next_read_size(self):
2018.3.2 by Andrew Bennetts
Ensure that a request's next_read_size() is 0 once an error response is sent.
348
        if self._finished:
2018.2.15 by Andrew Bennetts
Remove SmartServerRequestProtocolOne.finished_reading attribute, replace with next_read_size method.
349
            return 0
350
        if self._body_decoder is None:
351
            return 1
352
        else:
353
            return self._body_decoder.next_read_size()
354
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
355
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
356
class LengthPrefixedBodyDecoder(object):
357
    """Decodes the length-prefixed bulk data."""
358
    
359
    def __init__(self):
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
360
        self.bytes_left = None
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
361
        self.finished_reading = False
362
        self.unused_data = ''
363
        self.state_accept = self._state_accept_expecting_length
364
        self.state_read = self._state_read_no_data
365
        self._in_buffer = ''
366
        self._trailer_buffer = ''
367
    
368
    def accept_bytes(self, bytes):
369
        """Decode as much of bytes as possible.
370
371
        If 'bytes' contains too much data it will be appended to
372
        self.unused_data.
373
374
        finished_reading will be set when no more data is required.  Further
375
        data will be appended to self.unused_data.
376
        """
377
        # accept_bytes is allowed to change the state
378
        current_state = self.state_accept
379
        self.state_accept(bytes)
380
        while current_state != self.state_accept:
381
            current_state = self.state_accept
382
            self.state_accept('')
383
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
384
    def next_read_size(self):
385
        if self.bytes_left is not None:
386
            # Ideally we want to read all the remainder of the body and the
387
            # trailer in one go.
388
            return self.bytes_left + 5
389
        elif self.state_accept == self._state_accept_reading_trailer:
390
            # Just the trailer left
391
            return 5 - len(self._trailer_buffer)
392
        elif self.state_accept == self._state_accept_expecting_length:
393
            # There's still at least 6 bytes left ('\n' to end the length, plus
394
            # 'done\n').
395
            return 6
396
        else:
397
            # Reading excess data.  Either way, 1 byte at a time is fine.
398
            return 1
399
        
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
400
    def read_pending_data(self):
401
        """Return any pending data that has been decoded."""
402
        return self.state_read()
403
404
    def _state_accept_expecting_length(self, bytes):
405
        self._in_buffer += bytes
406
        pos = self._in_buffer.find('\n')
407
        if pos == -1:
408
            return
409
        self.bytes_left = int(self._in_buffer[:pos])
410
        self._in_buffer = self._in_buffer[pos+1:]
411
        self.bytes_left -= len(self._in_buffer)
412
        self.state_accept = self._state_accept_reading_body
413
        self.state_read = self._state_read_in_buffer
414
415
    def _state_accept_reading_body(self, bytes):
416
        self._in_buffer += bytes
417
        self.bytes_left -= len(bytes)
418
        if self.bytes_left <= 0:
419
            # Finished with body
420
            if self.bytes_left != 0:
421
                self._trailer_buffer = self._in_buffer[self.bytes_left:]
422
                self._in_buffer = self._in_buffer[:self.bytes_left]
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
423
            self.bytes_left = None
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
424
            self.state_accept = self._state_accept_reading_trailer
425
        
426
    def _state_accept_reading_trailer(self, bytes):
427
        self._trailer_buffer += bytes
2018.2.11 by Andrew Bennetts
Remove commented cruft, add a TODO comment.
428
        # TODO: what if the trailer does not match "done\n"?  Should this raise
429
        # a ProtocolViolation exception?
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
430
        if self._trailer_buffer.startswith('done\n'):
431
            self.unused_data = self._trailer_buffer[len('done\n'):]
432
            self.state_accept = self._state_accept_reading_unused
433
            self.finished_reading = True
434
    
435
    def _state_accept_reading_unused(self, bytes):
436
        self.unused_data += bytes
437
438
    def _state_read_no_data(self):
439
        return ''
440
441
    def _state_read_in_buffer(self):
442
        result = self._in_buffer
443
        self._in_buffer = ''
444
        return result
445
446
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
447
class SmartServerStreamMedium(object):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
448
    """Handles smart commands coming over a stream.
449
450
    The stream may be a pipe connected to sshd, or a tcp socket, or an
451
    in-process fifo for testing.
452
453
    One instance is created for each connected client; it can serve multiple
454
    requests in the lifetime of the connection.
455
456
    The server passes requests through to an underlying backing transport, 
457
    which will typically be a LocalTransport looking at the server's filesystem.
458
    """
459
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
460
    def __init__(self, backing_transport):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
461
        """Construct new server.
462
463
        :param backing_transport: Transport for the directory served.
464
        """
2018.2.18 by Andrew Bennetts
Just close the pipe/socket if the medium catches an error, and add tests for medium exception handling.
465
        # backing_transport could be passed to serve instead of __init__
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
466
        self.backing_transport = backing_transport
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
467
        self.finished = False
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
468
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
469
    def serve(self):
470
        """Serve requests until the client disconnects."""
471
        # Keep a reference to stderr because the sys module's globals get set to
472
        # None during interpreter shutdown.
473
        from sys import stderr
474
        try:
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
475
            while not self.finished:
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
476
                protocol = SmartServerRequestProtocolOne(self.backing_transport,
477
                                                         self._write_out)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
478
                self._serve_one_request(protocol)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
479
        except Exception, e:
480
            stderr.write("%s terminating on exception %s\n" % (self, e))
481
            raise
482
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
483
    def _serve_one_request(self, protocol):
484
        """Read one request from input, process, send back a response.
485
        
486
        :param protocol: a SmartServerRequestProtocol.
487
        """
488
        try:
489
            self._serve_one_request_unguarded(protocol)
490
        except KeyboardInterrupt:
491
            raise
492
        except Exception, e:
493
            self.terminate_due_to_error()
494
495
    def terminate_due_to_error(self):
496
        """Called when an unhandled exception from the protocol occurs."""
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
497
        raise NotImplementedError(self.terminate_due_to_error)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
498
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
499
500
class SmartServerSocketStreamMedium(SmartServerStreamMedium):
501
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
502
    def __init__(self, sock, backing_transport):
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
503
        """Constructor.
504
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
505
        :param sock: the socket the server will read from.  It will be put
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
506
            into blocking mode.
507
        """
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
508
        SmartServerStreamMedium.__init__(self, backing_transport)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
509
        self.push_back = ''
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
510
        sock.setblocking(True)
511
        self.socket = sock
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
512
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
513
    def _serve_one_request_unguarded(self, protocol):
514
        while protocol.next_read_size():
515
            if self.push_back:
516
                protocol.accept_bytes(self.push_back)
517
                self.push_back = ''
518
            else:
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
519
                bytes = self.socket.recv(4096)
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
520
                if bytes == '':
521
                    self.finished = True
522
                    return
523
                protocol.accept_bytes(bytes)
524
        
525
        self.push_back = protocol.excess_buffer
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
526
    
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
527
    def terminate_due_to_error(self):
528
        """Called when an unhandled exception from the protocol occurs."""
529
        # TODO: This should log to a server log file, but no such thing
530
        # exists yet.  Andrew Bennetts 2006-09-29.
531
        self.socket.close()
532
        self.finished = True
533
534
    def _write_out(self, bytes):
535
        self.socket.sendall(bytes)
536
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
537
538
class SmartServerPipeStreamMedium(SmartServerStreamMedium):
539
540
    def __init__(self, in_file, out_file, backing_transport):
541
        """Construct new server.
542
543
        :param in_file: Python file from which requests can be read.
544
        :param out_file: Python file to write responses.
545
        :param backing_transport: Transport for the directory served.
546
        """
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
547
        SmartServerStreamMedium.__init__(self, backing_transport)
2309.2.4 by Alexander Belchenko
SmartServerPipeStreamMedium: binary mode should be forced for media
548
        if sys.platform == 'win32':
549
            # force binary mode for files
550
            import msvcrt
551
            for f in (in_file, out_file):
552
                fileno = getattr(f, 'fileno', None)
553
                if fileno:
554
                    msvcrt.setmode(fileno(), os.O_BINARY)
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
555
        self._in = in_file
556
        self._out = out_file
557
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
558
    def _serve_one_request_unguarded(self, protocol):
559
        while True:
560
            bytes_to_read = protocol.next_read_size()
561
            if bytes_to_read == 0:
562
                # Finished serving this request.
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
563
                self._out.flush()
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
564
                return
565
            bytes = self._in.read(bytes_to_read)
566
            if bytes == '':
567
                # Connection has been closed.
568
                self.finished = True
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
569
                self._out.flush()
2018.2.20 by Andrew Bennetts
Tidy up _serve_one_request.
570
                return
571
            protocol.accept_bytes(bytes)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
572
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
573
    def terminate_due_to_error(self):
574
        # TODO: This should log to a server log file, but no such thing
575
        # exists yet.  Andrew Bennetts 2006-09-29.
576
        self._out.close()
577
        self.finished = True
578
579
    def _write_out(self, bytes):
580
        self._out.write(bytes)
581
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
582
583
class SmartServerResponse(object):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
584
    """Response generated by SmartServerRequestHandler."""
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
585
586
    def __init__(self, args, body=None):
587
        self.args = args
588
        self.body = body
589
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
590
# XXX: TODO: Create a SmartServerRequestHandler which will take the responsibility
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
591
# for delivering the data for a request. This could be done with as the
592
# StreamServer, though that would create conflation between request and response
593
# which may be undesirable.
594
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
595
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
596
class SmartServerRequestHandler(object):
1910.19.13 by Andrew Bennetts
Address various review comments.
597
    """Protocol logic for smart server.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
598
    
599
    This doesn't handle serialization at all, it just processes requests and
600
    creates responses.
601
    """
602
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
603
    # IMPORTANT FOR IMPLEMENTORS: It is important that SmartServerRequestHandler
604
    # not contain encoding or decoding logic to allow the wire protocol to vary
605
    # from the object protocol: we will want to tweak the wire protocol separate
606
    # from the object model, and ideally we will be able to do that without
607
    # having a SmartServerRequestHandler subclass for each wire protocol, rather
608
    # just a Protocol subclass.
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
609
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
610
    # TODO: Better way of representing the body for commands that take it,
611
    # and allow it to be streamed into the server.
612
    
613
    def __init__(self, backing_transport):
614
        self._backing_transport = backing_transport
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
615
        self._converted_command = False
616
        self.finished_reading = False
617
        self._body_bytes = ''
618
        self.response = None
619
620
    def accept_body(self, bytes):
621
        """Accept body data.
622
623
        This should be overriden for each command that desired body data to
624
        handle the right format of that data. I.e. plain bytes, a bundle etc.
625
626
        The deserialisation into that format should be done in the Protocol
627
        object. Set self.desired_body_format to the format your method will
628
        handle.
629
        """
630
        # default fallback is to accumulate bytes.
631
        self._body_bytes += bytes
632
        
633
    def _end_of_body_handler(self):
634
        """An unimplemented end of body handler."""
635
        raise NotImplementedError(self._end_of_body_handler)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
636
        
637
    def do_hello(self):
638
        """Answer a version request with my version."""
639
        return SmartServerResponse(('ok', '1'))
640
641
    def do_has(self, relpath):
642
        r = self._backing_transport.has(relpath) and 'yes' or 'no'
643
        return SmartServerResponse((r,))
644
645
    def do_get(self, relpath):
1910.19.13 by Andrew Bennetts
Address various review comments.
646
        backing_bytes = self._backing_transport.get_bytes(relpath)
647
        return SmartServerResponse(('ok',), backing_bytes)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
648
1910.19.13 by Andrew Bennetts
Address various review comments.
649
    def _deserialise_optional_mode(self, mode):
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
650
        # XXX: FIXME this should be on the protocol object.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
651
        if mode == '':
652
            return None
653
        else:
654
            return int(mode)
655
656
    def do_append(self, relpath, mode):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
657
        self._converted_command = True
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
658
        self._relpath = relpath
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
659
        self._mode = self._deserialise_optional_mode(mode)
660
        self._end_of_body_handler = self._handle_do_append_end
661
    
662
    def _handle_do_append_end(self):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
663
        old_length = self._backing_transport.append_bytes(
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
664
            self._relpath, self._body_bytes, self._mode)
665
        self.response = SmartServerResponse(('appended', '%d' % old_length))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
666
667
    def do_delete(self, relpath):
668
        self._backing_transport.delete(relpath)
669
2018.2.31 by Andrew Bennetts
Fix dispatching of smart server requests involving unicode paths.
670
    def do_iter_files_recursive(self, relpath):
671
        transport = self._backing_transport.clone(relpath)
1910.19.3 by Andrew Bennetts
Add SSH support.
672
        filenames = transport.iter_files_recursive()
673
        return SmartServerResponse(('names',) + tuple(filenames))
674
675
    def do_list_dir(self, relpath):
676
        filenames = self._backing_transport.list_dir(relpath)
677
        return SmartServerResponse(('names',) + tuple(filenames))
678
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
679
    def do_mkdir(self, relpath, mode):
1910.19.13 by Andrew Bennetts
Address various review comments.
680
        self._backing_transport.mkdir(relpath,
681
                                      self._deserialise_optional_mode(mode))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
682
683
    def do_move(self, rel_from, rel_to):
684
        self._backing_transport.move(rel_from, rel_to)
685
686
    def do_put(self, relpath, mode):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
687
        self._converted_command = True
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
688
        self._relpath = relpath
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
689
        self._mode = self._deserialise_optional_mode(mode)
690
        self._end_of_body_handler = self._handle_do_put
691
692
    def _handle_do_put(self):
693
        self._backing_transport.put_bytes(self._relpath,
694
                self._body_bytes, self._mode)
695
        self.response = SmartServerResponse(('ok',))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
696
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
697
    def _deserialise_offsets(self, text):
698
        # XXX: FIXME this should be on the protocol object.
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
699
        offsets = []
700
        for line in text.split('\n'):
701
            if not line:
702
                continue
703
            start, length = line.split(',')
704
            offsets.append((int(start), int(length)))
705
        return offsets
706
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
707
    def do_put_non_atomic(self, relpath, mode, create_parent, dir_mode):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
708
        self._converted_command = True
709
        self._end_of_body_handler = self._handle_put_non_atomic
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
710
        self._relpath = relpath
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
711
        self._dir_mode = self._deserialise_optional_mode(dir_mode)
712
        self._mode = self._deserialise_optional_mode(mode)
713
        # a boolean would be nicer XXX
714
        self._create_parent = (create_parent == 'T')
715
716
    def _handle_put_non_atomic(self):
717
        self._backing_transport.put_bytes_non_atomic(self._relpath,
718
                self._body_bytes,
719
                mode=self._mode,
720
                create_parent_dir=self._create_parent,
721
                dir_mode=self._dir_mode)
722
        self.response = SmartServerResponse(('ok',))
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
723
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
724
    def do_readv(self, relpath):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
725
        self._converted_command = True
726
        self._end_of_body_handler = self._handle_readv_offsets
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
727
        self._relpath = relpath
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
728
729
    def end_of_body(self):
730
        """No more body data will be received."""
731
        self._run_handler_code(self._end_of_body_handler, (), {})
732
        # cannot read after this.
733
        self.finished_reading = True
734
735
    def _handle_readv_offsets(self):
736
        """accept offsets for a readv request."""
737
        offsets = self._deserialise_offsets(self._body_bytes)
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
738
        backing_bytes = ''.join(bytes for offset, bytes in
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
739
            self._backing_transport.readv(self._relpath, offsets))
740
        self.response = SmartServerResponse(('readv',), backing_bytes)
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
741
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
742
    def do_rename(self, rel_from, rel_to):
743
        self._backing_transport.rename(rel_from, rel_to)
744
745
    def do_rmdir(self, relpath):
746
        self._backing_transport.rmdir(relpath)
747
1910.19.3 by Andrew Bennetts
Add SSH support.
748
    def do_stat(self, relpath):
749
        stat = self._backing_transport.stat(relpath)
750
        return SmartServerResponse(('stat', str(stat.st_size), oct(stat.st_mode)))
751
        
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
752
    def do_get_bundle(self, path, revision_id):
753
        # open transport relative to our base
754
        t = self._backing_transport.clone(path)
755
        control, extra_path = bzrdir.BzrDir.open_containing_from_transport(t)
756
        repo = control.open_repository()
757
        tmpf = tempfile.TemporaryFile()
758
        base_revision = revision.NULL_REVISION
759
        write_bundle(repo, revision_id, base_revision, tmpf)
760
        tmpf.seek(0)
761
        return SmartServerResponse((), tmpf.read())
762
763
    def dispatch_command(self, cmd, args):
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
764
        """Deprecated compatibility method.""" # XXX XXX
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
765
        func = getattr(self, 'do_' + cmd, None)
766
        if func is None:
1910.19.13 by Andrew Bennetts
Address various review comments.
767
            raise errors.SmartProtocolError("bad request %r" % (cmd,))
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
768
        self._run_handler_code(func, args, {})
769
770
    def _run_handler_code(self, callable, args, kwargs):
771
        """Run some handler specific code 'callable'.
772
773
        If a result is returned, it is considered to be the commands response,
774
        and finished_reading is set true, and its assigned to self.response.
775
776
        Any exceptions caught are translated and a response object created
777
        from them.
778
        """
779
        result = self._call_converting_errors(callable, args, kwargs)
780
        if result is not None:
781
            self.response = result
782
            self.finished_reading = True
783
        # handle unconverted commands
784
        if not self._converted_command:
785
            self.finished_reading = True
786
            if result is None:
787
                self.response = SmartServerResponse(('ok',))
788
789
    def _call_converting_errors(self, callable, args, kwargs):
790
        """Call callable converting errors to Response objects."""
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
791
        try:
2018.2.2 by Andrew Bennetts
Implement HTTP smart server.
792
            return callable(*args, **kwargs)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
793
        except errors.NoSuchFile, e:
794
            return SmartServerResponse(('NoSuchFile', e.path))
795
        except errors.FileExists, e:
796
            return SmartServerResponse(('FileExists', e.path))
797
        except errors.DirectoryNotEmpty, e:
798
            return SmartServerResponse(('DirectoryNotEmpty', e.path))
1910.19.18 by John Arbash Meinel
[merge] short-read code, implement for Smart readv
799
        except errors.ShortReadvError, e:
800
            return SmartServerResponse(('ShortReadvError',
801
                e.path, str(e.offset), str(e.length), str(e.actual)))
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
802
        except UnicodeError, e:
803
            # If it is a DecodeError, than most likely we are starting
804
            # with a plain string
805
            str_or_unicode = e.object
806
            if isinstance(str_or_unicode, unicode):
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
807
                # XXX: UTF-8 might have \x01 (our seperator byte) in it.  We
808
                # should escape it somehow.
809
                val = 'u:' + str_or_unicode.encode('utf-8')
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
810
            else:
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
811
                val = 's:' + str_or_unicode.encode('base64')
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
812
            # This handles UnicodeEncodeError or UnicodeDecodeError
813
            return SmartServerResponse((e.__class__.__name__,
814
                    e.encoding, val, str(e.start), str(e.end), e.reason))
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
815
        except errors.TransportNotPossible, e:
816
            if e.msg == "readonly transport":
817
                return SmartServerResponse(('ReadOnlyError', ))
818
            else:
819
                raise
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
820
821
822
class SmartTCPServer(object):
823
    """Listens on a TCP socket and accepts connections from smart clients"""
824
2018.2.5 by Andrew Bennetts
Clean up some pyflakes warnings, including making 'backing_transport' a required argument of SmartTCPServer.__init__.
825
    def __init__(self, backing_transport, host='127.0.0.1', port=0):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
826
        """Construct a new server.
827
828
        To actually start it running, call either start_background_thread or
829
        serve.
830
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
831
        :param host: Name of the interface to listen on.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
832
        :param port: TCP port to listen on, or 0 to allocate a transient port.
833
        """
834
        self._server_socket = socket.socket()
1910.19.7 by Andrew Bennetts
Allow specifying the host/interface to bzr serve, and use the new test
835
        self._server_socket.bind((host, port))
836
        self.port = self._server_socket.getsockname()[1]
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
837
        self._server_socket.listen(1)
838
        self._server_socket.settimeout(1)
839
        self.backing_transport = backing_transport
840
841
    def serve(self):
842
        # let connections timeout so that we get a chance to terminate
1910.19.3 by Andrew Bennetts
Add SSH support.
843
        # Keep a reference to the exceptions we want to catch because the socket
844
        # module's globals get set to None during interpreter shutdown.
845
        from socket import timeout as socket_timeout
846
        from socket import error as socket_error
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
847
        self._should_terminate = False
848
        while not self._should_terminate:
849
            try:
850
                self.accept_and_serve()
1910.19.3 by Andrew Bennetts
Add SSH support.
851
            except socket_timeout:
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
852
                # just check if we're asked to stop
853
                pass
1910.19.3 by Andrew Bennetts
Add SSH support.
854
            except socket_error, e:
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
855
                trace.warning("client disconnected: %s", e)
856
                pass
857
858
    def get_url(self):
859
        """Return the url of the server"""
860
        return "bzr://%s:%d/" % self._server_socket.getsockname()
861
862
    def accept_and_serve(self):
863
        conn, client_addr = self._server_socket.accept()
2037.1.3 by Robert Collins
(Andrew Bennetts, Robert Collins) Set smart server test sockets to be blocking
864
        # For WIN32, where the timeout value from the listening socket
865
        # propogates to the newly accepted socket.
866
        conn.setblocking(True)
1910.19.3 by Andrew Bennetts
Add SSH support.
867
        conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
2018.2.23 by Andrew Bennetts
Clean up SmartServerStreamMedium implementations, including removing unnecessary flushes.
868
        handler = SmartServerSocketStreamMedium(conn, self.backing_transport)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
869
        connection_thread = threading.Thread(None, handler.serve, name='smart-server-child')
870
        connection_thread.setDaemon(True)
871
        connection_thread.start()
872
873
    def start_background_thread(self):
874
        self._server_thread = threading.Thread(None,
875
                self.serve,
876
                name='server-' + self.get_url())
877
        self._server_thread.setDaemon(True)
878
        self._server_thread.start()
879
880
    def stop_background_thread(self):
881
        self._should_terminate = True
2288.1.1 by Martin Pool
os._exit rather than sys.exit at top level
882
        # At one point we would wait to join the threads here, but it looks
883
        # like they don't actually exit.  So now we just leave them running
884
        # and expect to terminate the process. -- mbp 20070215
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
885
        # self._server_socket.close()
2288.1.1 by Martin Pool
os._exit rather than sys.exit at top level
886
        ## sys.stderr.write("waiting for server thread to finish...")
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
887
        ## self._server_thread.join()
888
889
890
class SmartTCPServer_for_testing(SmartTCPServer):
891
    """Server suitable for use by transport tests.
892
    
893
    This server is backed by the process's cwd.
894
    """
895
896
    def __init__(self):
2049.1.1 by Lukáš Lalinský
Windows-speficic smart server transport selftest fixes.
897
        self._homedir = urlutils.local_path_to_url(os.getcwd())[7:]
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
898
        # The server is set up by default like for ssh access: the client
899
        # passes filesystem-absolute paths; therefore the server must look
900
        # them up relative to the root directory.  it might be better to act
901
        # a public server and have the server rewrite paths into the test
902
        # directory.
2049.1.1 by Lukáš Lalinský
Windows-speficic smart server transport selftest fixes.
903
        SmartTCPServer.__init__(self,
904
            transport.get_transport(urlutils.local_path_to_url('/')))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
905
        
2381.1.1 by Robert Collins
Split out hpss test fixes which dont depend on new or altered API's.
906
    def get_backing_transport(self, backing_transport_server):
907
        """Get a backing transport from a server we are decorating."""
908
        return transport.get_transport(backing_transport_server.get_url())
909
910
    def setUp(self, backing_transport_server=None):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
911
        """Set up server for testing"""
2381.1.1 by Robert Collins
Split out hpss test fixes which dont depend on new or altered API's.
912
        from bzrlib.transport.chroot import TestingChrootServer
913
        if backing_transport_server is None:
914
            from bzrlib.transport.local import LocalURLServer
915
            backing_transport_server = LocalURLServer()
916
        self.chroot_server = TestingChrootServer()
917
        self.chroot_server.setUp(backing_transport_server)
918
        self.backing_transport = transport.get_transport(
919
            self.chroot_server.get_url())
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
920
        self.start_background_thread()
921
922
    def tearDown(self):
923
        self.stop_background_thread()
924
925
    def get_bogus_url(self):
926
        """Return a URL which will fail to connect"""
927
        return 'bzr://127.0.0.1:1/'
928
929
1910.19.3 by Andrew Bennetts
Add SSH support.
930
class SmartStat(object):
931
932
    def __init__(self, size, mode):
933
        self.st_size = size
934
        self.st_mode = mode
935
936
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
937
class SmartTransport(transport.Transport):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
938
    """Connection to a smart server.
939
940
    The connection holds references to pipes that can be used to send requests
941
    to the server.
942
943
    The connection has a notion of the current directory to which it's
944
    connected; this is incorporated in filenames passed to the server.
945
    
946
    This supports some higher-level RPC operations and can also be treated 
947
    like a Transport to do file-like operations.
948
949
    The connection can be made over a tcp socket, or (in future) an ssh pipe
950
    or a series of http requests.  There are concrete subclasses for each
951
    type: SmartTCPTransport, etc.
952
    """
953
1910.19.22 by Robert Collins
Rearrange the readv patch to put the serialise offsets method into the correct class, and document the structure of the classes somewhat better to hint to people writing patches where code should go. Also alter the test so that the client and server components are tested in one place preventing possible encoding skew from occuring.
954
    # IMPORTANT FOR IMPLEMENTORS: SmartTransport MUST NOT be given encoding
955
    # responsibilities: Put those on SmartClient or similar. This is vital for
956
    # the ability to support multiple versions of the smart protocol over time:
957
    # SmartTransport is an adapter from the Transport object model to the 
958
    # SmartClient model, not an encoder.
959
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
960
    def __init__(self, url, clone_from=None, medium=None):
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
961
        """Constructor.
962
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
963
        :param medium: The medium to use for this RemoteTransport. This must be
964
            supplied if clone_from is None.
1910.19.11 by Andrew Bennetts
General code cleanup based on review comments and other observations.
965
        """
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
966
        ### Technically super() here is faulty because Transport's __init__
967
        ### fails to take 2 parameters, and if super were to choose a silly
968
        ### initialisation order things would blow up. 
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
969
        if not url.endswith('/'):
970
            url += '/'
971
        super(SmartTransport, self).__init__(url)
972
        self._scheme, self._username, self._password, self._host, self._port, self._path = \
973
                transport.split_url(url)
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
974
        if clone_from is None:
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
975
            self._medium = medium
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
976
        else:
977
            # credentials may be stripped from the base in some circumstances
978
            # as yet to be clearly defined or documented, so copy them.
979
            self._username = clone_from._username
980
            # reuse same connection
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
981
            self._medium = clone_from._medium
982
        assert self._medium is not None
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
983
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
984
    def abspath(self, relpath):
985
        """Return the full url to the given relative path.
986
        
987
        @param relpath: the relative path or path components
988
        @type relpath: str or list
989
        """
990
        return self._unparse_url(self._remote_path(relpath))
991
    
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
992
    def clone(self, relative_url):
993
        """Make a new SmartTransport related to me, sharing the same connection.
994
995
        This essentially opens a handle on a different remote directory.
996
        """
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
997
        if relative_url is None:
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
998
            return SmartTransport(self.base, self)
1910.19.6 by Andrew Bennetts
Merge increased transport root tests and fix the smart transport accordingly.
999
        else:
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1000
            return SmartTransport(self.abspath(relative_url), self)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1001
1002
    def is_readonly(self):
1003
        """Smart server transport can do read/write file operations."""
1004
        return False
1005
                                                   
1006
    def get_smart_client(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1007
        return self._medium
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1008
1009
    def get_smart_medium(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1010
        return self._medium
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1011
                                                   
1012
    def _unparse_url(self, path):
1013
        """Return URL for a path.
1014
1015
        :see: SFTPUrlHandling._unparse_url
1016
        """
1017
        # TODO: Eventually it should be possible to unify this with
1018
        # SFTPUrlHandling._unparse_url?
1019
        if path == '':
1020
            path = '/'
1021
        path = urllib.quote(path)
1022
        netloc = urllib.quote(self._host)
1023
        if self._username is not None:
1024
            netloc = '%s@%s' % (urllib.quote(self._username), netloc)
1025
        if self._port is not None:
1026
            netloc = '%s:%d' % (netloc, self._port)
1027
        return urlparse.urlunparse((self._scheme, netloc, path, '', '', ''))
1028
1029
    def _remote_path(self, relpath):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
1030
        """Returns the Unicode version of the absolute path for relpath."""
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1031
        return self._combine_paths(self._path, relpath)
1032
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1033
    def _call(self, method, *args):
1034
        resp = self._call2(method, *args)
1035
        self._translate_error(resp)
1036
1037
    def _call2(self, method, *args):
1038
        """Call a method on the remote server."""
1039
        protocol = SmartClientRequestProtocolOne(self._medium.get_request())
1040
        protocol.call(method, *args)
1041
        return protocol.read_response_tuple()
1042
1043
    def _call_with_body_bytes(self, method, args, body):
1044
        """Call a method on the remote server with body bytes."""
1045
        protocol = SmartClientRequestProtocolOne(self._medium.get_request())
1046
        protocol.call_with_body_bytes((method, ) + args, body)
1047
        return protocol.read_response_tuple()
1048
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1049
    def has(self, relpath):
1050
        """Indicate whether a remote file of the given name exists or not.
1051
1052
        :see: Transport.has()
1053
        """
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1054
        resp = self._call2('has', self._remote_path(relpath))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1055
        if resp == ('yes', ):
1056
            return True
1057
        elif resp == ('no', ):
1058
            return False
1059
        else:
1060
            self._translate_error(resp)
1061
2164.2.15 by Vincent Ladeuil
Http redirections are not followed by default. Do not use hints
1062
    def get(self, relpath):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1063
        """Return file-like object reading the contents of a remote file.
1064
        
1910.19.13 by Andrew Bennetts
Address various review comments.
1065
        :see: Transport.get_bytes()/get_file()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1066
        """
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1067
        return StringIO(self.get_bytes(relpath))
1068
1069
    def get_bytes(self, relpath):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1070
        remote = self._remote_path(relpath)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1071
        protocol = SmartClientRequestProtocolOne(self._medium.get_request())
1072
        protocol.call('get', remote)
1073
        resp = protocol.read_response_tuple(True)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1074
        if resp != ('ok', ):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1075
            protocol.cancel_read_body()
1910.19.3 by Andrew Bennetts
Add SSH support.
1076
            self._translate_error(resp, relpath)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1077
        return protocol.read_body_bytes()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1078
1910.19.13 by Andrew Bennetts
Address various review comments.
1079
    def _serialise_optional_mode(self, mode):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1080
        if mode is None:
1081
            return ''
1082
        else:
1083
            return '%d' % mode
1084
1085
    def mkdir(self, relpath, mode=None):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1086
        resp = self._call2('mkdir', self._remote_path(relpath),
1087
            self._serialise_optional_mode(mode))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1088
        self._translate_error(resp)
1089
1910.19.13 by Andrew Bennetts
Address various review comments.
1090
    def put_bytes(self, relpath, upload_contents, mode=None):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1091
        # FIXME: upload_file is probably not safe for non-ascii characters -
1092
        # should probably just pass all parameters as length-delimited
1093
        # strings?
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1094
        resp = self._call_with_body_bytes('put',
1910.19.13 by Andrew Bennetts
Address various review comments.
1095
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
1096
            upload_contents)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1097
        self._translate_error(resp)
1098
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
1099
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
1100
                             create_parent_dir=False,
1101
                             dir_mode=None):
1910.19.24 by Robert Collins
Touchup method order.
1102
        """See Transport.put_bytes_non_atomic."""
1103
        # FIXME: no encoding in the transport!
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
1104
        create_parent_str = 'F'
1105
        if create_parent_dir:
1106
            create_parent_str = 'T'
1107
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1108
        resp = self._call_with_body_bytes(
2017.2.1 by John Arbash Meinel
Implement put_bytes_non_atomic for the smart transport
1109
            'put_non_atomic',
1110
            (self._remote_path(relpath), self._serialise_optional_mode(mode),
1111
             create_parent_str, self._serialise_optional_mode(dir_mode)),
1112
            bytes)
1113
        self._translate_error(resp)
1114
1910.19.24 by Robert Collins
Touchup method order.
1115
    def put_file(self, relpath, upload_file, mode=None):
1116
        # its not ideal to seek back, but currently put_non_atomic_file depends
1117
        # on transports not reading before failing - which is a faulty
1118
        # assumption I think - RBC 20060915
1119
        pos = upload_file.tell()
1120
        try:
1121
            return self.put_bytes(relpath, upload_file.read(), mode)
1122
        except:
1123
            upload_file.seek(pos)
1124
            raise
1125
1126
    def put_file_non_atomic(self, relpath, f, mode=None,
1127
                            create_parent_dir=False,
1128
                            dir_mode=None):
1129
        return self.put_bytes_non_atomic(relpath, f.read(), mode=mode,
1130
                                         create_parent_dir=create_parent_dir,
1131
                                         dir_mode=dir_mode)
1132
1910.19.13 by Andrew Bennetts
Address various review comments.
1133
    def append_file(self, relpath, from_file, mode=None):
1910.19.14 by Robert Collins
Fix up all tests to pass, remove a couple more deprecated function calls, and break the dependency on sftp for the smart transport.
1134
        return self.append_bytes(relpath, from_file.read(), mode)
1910.19.13 by Andrew Bennetts
Address various review comments.
1135
        
1136
    def append_bytes(self, relpath, bytes, mode=None):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1137
        resp = self._call_with_body_bytes(
1910.19.13 by Andrew Bennetts
Address various review comments.
1138
            'append',
1139
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
1140
            bytes)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1141
        if resp[0] == 'appended':
1142
            return int(resp[1])
1143
        self._translate_error(resp)
1144
1145
    def delete(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1146
        resp = self._call2('delete', self._remote_path(relpath))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1147
        self._translate_error(resp)
1148
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
1149
    def readv(self, relpath, offsets):
1150
        if not offsets:
1151
            return
1152
1153
        offsets = list(offsets)
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
1154
1155
        sorted_offsets = sorted(offsets)
1156
        # turn the list of offsets into a stack
1157
        offset_stack = iter(offsets)
1158
        cur_offset_and_size = offset_stack.next()
1159
        coalesced = list(self._coalesce_offsets(sorted_offsets,
1160
                               limit=self._max_readv_combine,
1161
                               fudge_factor=self._bytes_to_read_before_seek))
1162
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1163
        protocol = SmartClientRequestProtocolOne(self._medium.get_request())
1164
        protocol.call_with_body_readv_array(
1165
            ('readv', self._remote_path(relpath)),
1166
            [(c.start, c.length) for c in coalesced])
1167
        resp = protocol.read_response_tuple(True)
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
1168
1169
        if resp[0] != 'readv':
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
1170
            # This should raise an exception
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1171
            protocol.cancel_read_body()
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
1172
            self._translate_error(resp)
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
1173
            return
1174
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1175
        # FIXME: this should know how many bytes are needed, for clarity.
1176
        data = protocol.read_body_bytes()
1910.19.20 by John Arbash Meinel
Update the client-side requests, so that we collapse nearby ranges, rather than requesting lots of small chunks
1177
        # Cache the results, but only until they have been fulfilled
1178
        data_map = {}
1179
        for c_offset in coalesced:
1180
            if len(data) < c_offset.length:
1181
                raise errors.ShortReadvError(relpath, c_offset.start,
1182
                            c_offset.length, actual=len(data))
1183
            for suboffset, subsize in c_offset.ranges:
1184
                key = (c_offset.start+suboffset, subsize)
1185
                data_map[key] = data[suboffset:suboffset+subsize]
1186
            data = data[c_offset.length:]
1187
1188
            # Now that we've read some data, see if we can yield anything back
1189
            while cur_offset_and_size in data_map:
1190
                this_data = data_map.pop(cur_offset_and_size)
1191
                yield cur_offset_and_size[0], this_data
1192
                cur_offset_and_size = offset_stack.next()
1910.19.17 by John Arbash Meinel
Implement readv() for the SmartServer
1193
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1194
    def rename(self, rel_from, rel_to):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1195
        self._call('rename',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1196
                   self._remote_path(rel_from),
1197
                   self._remote_path(rel_to))
1198
1199
    def move(self, rel_from, rel_to):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1200
        self._call('move',
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1201
                   self._remote_path(rel_from),
1202
                   self._remote_path(rel_to))
1203
1204
    def rmdir(self, relpath):
1205
        resp = self._call('rmdir', self._remote_path(relpath))
1206
1910.19.3 by Andrew Bennetts
Add SSH support.
1207
    def _translate_error(self, resp, orig_path=None):
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1208
        """Raise an exception from a response"""
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
1209
        if resp is None:
1210
            what = None
1211
        else:
1212
            what = resp[0]
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1213
        if what == 'ok':
1214
            return
1215
        elif what == 'NoSuchFile':
1910.19.3 by Andrew Bennetts
Add SSH support.
1216
            if orig_path is not None:
1217
                error_path = orig_path
1218
            else:
1219
                error_path = resp[1]
1220
            raise errors.NoSuchFile(error_path)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1221
        elif what == 'error':
1910.19.13 by Andrew Bennetts
Address various review comments.
1222
            raise errors.SmartProtocolError(unicode(resp[1]))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1223
        elif what == 'FileExists':
1224
            raise errors.FileExists(resp[1])
1225
        elif what == 'DirectoryNotEmpty':
1226
            raise errors.DirectoryNotEmpty(resp[1])
1910.19.18 by John Arbash Meinel
[merge] short-read code, implement for Smart readv
1227
        elif what == 'ShortReadvError':
1228
            raise errors.ShortReadvError(resp[1], int(resp[2]),
1229
                                         int(resp[3]), int(resp[4]))
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
1230
        elif what in ('UnicodeEncodeError', 'UnicodeDecodeError'):
1231
            encoding = str(resp[1]) # encoding must always be a string
1232
            val = resp[2]
1233
            start = int(resp[3])
1234
            end = int(resp[4])
1235
            reason = str(resp[5]) # reason must always be a string
1236
            if val.startswith('u:'):
2018.2.36 by Andrew Bennetts
Don't UTF-8 decode paths in requests. They should be url-quoted (and thus
1237
                val = val[2:].decode('utf-8')
1910.19.16 by John Arbash Meinel
Translate UnicodeErrors across the smart server
1238
            elif val.startswith('s:'):
1239
                val = val[2:].decode('base64')
1240
            if what == 'UnicodeDecodeError':
1241
                raise UnicodeDecodeError(encoding, val, start, end, reason)
1242
            elif what == 'UnicodeEncodeError':
1243
                raise UnicodeEncodeError(encoding, val, start, end, reason)
2020.1.1 by Robert Collins
Add readonly support to the smart server, enabled by default via `bzr server`.
1244
        elif what == "ReadOnlyError":
1245
            raise errors.TransportNotPossible('readonly transport')
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1246
        else:
1910.19.13 by Andrew Bennetts
Address various review comments.
1247
            raise errors.SmartProtocolError('unexpected smart server error: %r' % (resp,))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1248
1249
    def disconnect(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1250
        self._medium.disconnect()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1251
1252
    def delete_tree(self, relpath):
1253
        raise errors.TransportNotPossible('readonly transport')
1254
1255
    def stat(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1256
        resp = self._call2('stat', self._remote_path(relpath))
1910.19.3 by Andrew Bennetts
Add SSH support.
1257
        if resp[0] == 'stat':
1258
            return SmartStat(int(resp[1]), int(resp[2], 8))
1259
        else:
1260
            self._translate_error(resp)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1261
1262
    ## def lock_read(self, relpath):
1263
    ##     """Lock the given file for shared (read) access.
1264
    ##     :return: A lock object, which should be passed to Transport.unlock()
1265
    ##     """
1266
    ##     # The old RemoteBranch ignore lock for reading, so we will
1267
    ##     # continue that tradition and return a bogus lock object.
1268
    ##     class BogusLock(object):
1269
    ##         def __init__(self, path):
1270
    ##             self.path = path
1271
    ##         def unlock(self):
1272
    ##             pass
1273
    ##     return BogusLock(relpath)
1274
1910.19.3 by Andrew Bennetts
Add SSH support.
1275
    def listable(self):
1276
        return True
1277
1278
    def list_dir(self, relpath):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1279
        resp = self._call2('list_dir', self._remote_path(relpath))
1910.19.3 by Andrew Bennetts
Add SSH support.
1280
        if resp[0] == 'names':
1281
            return [name.encode('ascii') for name in resp[1:]]
1282
        else:
1283
            self._translate_error(resp)
1284
1285
    def iter_files_recursive(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1286
        resp = self._call2('iter_files_recursive', self._remote_path(''))
1910.19.3 by Andrew Bennetts
Add SSH support.
1287
        if resp[0] == 'names':
1288
            return resp[1:]
1289
        else:
1290
            self._translate_error(resp)
1291
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1292
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1293
class SmartClientMediumRequest(object):
1294
    """A request on a SmartClientMedium.
1295
1296
    Each request allows bytes to be provided to it via accept_bytes, and then
1297
    the response bytes to be read via read_bytes.
1298
1299
    For instance:
1300
    request.accept_bytes('123')
1301
    request.finished_writing()
1302
    result = request.read_bytes(3)
1303
    request.finished_reading()
1304
1305
    It is up to the individual SmartClientMedium whether multiple concurrent
1306
    requests can exist. See SmartClientMedium.get_request to obtain instances 
1307
    of SmartClientMediumRequest, and the concrete Medium you are using for 
1308
    details on concurrency and pipelining.
1309
    """
1310
1311
    def __init__(self, medium):
1312
        """Construct a SmartClientMediumRequest for the medium medium."""
1313
        self._medium = medium
1314
        # we track state by constants - we may want to use the same
1315
        # pattern as BodyReader if it gets more complex.
1316
        # valid states are: "writing", "reading", "done"
1317
        self._state = "writing"
1318
1319
    def accept_bytes(self, bytes):
1320
        """Accept bytes for inclusion in this request.
1321
1322
        This method may not be be called after finished_writing() has been
1323
        called.  It depends upon the Medium whether or not the bytes will be
1324
        immediately transmitted. Message based Mediums will tend to buffer the
1325
        bytes until finished_writing() is called.
1326
1327
        :param bytes: A bytestring.
1328
        """
1329
        if self._state != "writing":
1330
            raise errors.WritingCompleted(self)
1331
        self._accept_bytes(bytes)
1332
1333
    def _accept_bytes(self, bytes):
1334
        """Helper for accept_bytes.
1335
1336
        Accept_bytes checks the state of the request to determing if bytes
1337
        should be accepted. After that it hands off to _accept_bytes to do the
1338
        actual acceptance.
1339
        """
1340
        raise NotImplementedError(self._accept_bytes)
1341
1342
    def finished_reading(self):
1343
        """Inform the request that all desired data has been read.
1344
1345
        This will remove the request from the pipeline for its medium (if the
1346
        medium supports pipelining) and any further calls to methods on the
1347
        request will raise ReadingCompleted.
1348
        """
1349
        if self._state == "writing":
1350
            raise errors.WritingNotComplete(self)
1351
        if self._state != "reading":
1352
            raise errors.ReadingCompleted(self)
1353
        self._state = "done"
1354
        self._finished_reading()
1355
1356
    def _finished_reading(self):
1357
        """Helper for finished_reading.
1358
1359
        finished_reading checks the state of the request to determine if 
1360
        finished_reading is allowed, and if it is hands off to _finished_reading
1361
        to perform the action.
1362
        """
1363
        raise NotImplementedError(self._finished_reading)
1364
1365
    def finished_writing(self):
1366
        """Finish the writing phase of this request.
1367
1368
        This will flush all pending data for this request along the medium.
1369
        After calling finished_writing, you may not call accept_bytes anymore.
1370
        """
1371
        if self._state != "writing":
1372
            raise errors.WritingCompleted(self)
1373
        self._state = "reading"
1374
        self._finished_writing()
1375
1376
    def _finished_writing(self):
1377
        """Helper for finished_writing.
1378
1379
        finished_writing checks the state of the request to determine if 
1380
        finished_writing is allowed, and if it is hands off to _finished_writing
1381
        to perform the action.
1382
        """
1383
        raise NotImplementedError(self._finished_writing)
1384
1385
    def read_bytes(self, count):
1386
        """Read bytes from this requests response.
1387
1388
        This method will block and wait for count bytes to be read. It may not
1389
        be invoked until finished_writing() has been called - this is to ensure
1390
        a message-based approach to requests, for compatability with message
1391
        based mediums like HTTP.
1392
        """
1393
        if self._state == "writing":
1394
            raise errors.WritingNotComplete(self)
1395
        if self._state != "reading":
1396
            raise errors.ReadingCompleted(self)
1397
        return self._read_bytes(count)
1398
1399
    def _read_bytes(self, count):
1400
        """Helper for read_bytes.
1401
1402
        read_bytes checks the state of the request to determing if bytes
1403
        should be read. After that it hands off to _read_bytes to do the
1404
        actual read.
1405
        """
1406
        raise NotImplementedError(self._read_bytes)
1407
1408
1409
class SmartClientStreamMediumRequest(SmartClientMediumRequest):
1410
    """A SmartClientMediumRequest that works with an SmartClientStreamMedium."""
1411
1412
    def __init__(self, medium):
1413
        SmartClientMediumRequest.__init__(self, medium)
1414
        # check that we are safe concurrency wise. If some streams start
1415
        # allowing concurrent requests - i.e. via multiplexing - then this
1416
        # assert should be moved to SmartClientStreamMedium.get_request,
1417
        # and the setting/unsetting of _current_request likewise moved into
1418
        # that class : but its unneeded overhead for now. RBC 20060922
1419
        if self._medium._current_request is not None:
1420
            raise errors.TooManyConcurrentRequests(self._medium)
1421
        self._medium._current_request = self
1422
1423
    def _accept_bytes(self, bytes):
1424
        """See SmartClientMediumRequest._accept_bytes.
1425
        
1426
        This forwards to self._medium._accept_bytes because we are operating
1427
        on the mediums stream.
1428
        """
1429
        self._medium._accept_bytes(bytes)
1430
1431
    def _finished_reading(self):
1432
        """See SmartClientMediumRequest._finished_reading.
1433
1434
        This clears the _current_request on self._medium to allow a new 
1435
        request to be created.
1436
        """
1437
        assert self._medium._current_request is self
1438
        self._medium._current_request = None
1439
        
1440
    def _finished_writing(self):
1441
        """See SmartClientMediumRequest._finished_writing.
1442
1443
        This invokes self._medium._flush to ensure all bytes are transmitted.
1444
        """
1445
        self._medium._flush()
1446
1447
    def _read_bytes(self, count):
1448
        """See SmartClientMediumRequest._read_bytes.
1449
        
1450
        This forwards to self._medium._read_bytes because we are operating
1451
        on the mediums stream.
1452
        """
1453
        return self._medium._read_bytes(count)
1454
1455
1456
class SmartClientRequestProtocolOne(SmartProtocolBase):
1457
    """The client-side protocol for smart version 1."""
1458
1459
    def __init__(self, request):
1460
        """Construct a SmartClientRequestProtocolOne.
1461
1462
        :param request: A SmartClientMediumRequest to serialise onto and
1463
            deserialise from.
1464
        """
1465
        self._request = request
1466
        self._body_buffer = None
1467
1468
    def call(self, *args):
1469
        bytes = _encode_tuple(args)
1470
        self._request.accept_bytes(bytes)
1471
        self._request.finished_writing()
1472
1473
    def call_with_body_bytes(self, args, body):
1474
        """Make a remote call of args with body bytes 'body'.
1475
1476
        After calling this, call read_response_tuple to find the result out.
1477
        """
1478
        bytes = _encode_tuple(args)
1479
        self._request.accept_bytes(bytes)
1480
        bytes = self._encode_bulk_data(body)
1481
        self._request.accept_bytes(bytes)
1482
        self._request.finished_writing()
1483
1484
    def call_with_body_readv_array(self, args, body):
1485
        """Make a remote call with a readv array.
1486
1487
        The body is encoded with one line per readv offset pair. The numbers in
1488
        each pair are separated by a comma, and no trailing \n is emitted.
1489
        """
1490
        bytes = _encode_tuple(args)
1491
        self._request.accept_bytes(bytes)
1492
        readv_bytes = self._serialise_offsets(body)
1493
        bytes = self._encode_bulk_data(readv_bytes)
1494
        self._request.accept_bytes(bytes)
1495
        self._request.finished_writing()
1496
1497
    def cancel_read_body(self):
1498
        """After expecting a body, a response code may indicate one otherwise.
1499
1500
        This method lets the domain client inform the protocol that no body
1501
        will be transmitted. This is a terminal method: after calling it the
1502
        protocol is not able to be used further.
1503
        """
1504
        self._request.finished_reading()
1505
1506
    def read_response_tuple(self, expect_body=False):
1507
        """Read a response tuple from the wire.
1508
1509
        This should only be called once.
1510
        """
1511
        result = self._recv_tuple()
1512
        if not expect_body:
1513
            self._request.finished_reading()
1514
        return result
1515
1516
    def read_body_bytes(self, count=-1):
1517
        """Read bytes from the body, decoding into a byte stream.
1518
        
1519
        We read all bytes at once to ensure we've checked the trailer for 
1520
        errors, and then feed the buffer back as read_body_bytes is called.
1521
        """
1522
        if self._body_buffer is not None:
1523
            return self._body_buffer.read(count)
1524
        _body_decoder = LengthPrefixedBodyDecoder()
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1525
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1526
        while not _body_decoder.finished_reading:
2018.2.13 by Andrew Bennetts
Update SmartClientRequestProtocolOne.read_body_bytes to use next_read_size.
1527
            bytes_wanted = _body_decoder.next_read_size()
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1528
            bytes = self._request.read_bytes(bytes_wanted)
1529
            _body_decoder.accept_bytes(bytes)
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1530
        self._request.finished_reading()
1531
        self._body_buffer = StringIO(_body_decoder.read_pending_data())
1532
        # XXX: TODO check the trailer result.
1533
        return self._body_buffer.read(count)
1534
1535
    def _recv_tuple(self):
2018.2.10 by Andrew Bennetts
Tidy up TODOs, further testing and fixes for SmartServerRequestProtocolOne, and remove a read_bytes(1) call.
1536
        """Receive a tuple from the medium request."""
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1537
        line = ''
1538
        while not line or line[-1] != '\n':
2018.2.14 by Andrew Bennetts
Seperate SmartServer{Pipe,Socket}StreamMedium out of SmartServerStreamMedium. Use recv to make the socket server medium better.
1539
            # TODO: this is inefficient - but tuples are short.
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1540
            new_char = self._request.read_bytes(1)
1541
            line += new_char
1542
            assert new_char != '', "end of file reading from server."
1543
        return _decode_tuple(line)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1544
1545
    def query_version(self):
1546
        """Return protocol version number of the server."""
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1547
        self.call('hello')
1548
        resp = self.read_response_tuple()
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1549
        if resp == ('ok', '1'):
1550
            return 1
1551
        else:
1910.19.13 by Andrew Bennetts
Address various review comments.
1552
            raise errors.SmartProtocolError("bad response %r" % (resp,))
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1553
1554
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1555
class SmartClientMedium(object):
1556
    """Smart client is a medium for sending smart protocol requests over."""
1557
1558
    def disconnect(self):
1559
        """If this medium maintains a persistent connection, close it.
1560
        
1561
        The default implementation does nothing.
1562
        """
1563
        
1564
1565
class SmartClientStreamMedium(SmartClientMedium):
1566
    """Stream based medium common class.
1567
1568
    SmartClientStreamMediums operate on a stream. All subclasses use a common
1569
    SmartClientStreamMediumRequest for their requests, and should implement
1570
    _accept_bytes and _read_bytes to allow the request objects to send and
1571
    receive bytes.
1572
    """
1573
1574
    def __init__(self):
1575
        self._current_request = None
1576
1577
    def accept_bytes(self, bytes):
1578
        self._accept_bytes(bytes)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1579
1580
    def __del__(self):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1581
        """The SmartClientStreamMedium knows how to close the stream when it is
1582
        finished with it.
1583
        """
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1584
        self.disconnect()
1585
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1586
    def _flush(self):
1587
        """Flush the output stream.
1588
        
1589
        This method is used by the SmartClientStreamMediumRequest to ensure that
1590
        all data for a request is sent, to avoid long timeouts or deadlocks.
1591
        """
1592
        raise NotImplementedError(self._flush)
1593
1594
    def get_request(self):
1595
        """See SmartClientMedium.get_request().
1596
1597
        SmartClientStreamMedium always returns a SmartClientStreamMediumRequest
1598
        for get_request.
1599
        """
1600
        return SmartClientStreamMediumRequest(self)
1601
1602
    def read_bytes(self, count):
1603
        return self._read_bytes(count)
1604
1605
1606
class SmartSimplePipesClientMedium(SmartClientStreamMedium):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1607
    """A client medium using simple pipes.
1608
    
1609
    This client does not manage the pipes: it assumes they will always be open.
1610
    """
1611
1612
    def __init__(self, readable_pipe, writeable_pipe):
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1613
        SmartClientStreamMedium.__init__(self)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1614
        self._readable_pipe = readable_pipe
1615
        self._writeable_pipe = writeable_pipe
1616
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1617
    def _accept_bytes(self, bytes):
1618
        """See SmartClientStreamMedium.accept_bytes."""
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1619
        self._writeable_pipe.write(bytes)
1620
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1621
    def _flush(self):
1622
        """See SmartClientStreamMedium._flush()."""
1623
        self._writeable_pipe.flush()
1624
1625
    def _read_bytes(self, count):
1626
        """See SmartClientStreamMedium._read_bytes."""
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1627
        return self._readable_pipe.read(count)
1628
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1629
1630
class SmartSSHClientMedium(SmartClientStreamMedium):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1631
    """A client medium using SSH."""
1632
    
1633
    def __init__(self, host, port=None, username=None, password=None,
1634
            vendor=None):
1635
        """Creates a client that will connect on the first use.
1636
        
1637
        :param vendor: An optional override for the ssh vendor to use. See
1638
            bzrlib.transport.ssh for details on ssh vendors.
1639
        """
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1640
        SmartClientStreamMedium.__init__(self)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1641
        self._connected = False
1642
        self._host = host
1643
        self._password = password
1644
        self._port = port
1645
        self._username = username
1646
        self._read_from = None
1647
        self._ssh_connection = None
1648
        self._vendor = vendor
1649
        self._write_to = None
1650
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1651
    def _accept_bytes(self, bytes):
1652
        """See SmartClientStreamMedium.accept_bytes."""
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1653
        self._ensure_connection()
1654
        self._write_to.write(bytes)
1655
1656
    def disconnect(self):
1657
        """See SmartClientMedium.disconnect()."""
1658
        if not self._connected:
1659
            return
1660
        self._read_from.close()
1661
        self._write_to.close()
1662
        self._ssh_connection.close()
1663
        self._connected = False
1664
1665
    def _ensure_connection(self):
1666
        """Connect this medium if not already connected."""
1667
        if self._connected:
1668
            return
1669
        executable = os.environ.get('BZR_REMOTE_PATH', 'bzr')
1670
        if self._vendor is None:
1671
            vendor = ssh._get_ssh_vendor()
1672
        else:
1673
            vendor = self._vendor
1674
        self._ssh_connection = vendor.connect_ssh(self._username,
1675
                self._password, self._host, self._port,
1676
                command=[executable, 'serve', '--inet', '--directory=/',
1677
                         '--allow-writes'])
1678
        self._read_from, self._write_to = \
1679
            self._ssh_connection.get_filelike_channels()
1680
        self._connected = True
1681
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1682
    def _flush(self):
1683
        """See SmartClientStreamMedium._flush()."""
1684
        self._write_to.flush()
1685
1686
    def _read_bytes(self, count):
1687
        """See SmartClientStreamMedium.read_bytes."""
1688
        if not self._connected:
1689
            raise errors.MediumNotConnected(self)
1690
        return self._read_from.read(count)
1691
1692
1693
class SmartTCPClientMedium(SmartClientStreamMedium):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1694
    """A client medium using TCP."""
1695
    
1696
    def __init__(self, host, port):
1697
        """Creates a client that will connect on the first use."""
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1698
        SmartClientStreamMedium.__init__(self)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1699
        self._connected = False
1700
        self._host = host
1701
        self._port = port
1702
        self._socket = None
1703
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1704
    def _accept_bytes(self, bytes):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1705
        """See SmartClientMedium.accept_bytes."""
1706
        self._ensure_connection()
1707
        self._socket.sendall(bytes)
1708
1709
    def disconnect(self):
1710
        """See SmartClientMedium.disconnect()."""
1711
        if not self._connected:
1712
            return
1713
        self._socket.close()
1714
        self._socket = None
1715
        self._connected = False
1716
1717
    def _ensure_connection(self):
1718
        """Connect this medium if not already connected."""
1719
        if self._connected:
1720
            return
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1721
        self._socket = socket.socket()
1910.19.3 by Andrew Bennetts
Add SSH support.
1722
        self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1723
        result = self._socket.connect_ex((self._host, int(self._port)))
1724
        if result:
1725
            raise errors.ConnectionError("failed to connect to %s:%d: %s" %
1726
                    (self._host, self._port, os.strerror(result)))
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1727
        self._connected = True
1728
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1729
    def _flush(self):
1730
        """See SmartClientStreamMedium._flush().
1731
        
1732
        For TCP we do no flushing. We may want to turn off TCP_NODELAY and 
1733
        add a means to do a flush, but that can be done in the future.
1734
        """
1735
1736
    def _read_bytes(self, count):
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1737
        """See SmartClientMedium.read_bytes."""
2018.2.4 by Robert Collins
separate out the client medium from the client encoding protocol for the smart server.
1738
        if not self._connected:
1739
            raise errors.MediumNotConnected(self)
1740
        return self._socket.recv(count)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1741
1742
1743
class SmartTCPTransport(SmartTransport):
1744
    """Connection to smart server over plain tcp.
1745
    
1746
    This is essentially just a factory to get 'RemoteTransport(url,
1747
        SmartTCPClientMedium).
1748
    """
1749
1750
    def __init__(self, url):
1751
        _scheme, _username, _password, _host, _port, _path = \
1752
            transport.split_url(url)
2298.4.1 by Andrew Bennetts
Give bzr:// a default port of 4155.
1753
        if _port is None:
2298.4.3 by Andrew Bennetts
Use a BZR_DEFAULT_PORT constant instead of bare 4155.
1754
            _port = BZR_DEFAULT_PORT
2298.4.1 by Andrew Bennetts
Give bzr:// a default port of 4155.
1755
        else:
1756
            try:
1757
                _port = int(_port)
1758
            except (ValueError, TypeError), e:
1759
                raise errors.InvalidURL(
1760
                    path=url, extra="invalid port %s" % _port)
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1761
        medium = SmartTCPClientMedium(_host, _port)
1762
        super(SmartTCPTransport, self).__init__(url, medium=medium)
1763
2039.2.2 by Martin Pool
Define SmartSSHTransport class even without paramiko
1764
1765
class SmartSSHTransport(SmartTransport):
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
1766
    """Connection to smart server over SSH.
1767
1768
    This is essentially just a factory to get 'RemoteTransport(url,
1769
        SmartSSHClientMedium).
1770
    """
1771
1772
    def __init__(self, url):
1773
        _scheme, _username, _password, _host, _port, _path = \
1774
            transport.split_url(url)
2039.2.2 by Martin Pool
Define SmartSSHTransport class even without paramiko
1775
        try:
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
1776
            if _port is not None:
1777
                _port = int(_port)
2039.2.2 by Martin Pool
Define SmartSSHTransport class even without paramiko
1778
        except (ValueError, TypeError), e:
2018.2.27 by Andrew Bennetts
Merge from bzr.dev
1779
            raise errors.InvalidURL(path=url, extra="invalid port %s" % 
1780
                _port)
1781
        medium = SmartSSHClientMedium(_host, _port, _username, _password)
1782
        super(SmartSSHTransport, self).__init__(url, medium=medium)
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1783
1784
2190.1.3 by John Arbash Meinel
Implement bzr+http://
1785
class SmartHTTPTransport(SmartTransport):
1786
    """Just a way to connect between a bzr+http:// url and http://.
1787
    
1788
    This connection operates slightly differently than the SmartSSHTransport.
1789
    It uses a plain http:// transport underneath, which defines what remote
1790
    .bzr/smart URL we are connected to. From there, all paths that are sent are
1791
    sent as relative paths, this way, the remote side can properly
2190.1.5 by John Arbash Meinel
Typo fix from Martin
1792
    de-reference them, since it is likely doing rewrite rules to translate an
2190.1.3 by John Arbash Meinel
Implement bzr+http://
1793
    HTTP path into a local path.
1794
    """
1795
1796
    def __init__(self, url, http_transport=None):
1797
        assert url.startswith('bzr+http://')
1798
1799
        if http_transport is None:
1800
            http_url = url[len('bzr+'):]
1801
            self._http_transport = transport.get_transport(http_url)
1802
        else:
1803
            self._http_transport = http_transport
1804
        http_medium = self._http_transport.get_smart_medium()
1805
        super(SmartHTTPTransport, self).__init__(url, medium=http_medium)
1806
1807
    def _remote_path(self, relpath):
1808
        """After connecting HTTP Transport only deals in relative URLs."""
1809
        if relpath == '.':
1810
            return ''
1811
        else:
1812
            return relpath
1813
1814
    def abspath(self, relpath):
1815
        """Return the full url to the given relative path.
1816
        
1817
        :param relpath: the relative path or path components
1818
        :type relpath: str or list
1819
        """
1820
        return self._unparse_url(self._combine_paths(self._path, relpath))
1821
1822
    def clone(self, relative_url):
1823
        """Make a new SmartHTTPTransport related to me.
1824
1825
        This is re-implemented rather than using the default
1826
        SmartTransport.clone() because we must be careful about the underlying
1827
        http transport.
1828
        """
1829
        if relative_url:
1830
            abs_url = self.abspath(relative_url)
1831
        else:
1832
            abs_url = self.base
1833
        # By cloning the underlying http_transport, we are able to share the
1834
        # connection.
1835
        new_transport = self._http_transport.clone(relative_url)
1836
        return SmartHTTPTransport(abs_url, http_transport=new_transport)
1837
1838
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1839
def get_test_permutations():
2018.2.3 by Andrew Bennetts
Starting factoring out the smart server client "medium" from the protocol.
1840
    """Return (transport, server) permutations for testing."""
1841
    ### We may need a little more test framework support to construct an
1842
    ### appropriate RemoteTransport in the future.
1910.19.1 by Andrew Bennetts
Support bzr:// urls to work with the new RPC-based transport which will be used
1843
    return [(SmartTCPTransport, SmartTCPServer_for_testing)]