Q:

Asynchronous client in Twisted not sending / receiving request (using NetStringReceiver)

I am trying to do asynchronous programming using Twisted. What I'm trying to do is create a client which will pass query parameters (in my case, hash type and hash value) and wait for the server's response. I am currently using NetstringReceiver for the parameters. However, am having these problems:

The client is not able to send its request to the server, and
The client hangs forever when I ran it. It seems that there is a callback that's not returning.

Below are the codes for the client and server.
This code is actually based on this tutorial by Dave Peticolas.

Client Code

import os, sys, argparse
from twisted.internet import defer
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.protocols.basic import NetstringReceiver

class QueryProtocol(Protocol):
    response = ''

    def dataReceived(self, data):
        self.response = data

    def connectionLost(self, reason):
        self.responseReceived(self.response)

    def responseReceived(self, response):
        self.factory.response_finished(response)

class QueryFactory(ClientFactory):
    protocol = QueryProtocol

    def __init__(self, deferred):
        self.deferred = deferred

    def response_finished(self, response):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.callback(response)

    def clientConnectionFailed(self, connector, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

class QueryNetProtocol(NetstringReceiver):
    def connectionMade(self):
        self.sendRequest(self.factory.hash_type, self.factory.hash_value)

    def sendRequest(self, hash_type, hash_value):
        self.sendString(hash_type + '.' + hash_value)

    def stringReceived(self, s):
        self.transport.loseConnection()
        self.responseReceived(s)

    def responseReceived(self, response):
        self.factory.handleResponse(response)

class QueryNetFactory(ClientFactory):
    protocol = QueryNetProtocol

    def __init__(self, hash_type, hash_value):
        self.hash_type = hash_type
        self.hash_value = hash_value
        self.deferred = defer.Deferred()

    def handleResponse(self, response):
        d, self.deferred = self.deferred, None
        d.callback(response)

    def clientConnectionLost(self, _, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

    clientConnectionFailed = clientConnectionLost

class QueryProxy(object):
    def __init__(self, host, port):
        self.host = host
        self.port = port

    def query(self, hash_type, hash_value):
        factory = QueryNetFactory(hash_type, hash_value)
        from twisted.internet import reactor
        reactor.connectTCP(self.host, self.port, factory)
        return factory.deferred

def perform_query(host, port):
    d = defer.Deferred()
    from twisted.internet import reactor
    factory = QueryFactory(d)
    reactor.connectTCP(host, port, factory)
    return d

def main(options):
    done = False
    query_result = ""
    host = options.host
    port = int(options.port)
    sha1 = options.sha1
    proxy = QueryProxy(host, port)
    from twisted.internet import reactor

    def process_query_result(response):
        d = proxy.query('sha1', sha1)

        def fail(err):
            print "Problem in processing response : %s" % err
            return response

        return d.addErrback(fail)

    def query_ok(response):
        query_result = response
        done = True

    def query_failed(err):
        print  "Problem in query : %s" % err
        done = True

    def query_done(_):
        if done == True: reactor.stop()

    d = perform_query(host, port)
    d.addCallback(process_query_result)
    d.addCallbacks(query_ok, query_failed)
    d.addBoth(query_done)
    reactor.run()
    print "The result of the query is : %s" % query_result

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("host", help="server host/ip")
    parser.add_argument("port", help="server port number to listen to")
    parser.add_argument("-sha1", help="sha1 value to be queried")
    options = parser.parse_args()
    main(options)

Server Code

import os, sys, argparse
from twisted.internet.protocol import ServerFactory
from twisted.protocols.basic import NetstringReceiver

class GridQueryService(object):
    def query(self, hash_type, hash_value):
        print "this is the query service. Type is %s and value is %s" % (hash_type, hash_value)
        return hash_value

class GridQueryProtocol(NetstringReceiver):
    def stringReceived(self, request):
        print >>sys.stderr, request
        if '.' not in request: 
            self.transport.loseConnection() 
            return
        hash_type, hash_value = request.split('.')
        self.formRequestReceived(hash_type, hash_value)

    def formRequestReceived(self, hash_type, hash_value):
        found_flag = self.factory.query(hash_type, hash_value)
        if found_flag: self.sendString(str(found_flag))
        self.transport.loseConnection()

class GridQueryFactory(ServerFactory):
    protocol = GridQueryProtocol

    def __init__(self, service):
        self.service = service

    def query(self, hash_type, hash_value):
        return self.service.query(hash_type, hash_value)

def main(options):
    grid_query_service = GridQueryService()
    grid_query_factory = GridQueryFactory(grid_query_service)
    from twisted.internet import reactor
    port = reactor.listenTCP(int(options.port), grid_query_factory, interface=options.host)
    print "Serving GRID query service on %s" % str(port.getHost())
    reactor.run()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("host", help="server host/ip")
    parser.add_argument("port", help="server port number to listen to")
    options = parser.parse_args()
    main(options)

Any ideas on how to resolve this? Thanks in advance. Help is greatly appreciated!

A:

Your server may use netstrings and your client may contain code for using netstrings, but your client doesn't use netstrings.
main calls perform_query.  perform_query makes a QueryFactory which connects a QueryProtocol which never sends any data and doesn't have any logic related to handling netstrings even if it did.

