Class t.p.n.Negotiation(protocol.Protocol):

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 VariablesnegotationOffer

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.
def __init__(self):
Undocumented
def initClient(self, connector, targetHost):
Undocumented
def initServer(self, listener):
Undocumented
def parseLines(self, header):
Undocumented
def sendBlock(self, block):
Undocumented
def debug_doTimer(self, name, timeout, call, *args):
Undocumented
def debug_addTimerCallback(self, name, call, *args):
Undocumented
def debug_forceTimer(self, name):
Undocumented
def debug_forceAllTimers(self):
Undocumented
def debug_cancelAllTimers(self):
Undocumented
def debug_fireTimer(self, name):
Undocumented
def connectionMade(self):

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.
def connectionMadeClient(self):
Undocumented
def sendPlaintextClient(self):
Undocumented
def connectionMadeServer(self):
Undocumented
def sendError(self, why):
Undocumented
def negotiationTimedOut(self):
Undocumented
def stopNegotiationTimer(self):
Undocumented
def dataReceived(self, chunk):

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.
Parametersdataa 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.
def connectionLost(self, reason):

Called when the connection is shut down.

Clear any circular references here, and any external references to this Protocol. The connection has been closed. The reason Failure wraps a twisted.internet.error.ConnectionDone or twisted.internet.error.ConnectionLost instance (or a subclass of one of those).
Parametersreason(type: twisted.python.failure.Failure )
def handlePLAINTEXTServer(self, header):
Undocumented
def sendPlaintextServerAndStartENCRYPTED(self, encrypted):
Undocumented
def sendRedirect(self, redirect):
Undocumented
def handlePLAINTEXTClient(self, header):
Undocumented
def startENCRYPTED(self, encrypted):
Undocumented
def sendHello(self):
This is called on both sides as soon as the encrypted connection is established. This causes a negotiation block to be sent to the other side as an offer.
def handleENCRYPTED(self, header):
Undocumented
def evaluateHello(self, offer):

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
def sendDecision(self, decision, params):
Undocumented
def handleDECIDING(self, header):
Undocumented
def acceptDecision(self, decision):
This is called on the client end when it receives the results of the negotiation from the server. The client must accept this decision (and return the connection parameters dict), or raise NegotiationError to hang up.negotiationResults.
def startTLS(self, cert):
Undocumented
def switchToBanana(self, params):
Undocumented
def negotiationFailed(self, reason):
Undocumented