porgarmingduod
porgarmingduod

Reputation: 7878

How to separate two different phases of a protocol in Twisted?

I'm trying to learn how to do stuff with Twisted, and I'm a bit stuck on one concept: Making a protocol that basically communicates in two separate phases: First a short handshake and authentication, then actual work.

My naive approach is to write a protocol like this:

def stringReceived(self, data):
    if self.state == "authenticate":
        handle_auth(data)
    else:
        handle_actual_work(data)

I'm having a hard time figuring out the twisted way of doing this. Is the above normal? It seems to me that it would make much more sense to write one protocol that does authentication and another that deals only with authenticated clients, but how, exactly, would I do that?

I looked at the similar question Twisted: How can I identify protocol on initial connection, then delegate to appropriate Protocol implementation?. The solution given there boils down to basically the same as my current approach. Is this really the proper approach?

Upvotes: 3

Views: 747

Answers (1)

Glyph
Glyph

Reputation: 31860

Yes, your version is pretty normal. Arguably Twisted should have more support for state-machines, since it has about 100 different implementations of this general pattern internally for different protocols, different event constellations, etc. But this part isn't really Twisted's job, per se: Twisted is exposing your object to the network, and methods will be called on it (in this case, stringReceived). What you do with that message is totally up to your object, and an if statement is a totally reasonable thing to do with it :).

At this level, the question is not really about the "twisted way" but, rather, about better Python idioms for state machines and methods which are context-dependent. Depending on the exact needs of your protocol, your current approach may be fine, or you might want to dispatch to a method with a special name, like this:

def stringReceived(self, data):
    getattr(self, "stringReceived_{}".format(self.state))(data)

def stringReceived_authenticate(self, data):
    if self.auth_ok(data):
       self.state = 'normal'
    else:
       self.transport.loseConnection()

def stringReceived_normal(self, data):
    self.do_stuff(data)

... or getting even fancier, you might want to use decorators, or metaclasses, or something else entirely. Nothing is wrong with any of these approaches.

Upvotes: 3

Related Questions