Part of twisted.pb.negotiate View Source View In Hierarchy
This is the first protocol to speak over the wire. It is responsible for negotiating the connection parameters, then switching the connection over to the actual Banana protocol. This removes all the details of negotiation from Banana, and makes it easier to use a more complex scheme (including a STARTTLS transition) in PB.
Negotiation consists of three phases. In the PLAINTEXT phase, the client side (i.e. the one which initiated the connection) sends an HTTP-compatible GET request for the target Tub ID. This request includes an Connection: Upgrade header. The GET request serves a couple of purposes: if a PB client is accidentally pointed at an HTTP server, it will trigger a sensible 404 Error instead of getting confused. A regular HTTP server can be used to send back a 303 Redirect, allowing Apache (or whatever) to be used as a redirection server.
After sending the GET request, the client waits for the server to send a 101 Switching Protocols command, then starts the TLS session. It may also receive a 303 Redirect command, in which case it drops the connection and tries again with the new target.
In the PLAINTEXT phase, the server side (i.e. the one which accepted the connection) waits for the client's GET request, extracts the TubID from the first line, consults the local Listener object to locate the appropriate Tub (and its certificate), sends back a 101 Switching Protocols response, then starts the TLS session with the Tub's certificate. If the Listener reports that the requested Tub is listening elsewhere, the server sends back a 303 Redirect instead, and then drops the connection.
By the end of the PLAINTEXT phase, both ends know which Tub they are using (self.tub has been set).
Both sides send a Hello Block upon entering the ENCRYPTED phase, which in practice means just after starting the TLS session. The Hello block contains the negotiation offer, as a series of Key: Value lines separated by \r\n delimiters and terminated by a blank line. Upon receiving the other end's Hello block, each side switches to the DECIDING phase, and then evaluates the received Hello message.
Each side compares TubIDs, and the side with the lexicographically higher value becomes the Master. (If, for some reason, one side does not claim a TubID, its value is treated as None, which always compares *less* than any actual TubID, so the non-TubID side will probably not be the Master. Any possible ties are resolved by having the server side be the master). Both sides know the other's TubID, so both sides know whether they are the Master or not.
The Master has two jobs to do. The first is that it compares the negotiation offer against its own capabilities, and comes to a decision about what the connection parameters shall be. It may decide that the two sides are not compatible, in which case it will abandon the connection. The second job is to decide whether to continue to use the connection at all: if the Master already has a connection to the other Tub, it will drop this new one. This decision must be made by the Master (as opposed to the Server) because it is possible for both Tubs to connect to each other simultaneously, and this design avoids a race condition that could otherwise drop *both* connections.
If the Master decides to continue with the connection, it sends the Decision block to the non-master side. It then swaps out the Negotiation protocol for a new Banana protocol instance that has been created with the same parameters that were used to create the Decision block.
The non-master side is waiting in the DECIDING phase for this block. Upon receiving it, the non-master side evaluates the connection parameters and either drops the connection or swaps in a new Banana protocol instance with the same parameters. At this point, negotiation is complete and the Negotiation instances are dropped.| Instance Variables | negotationOffer | a dict which describes what we will offer to the far side. Each key/value pair will be put into a rfc822-style header and sent from the client to the server when the connection is established. On the server side, handleNegotiation() uses negotationOffer to indicate what we are locally capable of. Subclasses may influence the negotiation process by modifying this dictionary before connectionMade() is called. |
| negotiationResults | a dict which describes what the two ends have agreed upon. This is computed by the server, stored locally, and sent down to the client. The client receives it and stores it without modification (server chooses). In general, the negotiationResults are the same on both sides of the same connection. However there may be certain parameters which are sent as part of the negotiation block (the PB TubID, for example) which will not. |
| Line # | Kind | Name | Docs |
|---|---|---|---|
| 183 | Method | __init__ | Undocumented |
| 208 | Method | initClient | Undocumented |
| 220 | Method | initServer | Undocumented |
| 226 | Method | parseLines | Undocumented |
| 236 | Method | sendBlock | Undocumented |
| 243 | Method | debug_doTimer | Undocumented |
| 255 | Method | debug_addTimerCallback | Undocumented |
| 261 | Method | debug_forceTimer | Undocumented |
| 266 | Method | debug_forceAllTimers | Undocumented |
| 272 | Method | debug_cancelAllTimers | Undocumented |
| 278 | Method | debug_fireTimer | Undocumented |
| 284 | Method | connectionMade | Called when a connection is made. |
| 298 | Method | connectionMadeClient | Undocumented |
| 305 | Method | sendPlaintextClient | Undocumented |
| 332 | Method | connectionMadeServer | Undocumented |
| 343 | Method | sendError | Undocumented |
| 346 | Method | negotiationTimedOut | Undocumented |
| 353 | Method | stopNegotiationTimer | Undocumented |
| 358 | Method | dataReceived | Called whenever data is received. |
| 422 | Method | connectionLost | Called when the connection is shut down. |
| 439 | Method | handlePLAINTEXTServer | Undocumented |
| 497 | Method | sendPlaintextServerAndStartENCRYPTED | Undocumented |
| 518 | Method | sendRedirect | Undocumented |
| 524 | Method | handlePLAINTEXTClient | Undocumented |
| 544 | Method | startENCRYPTED | Undocumented |
| 559 | Method | sendHello | This is called on both sides as soon as the encrypted connection |
| 581 | Method | handleENCRYPTED | Undocumented |
| 599 | Method | evaluateHello | Evaluate the HELLO message sent by the other side. We compare |
| 754 | Method | sendDecision | Undocumented |
| 764 | Method | handleDECIDING | Undocumented |
| 781 | Method | acceptDecision | This is called on the client end when it receives the results of |
| 805 | Method | startTLS | Undocumented |
| 829 | Method | switchToBanana | Undocumented |
| 863 | Method | negotiationFailed | Undocumented |
Inherited from BaseProtocol (via Protocol):
| Line # | Kind | Name | Docs |
|---|---|---|---|
| 325 | Method | makeConnection | Make a connection to a transport and a server. |
Called when a connection is made.
This may be considered the initializer of the protocol, because it is called when the connection is completed. For clients, this is called once the connection to the server has been established; for servers, this is called after an accept() call stops blocking and a socket has been received. If you need to send any greeting or initial message, do it here.Called whenever data is received.
Use this method to translate to a higher-level message. Usually, some callback will be made upon the receipt of each complete protocol message.| Parameters | data | a string of indeterminate length. Please keep in mind that you will probably need to buffer some data, as partial (or multiple) protocol messages may be received! I recommend that unit tests for protocols call through to this method with differing chunk sizes, down to one byte at a time. |
Called when the connection is shut down.
Clear any circular references here, and any external references to this Protocol. The connection has been closed. Thereason
Failure wraps a twisted.internet.error.ConnectionDone
or twisted.internet.error.ConnectionLost
instance (or a subclass of one of those).
| Parameters | reason | (type: twisted.python.failure.Failure ) |
Evaluate the HELLO message sent by the other side. We compare TubIDs, and the higher value becomes the 'master' and makes the negotiation decisions.
This method returns a tuple of DECISION,PARAMS. There are a few different possibilities:- We are the master, we make a negotiation decision: DECISION is the block of data to send back to the non-master side, PARAMS are the connection parameters we will use ourselves. - We are the master, we can't accomodate their request: raise NegotiationError - We are not the master: DECISION is None