bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 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 | ||
| 2018.5.19
by Andrew Bennetts Add docstrings to all the new modules, and a few other places. | 17 | """The 'medium' layer for the smart servers and clients.
 | 
| 18 | ||
| 19 | "Medium" here is the noun meaning "a means of transmission", not the adjective
 | |
| 20 | for "the quality between big and small."
 | |
| 21 | ||
| 22 | Media carry the bytes of the requests somehow (e.g. via TCP, wrapped in HTTP, or
 | |
| 23 | over SSH), and pass them to and from the protocol logic.  See the overview in
 | |
| 24 | bzrlib/transport/smart/__init__.py.
 | |
| 25 | """
 | |
| 26 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 27 | import os | 
| 28 | import socket | |
| 2018.5.162
by Andrew Bennetts Add some missing _ensure_real calls, and a missing import. | 29 | import sys | 
| 3431.3.11
by Andrew Bennetts Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient. | 30 | import urllib | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 31 | |
| 1551.18.17
by Aaron Bentley Introduce bzr_remote_path configuration variable | 32 | from bzrlib import ( | 
| 33 | errors, | |
| 3118.2.1
by Andrew Bennetts (andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall. | 34 | osutils, | 
| 1551.18.17
by Aaron Bentley Introduce bzr_remote_path configuration variable | 35 | symbol_versioning, | 
| 3431.3.11
by Andrew Bennetts Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient. | 36 | urlutils, | 
| 1551.18.17
by Aaron Bentley Introduce bzr_remote_path configuration variable | 37 |     )
 | 
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 38 | from bzrlib.smart.protocol import ( | 
| 3195.3.17
by Andrew Bennetts Some tests now passing using protocol 3. | 39 | MESSAGE_VERSION_THREE, | 
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 40 | REQUEST_VERSION_TWO, | 
| 3241.1.1
by Andrew Bennetts Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium. | 41 | SmartClientRequestProtocolOne, | 
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 42 | SmartServerRequestProtocolOne, | 
| 43 | SmartServerRequestProtocolTwo, | |
| 3195.3.17
by Andrew Bennetts Some tests now passing using protocol 3. | 44 |     build_server_protocol_three
 | 
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 45 |     )
 | 
| 3066.2.1
by John Arbash Meinel We don't require paramiko for bzr+ssh. | 46 | from bzrlib.transport import ssh | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 47 | |
| 2018.5.17
by Andrew Bennetts Paramaterise the commands handled by SmartServerRequestHandler. | 48 | |
| 3245.4.16
by Andrew Bennetts Remove duplication of request version identification logic in wsgi.py | 49 | def _get_protocol_factory_for_bytes(bytes): | 
| 50 | """Determine the right protocol factory for 'bytes'. | |
| 51 | ||
| 52 |     This will return an appropriate protocol factory depending on the version
 | |
| 53 |     of the protocol being used, as determined by inspecting the given bytes.
 | |
| 54 |     The bytes should have at least one newline byte (i.e. be a whole line),
 | |
| 55 |     otherwise it's possible that a request will be incorrectly identified as
 | |
| 56 |     version 1.
 | |
| 57 | ||
| 58 |     Typical use would be::
 | |
| 59 | ||
| 60 |          factory, unused_bytes = _get_protocol_factory_for_bytes(bytes)
 | |
| 61 |          server_protocol = factory(transport, write_func, root_client_path)
 | |
| 62 |          server_protocol.accept_bytes(unused_bytes)
 | |
| 63 | ||
| 64 |     :param bytes: a str of bytes of the start of the request.
 | |
| 65 |     :returns: 2-tuple of (protocol_factory, unused_bytes).  protocol_factory is
 | |
| 66 |         a callable that takes three args: transport, write_func,
 | |
| 67 |         root_client_path.  unused_bytes are any bytes that were not part of a
 | |
| 68 |         protocol version marker.
 | |
| 69 |     """
 | |
| 70 | if bytes.startswith(MESSAGE_VERSION_THREE): | |
| 71 | protocol_factory = build_server_protocol_three | |
| 72 | bytes = bytes[len(MESSAGE_VERSION_THREE):] | |
| 73 | elif bytes.startswith(REQUEST_VERSION_TWO): | |
| 74 | protocol_factory = SmartServerRequestProtocolTwo | |
| 75 | bytes = bytes[len(REQUEST_VERSION_TWO):] | |
| 76 | else: | |
| 77 | protocol_factory = SmartServerRequestProtocolOne | |
| 78 | return protocol_factory, bytes | |
| 79 | ||
| 80 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 81 | class SmartServerStreamMedium(object): | 
| 82 | """Handles smart commands coming over a stream. | |
| 83 | ||
| 84 |     The stream may be a pipe connected to sshd, or a tcp socket, or an
 | |
| 85 |     in-process fifo for testing.
 | |
| 86 | ||
| 87 |     One instance is created for each connected client; it can serve multiple
 | |
| 88 |     requests in the lifetime of the connection.
 | |
| 89 | ||
| 90 |     The server passes requests through to an underlying backing transport, 
 | |
| 91 |     which will typically be a LocalTransport looking at the server's filesystem.
 | |
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 92 | |
| 93 |     :ivar _push_back_buffer: a str of bytes that have been read from the stream
 | |
| 94 |         but not used yet, or None if there are no buffered bytes.  Subclasses
 | |
| 95 |         should make sure to exhaust this buffer before reading more bytes from
 | |
| 96 |         the stream.  See also the _push_back method.
 | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 97 |     """
 | 
| 98 | ||
| 2692.1.11
by Andrew Bennetts Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured. | 99 | def __init__(self, backing_transport, root_client_path='/'): | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 100 | """Construct new server. | 
| 101 | ||
| 102 |         :param backing_transport: Transport for the directory served.
 | |
| 103 |         """
 | |
| 104 |         # backing_transport could be passed to serve instead of __init__
 | |
| 105 | self.backing_transport = backing_transport | |
| 2692.1.11
by Andrew Bennetts Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured. | 106 | self.root_client_path = root_client_path | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 107 | self.finished = False | 
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 108 | self._push_back_buffer = None | 
| 3236.3.2
by Andrew Bennetts Fix SmartServerPipeStreamMedium._get_line too. | 109 | |
| 110 | def _push_back(self, bytes): | |
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 111 | """Return unused bytes to the medium, because they belong to the next | 
| 112 |         request(s).
 | |
| 113 | ||
| 114 |         This sets the _push_back_buffer to the given bytes.
 | |
| 115 |         """
 | |
| 3376.2.4
by Martin Pool Remove every assert statement from bzrlib! | 116 | if self._push_back_buffer is not None: | 
| 117 | raise AssertionError( | |
| 118 | "_push_back called when self._push_back_buffer is %r" | |
| 119 | % (self._push_back_buffer,)) | |
| 3236.3.2
by Andrew Bennetts Fix SmartServerPipeStreamMedium._get_line too. | 120 | if bytes == '': | 
| 121 |             return
 | |
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 122 | self._push_back_buffer = bytes | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 123 | |
| 3236.3.5
by Andrew Bennetts Add _get_push_back_buffer helper. | 124 | def _get_push_back_buffer(self): | 
| 3376.2.4
by Martin Pool Remove every assert statement from bzrlib! | 125 | if self._push_back_buffer == '': | 
| 126 | raise AssertionError( | |
| 127 | '%s._push_back_buffer should never be the empty string, ' | |
| 128 | 'which can be confused with EOF' % (self,)) | |
| 3236.3.5
by Andrew Bennetts Add _get_push_back_buffer helper. | 129 | bytes = self._push_back_buffer | 
| 130 | self._push_back_buffer = None | |
| 131 | return bytes | |
| 132 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 133 | def serve(self): | 
| 134 | """Serve requests until the client disconnects.""" | |
| 135 |         # Keep a reference to stderr because the sys module's globals get set to
 | |
| 136 |         # None during interpreter shutdown.
 | |
| 137 | from sys import stderr | |
| 138 | try: | |
| 139 | while not self.finished: | |
| 2432.2.3
by Andrew Bennetts Merge from bzr.dev. | 140 | server_protocol = self._build_protocol() | 
| 2018.5.14
by Andrew Bennetts Move SmartTCPServer to smart/server.py, and SmartServerRequestHandler to smart/request.py. | 141 | self._serve_one_request(server_protocol) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 142 | except Exception, e: | 
| 143 | stderr.write("%s terminating on exception %s\n" % (self, e)) | |
| 144 |             raise
 | |
| 145 | ||
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 146 | def _build_protocol(self): | 
| 2432.2.8
by Andrew Bennetts NEWS entry, greatly improved docstring in bzrlib.smart. | 147 | """Identifies the version of the incoming request, and returns an | 
| 148 |         a protocol object that can interpret it.
 | |
| 149 | ||
| 150 |         If more bytes than the version prefix of the request are read, they will
 | |
| 151 |         be fed into the protocol before it is returned.
 | |
| 152 | ||
| 153 |         :returns: a SmartServerRequestProtocol.
 | |
| 154 |         """
 | |
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 155 | bytes = self._get_line() | 
| 3245.4.16
by Andrew Bennetts Remove duplication of request version identification logic in wsgi.py | 156 | protocol_factory, unused_bytes = _get_protocol_factory_for_bytes(bytes) | 
| 3245.4.14
by Andrew Bennetts Merge from bzr.dev (via loom thread). | 157 | protocol = protocol_factory( | 
| 2692.1.11
by Andrew Bennetts Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured. | 158 | self.backing_transport, self._write_out, self.root_client_path) | 
| 3245.4.16
by Andrew Bennetts Remove duplication of request version identification logic in wsgi.py | 159 | protocol.accept_bytes(unused_bytes) | 
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 160 | return protocol | 
| 161 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 162 | def _serve_one_request(self, protocol): | 
| 163 | """Read one request from input, process, send back a response. | |
| 164 |         
 | |
| 165 |         :param protocol: a SmartServerRequestProtocol.
 | |
| 166 |         """
 | |
| 167 | try: | |
| 168 | self._serve_one_request_unguarded(protocol) | |
| 169 | except KeyboardInterrupt: | |
| 170 |             raise
 | |
| 171 | except Exception, e: | |
| 172 | self.terminate_due_to_error() | |
| 173 | ||
| 174 | def terminate_due_to_error(self): | |
| 175 | """Called when an unhandled exception from the protocol occurs.""" | |
| 176 | raise NotImplementedError(self.terminate_due_to_error) | |
| 177 | ||
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 178 | def _get_bytes(self, desired_count): | 
| 179 | """Get some bytes from the medium. | |
| 180 | ||
| 181 |         :param desired_count: number of bytes we want to read.
 | |
| 182 |         """
 | |
| 183 | raise NotImplementedError(self._get_bytes) | |
| 184 | ||
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 185 | def _get_line(self): | 
| 186 | """Read bytes from this request's response until a newline byte. | |
| 187 |         
 | |
| 188 |         This isn't particularly efficient, so should only be used when the
 | |
| 189 |         expected size of the line is quite short.
 | |
| 190 | ||
| 191 |         :returns: a string of bytes ending in a newline (byte 0x0A).
 | |
| 192 |         """
 | |
| 3236.3.2
by Andrew Bennetts Fix SmartServerPipeStreamMedium._get_line too. | 193 | newline_pos = -1 | 
| 194 | bytes = '' | |
| 195 | while newline_pos == -1: | |
| 196 | new_bytes = self._get_bytes(1) | |
| 197 | bytes += new_bytes | |
| 198 | if new_bytes == '': | |
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 199 |                 # Ran out of bytes before receiving a complete line.
 | 
| 3236.3.2
by Andrew Bennetts Fix SmartServerPipeStreamMedium._get_line too. | 200 | return bytes | 
| 201 | newline_pos = bytes.find('\n') | |
| 202 | line = bytes[:newline_pos+1] | |
| 203 | self._push_back(bytes[newline_pos+1:]) | |
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 204 | return line | 
| 3236.3.2
by Andrew Bennetts Fix SmartServerPipeStreamMedium._get_line too. | 205 | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 206 | |
| 207 | class SmartServerSocketStreamMedium(SmartServerStreamMedium): | |
| 208 | ||
| 2692.1.11
by Andrew Bennetts Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured. | 209 | def __init__(self, sock, backing_transport, root_client_path='/'): | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 210 | """Constructor. | 
| 211 | ||
| 212 |         :param sock: the socket the server will read from.  It will be put
 | |
| 213 |             into blocking mode.
 | |
| 214 |         """
 | |
| 2692.1.11
by Andrew Bennetts Improve test coverage by making SmartTCPServer_for_testing by default create a server that does not serve the backing transport's root at its own root. This mirrors the way most HTTP smart servers are configured. | 215 | SmartServerStreamMedium.__init__( | 
| 216 | self, backing_transport, root_client_path=root_client_path) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 217 | sock.setblocking(True) | 
| 218 | self.socket = sock | |
| 219 | ||
| 220 | def _serve_one_request_unguarded(self, protocol): | |
| 221 | while protocol.next_read_size(): | |
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 222 | bytes = self._get_bytes(4096) | 
| 223 | if bytes == '': | |
| 224 | self.finished = True | |
| 225 |                 return
 | |
| 226 | protocol.accept_bytes(bytes) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 227 | |
| 3245.4.21
by Andrew Bennetts Remove 'excess_buffer' attribute and another crufty comment. | 228 | self._push_back(protocol.unused_data) | 
| 3195.3.18
by Andrew Bennetts call_with_body_bytes now works with v3 (e.g. test_copy_content_remote_to_local passes). Lots of debugging cruft, though. | 229 | |
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 230 | def _get_bytes(self, desired_count): | 
| 3236.3.5
by Andrew Bennetts Add _get_push_back_buffer helper. | 231 | if self._push_back_buffer is not None: | 
| 232 | return self._get_push_back_buffer() | |
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 233 |         # We ignore the desired_count because on sockets it's more efficient to
 | 
| 234 |         # read 4k at a time.
 | |
| 235 | return self.socket.recv(4096) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 236 | |
| 237 | def terminate_due_to_error(self): | |
| 3245.4.59
by Andrew Bennetts Various tweaks in response to Martin's review. | 238 |         # TODO: This should log to a server log file, but no such thing
 | 
| 239 |         # exists yet.  Andrew Bennetts 2006-09-29.
 | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 240 | self.socket.close() | 
| 241 | self.finished = True | |
| 242 | ||
| 243 | def _write_out(self, bytes): | |
| 3118.2.1
by Andrew Bennetts (andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall. | 244 | osutils.send_all(self.socket, bytes) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 245 | |
| 246 | ||
| 247 | class SmartServerPipeStreamMedium(SmartServerStreamMedium): | |
| 248 | ||
| 249 | def __init__(self, in_file, out_file, backing_transport): | |
| 250 | """Construct new server. | |
| 251 | ||
| 252 |         :param in_file: Python file from which requests can be read.
 | |
| 253 |         :param out_file: Python file to write responses.
 | |
| 254 |         :param backing_transport: Transport for the directory served.
 | |
| 255 |         """
 | |
| 256 | SmartServerStreamMedium.__init__(self, backing_transport) | |
| 2018.5.161
by Andrew Bennetts Reinstate forcing binary mode on windows in SmartServerStreamMedium. | 257 | if sys.platform == 'win32': | 
| 258 |             # force binary mode for files
 | |
| 259 | import msvcrt | |
| 260 | for f in (in_file, out_file): | |
| 261 | fileno = getattr(f, 'fileno', None) | |
| 262 | if fileno: | |
| 263 | msvcrt.setmode(fileno(), os.O_BINARY) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 264 | self._in = in_file | 
| 265 | self._out = out_file | |
| 266 | ||
| 267 | def _serve_one_request_unguarded(self, protocol): | |
| 268 | while True: | |
| 269 | bytes_to_read = protocol.next_read_size() | |
| 270 | if bytes_to_read == 0: | |
| 271 |                 # Finished serving this request.
 | |
| 272 | self._out.flush() | |
| 273 |                 return
 | |
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 274 | bytes = self._get_bytes(bytes_to_read) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 275 | if bytes == '': | 
| 276 |                 # Connection has been closed.
 | |
| 277 | self.finished = True | |
| 278 | self._out.flush() | |
| 279 |                 return
 | |
| 280 | protocol.accept_bytes(bytes) | |
| 281 | ||
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 282 | def _get_bytes(self, desired_count): | 
| 3236.3.4
by Andrew Bennetts Rename 'push_back' attribute to '_push_back_buffer', add some docstrings, and remove a little bit of redundant code from SmartServerSocketStreamMedium._serve_one_request_unguarded. | 283 | if self._push_back_buffer is not None: | 
| 3236.3.5
by Andrew Bennetts Add _get_push_back_buffer helper. | 284 | return self._get_push_back_buffer() | 
| 2432.2.2
by Andrew Bennetts Smart server mediums now detect which protocol version a request is and dispatch accordingly. | 285 | return self._in.read(desired_count) | 
| 286 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 287 | def terminate_due_to_error(self): | 
| 288 |         # TODO: This should log to a server log file, but no such thing
 | |
| 289 |         # exists yet.  Andrew Bennetts 2006-09-29.
 | |
| 290 | self._out.close() | |
| 291 | self.finished = True | |
| 292 | ||
| 293 | def _write_out(self, bytes): | |
| 294 | self._out.write(bytes) | |
| 295 | ||
| 296 | ||
| 297 | class SmartClientMediumRequest(object): | |
| 298 | """A request on a SmartClientMedium. | |
| 299 | ||
| 300 |     Each request allows bytes to be provided to it via accept_bytes, and then
 | |
| 301 |     the response bytes to be read via read_bytes.
 | |
| 302 | ||
| 303 |     For instance:
 | |
| 304 |     request.accept_bytes('123')
 | |
| 305 |     request.finished_writing()
 | |
| 306 |     result = request.read_bytes(3)
 | |
| 307 |     request.finished_reading()
 | |
| 308 | ||
| 309 |     It is up to the individual SmartClientMedium whether multiple concurrent
 | |
| 310 |     requests can exist. See SmartClientMedium.get_request to obtain instances 
 | |
| 311 |     of SmartClientMediumRequest, and the concrete Medium you are using for 
 | |
| 312 |     details on concurrency and pipelining.
 | |
| 313 |     """
 | |
| 314 | ||
| 315 | def __init__(self, medium): | |
| 316 | """Construct a SmartClientMediumRequest for the medium medium.""" | |
| 317 | self._medium = medium | |
| 318 |         # we track state by constants - we may want to use the same
 | |
| 319 |         # pattern as BodyReader if it gets more complex.
 | |
| 320 |         # valid states are: "writing", "reading", "done"
 | |
| 321 | self._state = "writing" | |
| 322 | ||
| 323 | def accept_bytes(self, bytes): | |
| 324 | """Accept bytes for inclusion in this request. | |
| 325 | ||
| 326 |         This method may not be be called after finished_writing() has been
 | |
| 327 |         called.  It depends upon the Medium whether or not the bytes will be
 | |
| 328 |         immediately transmitted. Message based Mediums will tend to buffer the
 | |
| 329 |         bytes until finished_writing() is called.
 | |
| 330 | ||
| 331 |         :param bytes: A bytestring.
 | |
| 332 |         """
 | |
| 333 | if self._state != "writing": | |
| 334 | raise errors.WritingCompleted(self) | |
| 335 | self._accept_bytes(bytes) | |
| 336 | ||
| 337 | def _accept_bytes(self, bytes): | |
| 338 | """Helper for accept_bytes. | |
| 339 | ||
| 340 |         Accept_bytes checks the state of the request to determing if bytes
 | |
| 341 |         should be accepted. After that it hands off to _accept_bytes to do the
 | |
| 342 |         actual acceptance.
 | |
| 343 |         """
 | |
| 344 | raise NotImplementedError(self._accept_bytes) | |
| 345 | ||
| 346 | def finished_reading(self): | |
| 347 | """Inform the request that all desired data has been read. | |
| 348 | ||
| 349 |         This will remove the request from the pipeline for its medium (if the
 | |
| 350 |         medium supports pipelining) and any further calls to methods on the
 | |
| 351 |         request will raise ReadingCompleted.
 | |
| 352 |         """
 | |
| 353 | if self._state == "writing": | |
| 354 | raise errors.WritingNotComplete(self) | |
| 355 | if self._state != "reading": | |
| 356 | raise errors.ReadingCompleted(self) | |
| 357 | self._state = "done" | |
| 358 | self._finished_reading() | |
| 359 | ||
| 360 | def _finished_reading(self): | |
| 361 | """Helper for finished_reading. | |
| 362 | ||
| 363 |         finished_reading checks the state of the request to determine if 
 | |
| 364 |         finished_reading is allowed, and if it is hands off to _finished_reading
 | |
| 365 |         to perform the action.
 | |
| 366 |         """
 | |
| 367 | raise NotImplementedError(self._finished_reading) | |
| 368 | ||
| 369 | def finished_writing(self): | |
| 370 | """Finish the writing phase of this request. | |
| 371 | ||
| 372 |         This will flush all pending data for this request along the medium.
 | |
| 373 |         After calling finished_writing, you may not call accept_bytes anymore.
 | |
| 374 |         """
 | |
| 375 | if self._state != "writing": | |
| 376 | raise errors.WritingCompleted(self) | |
| 377 | self._state = "reading" | |
| 378 | self._finished_writing() | |
| 379 | ||
| 380 | def _finished_writing(self): | |
| 381 | """Helper for finished_writing. | |
| 382 | ||
| 383 |         finished_writing checks the state of the request to determine if 
 | |
| 384 |         finished_writing is allowed, and if it is hands off to _finished_writing
 | |
| 385 |         to perform the action.
 | |
| 386 |         """
 | |
| 387 | raise NotImplementedError(self._finished_writing) | |
| 388 | ||
| 389 | def read_bytes(self, count): | |
| 390 | """Read bytes from this requests response. | |
| 391 | ||
| 392 |         This method will block and wait for count bytes to be read. It may not
 | |
| 393 |         be invoked until finished_writing() has been called - this is to ensure
 | |
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 394 |         a message-based approach to requests, for compatibility with message
 | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 395 |         based mediums like HTTP.
 | 
| 396 |         """
 | |
| 397 | if self._state == "writing": | |
| 398 | raise errors.WritingNotComplete(self) | |
| 399 | if self._state != "reading": | |
| 400 | raise errors.ReadingCompleted(self) | |
| 401 | return self._read_bytes(count) | |
| 402 | ||
| 403 | def _read_bytes(self, count): | |
| 404 | """Helper for read_bytes. | |
| 405 | ||
| 406 |         read_bytes checks the state of the request to determing if bytes
 | |
| 407 |         should be read. After that it hands off to _read_bytes to do the
 | |
| 408 |         actual read.
 | |
| 409 |         """
 | |
| 410 | raise NotImplementedError(self._read_bytes) | |
| 411 | ||
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 412 | def read_line(self): | 
| 413 | """Read bytes from this request's response until a newline byte. | |
| 414 |         
 | |
| 415 |         This isn't particularly efficient, so should only be used when the
 | |
| 416 |         expected size of the line is quite short.
 | |
| 417 | ||
| 418 |         :returns: a string of bytes ending in a newline (byte 0x0A).
 | |
| 419 |         """
 | |
| 420 |         # XXX: this duplicates SmartClientRequestProtocolOne._recv_tuple
 | |
| 421 | line = '' | |
| 422 | while not line or line[-1] != '\n': | |
| 423 | new_char = self.read_bytes(1) | |
| 424 | line += new_char | |
| 425 | if new_char == '': | |
| 3195.2.1
by Andrew Bennetts Improve test coverage, and fix a bug revealed by the improved coverage. | 426 |                 # end of file encountered reading from server
 | 
| 427 | raise errors.ConnectionReset( | |
| 428 | "please check connectivity and permissions", | |
| 429 | "(and try -Dhpss if further diagnosis is required)") | |
| 2432.2.7
by Andrew Bennetts Use less confusing version strings, and define REQUEST_VERSION_TWO/RESPONSE_VERSION_TWO constants for them. | 430 | return line | 
| 431 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 432 | |
| 433 | class SmartClientMedium(object): | |
| 434 | """Smart client is a medium for sending smart protocol requests over.""" | |
| 435 | ||
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 436 | def __init__(self, base): | 
| 3241.1.1
by Andrew Bennetts Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium. | 437 | super(SmartClientMedium, self).__init__() | 
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 438 | self.base = base | 
| 3241.1.4
by Andrew Bennetts Use get_smart_medium as suggested by Robert, and deal with the fallout. | 439 | self._protocol_version_error = None | 
| 3241.1.1
by Andrew Bennetts Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium. | 440 | self._protocol_version = None | 
| 3245.4.47
by Andrew Bennetts Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP). | 441 | self._done_hello = False | 
| 3435.1.1
by Andrew Bennetts Define _remote_is_at_least_1_2 on SmartClientMedium base class, rather than just SmartClientStreamMedium. | 442 |         # Be optimistic: we assume the remote end can accept new remote
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 443 |         # requests until we get an error saying otherwise.
 | 
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 444 |         # _remote_version_is_before tracks the bzr version the remote side
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 445 |         # can be based on what we've seen so far.
 | 
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 446 | self._remote_version_is_before = None | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 447 | |
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 448 | def _is_remote_before(self, version_tuple): | 
| 3502.1.1
by Matt Nordhoff Fix a docstring typo, and a two-expression ``raise`` statement | 449 | """Is it possible the remote side supports RPCs for a given version? | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 450 | |
| 451 |         Typical use::
 | |
| 452 | ||
| 453 |             needed_version = (1, 2)
 | |
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 454 |             if medium._is_remote_before(needed_version):
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 455 |                 fallback_to_pre_1_2_rpc()
 | 
| 456 |             else:
 | |
| 457 |                 try:
 | |
| 458 |                     do_1_2_rpc()
 | |
| 459 |                 except UnknownSmartMethod:
 | |
| 3453.4.9
by Andrew Bennetts Rename _remote_is_not to _remember_remote_is_before. | 460 |                     medium._remember_remote_is_before(needed_version)
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 461 |                     fallback_to_pre_1_2_rpc()
 | 
| 462 | ||
| 3453.4.9
by Andrew Bennetts Rename _remote_is_not to _remember_remote_is_before. | 463 |         :seealso: _remember_remote_is_before
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 464 |         """
 | 
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 465 | if self._remote_version_is_before is None: | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 466 |             # So far, the remote side seems to support everything
 | 
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 467 | return False | 
| 468 | return version_tuple >= self._remote_version_is_before | |
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 469 | |
| 3453.4.9
by Andrew Bennetts Rename _remote_is_not to _remember_remote_is_before. | 470 | def _remember_remote_is_before(self, version_tuple): | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 471 | """Tell this medium that the remote side is older the given version. | 
| 472 | ||
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 473 |         :seealso: _is_remote_before
 | 
| 3453.4.1
by Andrew Bennetts Better infrastructure on SmartClientMedium for tracking the remote version. | 474 |         """
 | 
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 475 | if (self._remote_version_is_before is not None and | 
| 476 | version_tuple > self._remote_version_is_before): | |
| 3502.1.1
by Matt Nordhoff Fix a docstring typo, and a two-expression ``raise`` statement | 477 | raise AssertionError( | 
| 3453.4.9
by Andrew Bennetts Rename _remote_is_not to _remember_remote_is_before. | 478 | "_remember_remote_is_before(%r) called, but " | 
| 479 | "_remember_remote_is_before(%r) was called previously." | |
| 3453.4.10
by Andrew Bennetts Change _is_remote_at_least to _is_remote_before. | 480 | % (version_tuple, self._remote_version_is_before)) | 
| 481 | self._remote_version_is_before = version_tuple | |
| 3241.1.1
by Andrew Bennetts Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium. | 482 | |
| 483 | def protocol_version(self): | |
| 3245.4.47
by Andrew Bennetts Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP). | 484 | """Find out if 'hello' smart request works.""" | 
| 3241.1.4
by Andrew Bennetts Use get_smart_medium as suggested by Robert, and deal with the fallout. | 485 | if self._protocol_version_error is not None: | 
| 486 | raise self._protocol_version_error | |
| 3245.4.47
by Andrew Bennetts Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP). | 487 | if not self._done_hello: | 
| 3241.1.4
by Andrew Bennetts Use get_smart_medium as suggested by Robert, and deal with the fallout. | 488 | try: | 
| 489 | medium_request = self.get_request() | |
| 490 |                 # Send a 'hello' request in protocol version one, for maximum
 | |
| 491 |                 # backwards compatibility.
 | |
| 492 | client_protocol = SmartClientRequestProtocolOne(medium_request) | |
| 3245.4.47
by Andrew Bennetts Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP). | 493 | client_protocol.query_version() | 
| 494 | self._done_hello = True | |
| 3241.1.4
by Andrew Bennetts Use get_smart_medium as suggested by Robert, and deal with the fallout. | 495 | except errors.SmartProtocolError, e: | 
| 496 |                 # Cache the error, just like we would cache a successful
 | |
| 497 |                 # result.
 | |
| 498 | self._protocol_version_error = e | |
| 499 |                 raise
 | |
| 3245.4.47
by Andrew Bennetts Don't automatically send 'hello' requests from RemoteBzrDirFormat.probe_transport unless we have to (i.e. the transport is HTTP). | 500 | return '2' | 
| 501 | ||
| 502 | def should_probe(self): | |
| 503 | """Should RemoteBzrDirFormat.probe_transport send a smart request on | |
| 504 |         this medium?
 | |
| 505 | ||
| 506 |         Some transports are unambiguously smart-only; there's no need to check
 | |
| 507 |         if the transport is able to carry smart requests, because that's all
 | |
| 508 |         it is for.  In those cases, this method should return False.
 | |
| 509 | ||
| 510 |         But some HTTP transports can sometimes fail to carry smart requests,
 | |
| 511 |         but still be usuable for accessing remote bzrdirs via plain file
 | |
| 512 |         accesses.  So for those transports, their media should return True here
 | |
| 513 |         so that RemoteBzrDirFormat can determine if it is appropriate for that
 | |
| 514 |         transport.
 | |
| 515 |         """
 | |
| 516 | return False | |
| 3241.1.1
by Andrew Bennetts Shift protocol version querying from RemoteBzrDirFormat into SmartClientMedium. | 517 | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 518 | def disconnect(self): | 
| 519 | """If this medium maintains a persistent connection, close it. | |
| 520 |         
 | |
| 521 |         The default implementation does nothing.
 | |
| 522 |         """
 | |
| 523 | ||
| 3431.3.11
by Andrew Bennetts Push remote_path_from_transport logic into SmartClientMedium, removing special-casing of bzr+http from _SmartClient. | 524 | def remote_path_from_transport(self, transport): | 
| 525 | """Convert transport into a path suitable for using in a request. | |
| 526 |         
 | |
| 527 |         Note that the resulting remote path doesn't encode the host name or
 | |
| 528 |         anything but path, so it is only safe to use it in requests sent over
 | |
| 529 |         the medium from the matching transport.
 | |
| 530 |         """
 | |
| 531 | medium_base = urlutils.join(self.base, '/') | |
| 532 | rel_url = urlutils.relative_url(medium_base, transport.base) | |
| 533 | return urllib.unquote(rel_url) | |
| 534 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 535 | |
| 536 | class SmartClientStreamMedium(SmartClientMedium): | |
| 537 | """Stream based medium common class. | |
| 538 | ||
| 539 |     SmartClientStreamMediums operate on a stream. All subclasses use a common
 | |
| 540 |     SmartClientStreamMediumRequest for their requests, and should implement
 | |
| 541 |     _accept_bytes and _read_bytes to allow the request objects to send and
 | |
| 542 |     receive bytes.
 | |
| 543 |     """
 | |
| 544 | ||
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 545 | def __init__(self, base): | 
| 546 | SmartClientMedium.__init__(self, base) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 547 | self._current_request = None | 
| 548 | ||
| 549 | def accept_bytes(self, bytes): | |
| 550 | self._accept_bytes(bytes) | |
| 551 | ||
| 552 | def __del__(self): | |
| 553 | """The SmartClientStreamMedium knows how to close the stream when it is | |
| 554 |         finished with it.
 | |
| 555 |         """
 | |
| 556 | self.disconnect() | |
| 557 | ||
| 558 | def _flush(self): | |
| 559 | """Flush the output stream. | |
| 560 |         
 | |
| 561 |         This method is used by the SmartClientStreamMediumRequest to ensure that
 | |
| 562 |         all data for a request is sent, to avoid long timeouts or deadlocks.
 | |
| 563 |         """
 | |
| 564 | raise NotImplementedError(self._flush) | |
| 565 | ||
| 566 | def get_request(self): | |
| 567 | """See SmartClientMedium.get_request(). | |
| 568 | ||
| 569 |         SmartClientStreamMedium always returns a SmartClientStreamMediumRequest
 | |
| 570 |         for get_request.
 | |
| 571 |         """
 | |
| 572 | return SmartClientStreamMediumRequest(self) | |
| 573 | ||
| 574 | def read_bytes(self, count): | |
| 575 | return self._read_bytes(count) | |
| 576 | ||
| 577 | ||
| 578 | class SmartSimplePipesClientMedium(SmartClientStreamMedium): | |
| 579 | """A client medium using simple pipes. | |
| 580 |     
 | |
| 581 |     This client does not manage the pipes: it assumes they will always be open.
 | |
| 582 |     """
 | |
| 583 | ||
| 3431.3.1
by Andrew Bennetts First rough cut of a fix for bug #230550, by adding .base to SmartClientMedia rather than relying on other objects to track this accurately while reusing client media. | 584 | def __init__(self, readable_pipe, writeable_pipe, base): | 
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 585 | SmartClientStreamMedium.__init__(self, base) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 586 | self._readable_pipe = readable_pipe | 
| 587 | self._writeable_pipe = writeable_pipe | |
| 588 | ||
| 589 | def _accept_bytes(self, bytes): | |
| 590 | """See SmartClientStreamMedium.accept_bytes.""" | |
| 591 | self._writeable_pipe.write(bytes) | |
| 592 | ||
| 593 | def _flush(self): | |
| 594 | """See SmartClientStreamMedium._flush().""" | |
| 595 | self._writeable_pipe.flush() | |
| 596 | ||
| 597 | def _read_bytes(self, count): | |
| 598 | """See SmartClientStreamMedium._read_bytes.""" | |
| 599 | return self._readable_pipe.read(count) | |
| 600 | ||
| 601 | ||
| 602 | class SmartSSHClientMedium(SmartClientStreamMedium): | |
| 603 | """A client medium using SSH.""" | |
| 604 | ||
| 605 | def __init__(self, host, port=None, username=None, password=None, | |
| 3431.3.1
by Andrew Bennetts First rough cut of a fix for bug #230550, by adding .base to SmartClientMedia rather than relying on other objects to track this accurately while reusing client media. | 606 | base=None, vendor=None, bzr_remote_path=None): | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 607 | """Creates a client that will connect on the first use. | 
| 608 |         
 | |
| 609 |         :param vendor: An optional override for the ssh vendor to use. See
 | |
| 610 |             bzrlib.transport.ssh for details on ssh vendors.
 | |
| 611 |         """
 | |
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 612 | SmartClientStreamMedium.__init__(self, base) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 613 | self._connected = False | 
| 614 | self._host = host | |
| 615 | self._password = password | |
| 616 | self._port = port | |
| 617 | self._username = username | |
| 618 | self._read_from = None | |
| 619 | self._ssh_connection = None | |
| 620 | self._vendor = vendor | |
| 621 | self._write_to = None | |
| 1551.18.17
by Aaron Bentley Introduce bzr_remote_path configuration variable | 622 | self._bzr_remote_path = bzr_remote_path | 
| 623 | if self._bzr_remote_path is None: | |
| 624 | symbol_versioning.warn( | |
| 625 | 'bzr_remote_path is required as of bzr 0.92', | |
| 626 | DeprecationWarning, stacklevel=2) | |
| 627 | self._bzr_remote_path = os.environ.get('BZR_REMOTE_PATH', 'bzr') | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 628 | |
| 629 | def _accept_bytes(self, bytes): | |
| 630 | """See SmartClientStreamMedium.accept_bytes.""" | |
| 631 | self._ensure_connection() | |
| 632 | self._write_to.write(bytes) | |
| 633 | ||
| 634 | def disconnect(self): | |
| 635 | """See SmartClientMedium.disconnect().""" | |
| 636 | if not self._connected: | |
| 637 |             return
 | |
| 638 | self._read_from.close() | |
| 639 | self._write_to.close() | |
| 640 | self._ssh_connection.close() | |
| 641 | self._connected = False | |
| 642 | ||
| 643 | def _ensure_connection(self): | |
| 644 | """Connect this medium if not already connected.""" | |
| 645 | if self._connected: | |
| 646 |             return
 | |
| 647 | if self._vendor is None: | |
| 648 | vendor = ssh._get_ssh_vendor() | |
| 649 | else: | |
| 650 | vendor = self._vendor | |
| 651 | self._ssh_connection = vendor.connect_ssh(self._username, | |
| 652 | self._password, self._host, self._port, | |
| 1551.18.17
by Aaron Bentley Introduce bzr_remote_path configuration variable | 653 | command=[self._bzr_remote_path, 'serve', '--inet', | 
| 654 | '--directory=/', '--allow-writes']) | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 655 | self._read_from, self._write_to = \ | 
| 656 | self._ssh_connection.get_filelike_channels() | |
| 657 | self._connected = True | |
| 658 | ||
| 659 | def _flush(self): | |
| 660 | """See SmartClientStreamMedium._flush().""" | |
| 661 | self._write_to.flush() | |
| 662 | ||
| 663 | def _read_bytes(self, count): | |
| 664 | """See SmartClientStreamMedium.read_bytes.""" | |
| 665 | if not self._connected: | |
| 666 | raise errors.MediumNotConnected(self) | |
| 667 | return self._read_from.read(count) | |
| 668 | ||
| 669 | ||
| 3004.2.1
by Vincent Ladeuil Fix 150860 by leaving port as user specified it. | 670 | # Port 4155 is the default port for bzr://, registered with IANA.
 | 
| 671 | BZR_DEFAULT_INTERFACE = '0.0.0.0' | |
| 672 | BZR_DEFAULT_PORT = 4155 | |
| 673 | ||
| 674 | ||
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 675 | class SmartTCPClientMedium(SmartClientStreamMedium): | 
| 676 | """A client medium using TCP.""" | |
| 677 | ||
| 3431.3.1
by Andrew Bennetts First rough cut of a fix for bug #230550, by adding .base to SmartClientMedia rather than relying on other objects to track this accurately while reusing client media. | 678 | def __init__(self, host, port, base): | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 679 | """Creates a client that will connect on the first use.""" | 
| 3431.3.3
by Andrew Bennetts Set 'base' in SmartClientMedium base class. | 680 | SmartClientStreamMedium.__init__(self, base) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 681 | self._connected = False | 
| 682 | self._host = host | |
| 683 | self._port = port | |
| 684 | self._socket = None | |
| 685 | ||
| 686 | def _accept_bytes(self, bytes): | |
| 687 | """See SmartClientMedium.accept_bytes.""" | |
| 688 | self._ensure_connection() | |
| 3118.2.1
by Andrew Bennetts (andrew) Fix #115781 by passing no more than 64k at a time to socket.sendall. | 689 | osutils.send_all(self._socket, bytes) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 690 | |
| 691 | def disconnect(self): | |
| 692 | """See SmartClientMedium.disconnect().""" | |
| 693 | if not self._connected: | |
| 694 |             return
 | |
| 695 | self._socket.close() | |
| 696 | self._socket = None | |
| 697 | self._connected = False | |
| 698 | ||
| 699 | def _ensure_connection(self): | |
| 700 | """Connect this medium if not already connected.""" | |
| 701 | if self._connected: | |
| 702 |             return
 | |
| 703 | self._socket = socket.socket() | |
| 704 | self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) | |
| 3004.2.1
by Vincent Ladeuil Fix 150860 by leaving port as user specified it. | 705 | if self._port is None: | 
| 706 | port = BZR_DEFAULT_PORT | |
| 707 | else: | |
| 708 | port = int(self._port) | |
| 3180.1.1
by Andrew Bennetts Don't traceback on host name errors when connecting to bzr://... | 709 | try: | 
| 710 | self._socket.connect((self._host, port)) | |
| 711 | except socket.error, err: | |
| 712 |             # socket errors either have a (string) or (errno, string) as their
 | |
| 713 |             # args.
 | |
| 714 | if type(err.args) is str: | |
| 715 | err_msg = err.args | |
| 716 | else: | |
| 717 | err_msg = err.args[1] | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 718 | raise errors.ConnectionError("failed to connect to %s:%d: %s" % | 
| 3180.1.1
by Andrew Bennetts Don't traceback on host name errors when connecting to bzr://... | 719 | (self._host, port, err_msg)) | 
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 720 | self._connected = True | 
| 721 | ||
| 722 | def _flush(self): | |
| 723 | """See SmartClientStreamMedium._flush(). | |
| 724 |         
 | |
| 725 |         For TCP we do no flushing. We may want to turn off TCP_NODELAY and 
 | |
| 726 |         add a means to do a flush, but that can be done in the future.
 | |
| 727 |         """
 | |
| 728 | ||
| 729 | def _read_bytes(self, count): | |
| 730 | """See SmartClientMedium.read_bytes.""" | |
| 731 | if not self._connected: | |
| 732 | raise errors.MediumNotConnected(self) | |
| 733 | return self._socket.recv(count) | |
| 734 | ||
| 735 | ||
| 736 | class SmartClientStreamMediumRequest(SmartClientMediumRequest): | |
| 737 | """A SmartClientMediumRequest that works with an SmartClientStreamMedium.""" | |
| 738 | ||
| 739 | def __init__(self, medium): | |
| 740 | SmartClientMediumRequest.__init__(self, medium) | |
| 741 |         # check that we are safe concurrency wise. If some streams start
 | |
| 742 |         # allowing concurrent requests - i.e. via multiplexing - then this
 | |
| 743 |         # assert should be moved to SmartClientStreamMedium.get_request,
 | |
| 744 |         # and the setting/unsetting of _current_request likewise moved into
 | |
| 745 |         # that class : but its unneeded overhead for now. RBC 20060922
 | |
| 746 | if self._medium._current_request is not None: | |
| 747 | raise errors.TooManyConcurrentRequests(self._medium) | |
| 748 | self._medium._current_request = self | |
| 749 | ||
| 750 | def _accept_bytes(self, bytes): | |
| 751 | """See SmartClientMediumRequest._accept_bytes. | |
| 752 |         
 | |
| 753 |         This forwards to self._medium._accept_bytes because we are operating
 | |
| 754 |         on the mediums stream.
 | |
| 755 |         """
 | |
| 756 | self._medium._accept_bytes(bytes) | |
| 757 | ||
| 758 | def _finished_reading(self): | |
| 759 | """See SmartClientMediumRequest._finished_reading. | |
| 760 | ||
| 761 |         This clears the _current_request on self._medium to allow a new 
 | |
| 762 |         request to be created.
 | |
| 763 |         """
 | |
| 3376.2.4
by Martin Pool Remove every assert statement from bzrlib! | 764 | if self._medium._current_request is not self: | 
| 765 | raise AssertionError() | |
| 2018.5.2
by Andrew Bennetts Start splitting bzrlib/transport/smart.py into a package. | 766 | self._medium._current_request = None | 
| 767 | ||
| 768 | def _finished_writing(self): | |
| 769 | """See SmartClientMediumRequest._finished_writing. | |
| 770 | ||
| 771 |         This invokes self._medium._flush to ensure all bytes are transmitted.
 | |
| 772 |         """
 | |
| 773 | self._medium._flush() | |
| 774 | ||
| 775 | def _read_bytes(self, count): | |
| 776 | """See SmartClientMediumRequest._read_bytes. | |
| 777 |         
 | |
| 778 |         This forwards to self._medium._read_bytes because we are operating
 | |
| 779 |         on the mediums stream.
 | |
| 780 |         """
 | |
| 781 | return self._medium._read_bytes(count) | |
| 3245.4.32
by Andrew Bennetts Undo trivial whitespace change relative to bzr.dev. | 782 |