Skip to content

Instantly share code, notes, and snippets.

@lehrblogger
Last active December 10, 2015 17:58
Show Gist options
  • Save lehrblogger/4471424 to your computer and use it in GitHub Desktop.
Save lehrblogger/4471424 to your computer and use it in GitHub Desktop.
httplib error with gevent, and xmlrpclib (update: no longer requires sleekxmpp to repro)
# from geventhttpclient import httplib
# httplib.patch()
from gevent import monkey; monkey.patch_all()
import gevent
from time import sleep
import logging
import xmlrpclib
server = 'dev.vine.im'
logging.basicConfig(level=logging.DEBUG, format='%(asctime)-15s %(levelname)-7s - %(message)s')
xmlrpc_server = xmlrpclib.ServerProxy('http://%s:4560' % server)
def add_rosteritem(user, other_user):
gevent.spawn(_xmlrpc_command, 'add_rosteritem', {
'localuser': user,
'localserver': server,
'user': other_user,
'server': server,
'group': 'argh',
'nick': other_user,
'subs': 'both'
})
def _xmlrpc_command(command, data):
logging.debug('XMLRPC ejabberdctl: %s %s' % (command, str(data)))
fn = getattr(xmlrpc_server, command)
return fn({
'user': '_leaves',
'server': server,
'password': 'somepassword'
}, data)
if __name__ == '__main__':
for i in range(1,20):
add_rosteritem('jabberwocky', 'user%d' % i)
sleep(2)
from time import sleep
import logging
import xmlrpclib
import httplib
import io
server = 'dev.vine.im'
logging.basicConfig(level=logging.DEBUG, format='%(asctime)-15s %(levelname)-7s - %(message)s')
class FireAndForget(xmlrpclib.Transport):
mock_xml_response = u'<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>res</name><value><int>0</int></value></member></struct></value></param></params></methodResponse>'
def single_request(self, host, handler, request_body, verbose=0):
logging.warn(self._connection)
h = self.make_connection(host)
if verbose:
h.set_debuglevel(1)
try:
self.send_request(h, handler, request_body)
self.send_host(h, host)
self.send_user_agent(h)
self.send_content(h, request_body)
self.verbose = verbose
h.close() # h is closed by the standard transport anyway, so it's fine to ignore the response by doing this.
response = io.StringIO(self.mock_xml_response)
return self.parse_response(response)
except xmlrpclib.Fault:
raise
except Exception:
# All unexpected errors leave connection in a strange state, so we clear it.
self.close()
raise
xmlrpc_server = xmlrpclib.ServerProxy('http://%s:4560' % server, transport=FireAndForget())
def add_rosteritem(user, other_user):
_xmlrpc_command('add_rosteritem', {
'localuser': user,
'localserver': server,
'user': other_user,
'server': server,
'group': 'argh',
'nick': other_user,
'subs': 'both'
})
def _xmlrpc_command(command, data):
logging.debug('XMLRPC ejabberdctl: %s %s' % (command, str(data)))
fn = getattr(xmlrpc_server, command)
return fn({
'user': '_leaves',
'server': server,
'password': 'somepassword'
}, data)
if __name__ == '__main__':
for i in range(3):
add_rosteritem('jabberwocky', 'user%d' % i)
sleep(2)
@lehrblogger
Copy link
Author

I think this is the relevant bit of httplib.py that's failing (full source here):

    ...
    def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
        """Send a request to the server.

        `method' specifies an HTTP request method, e.g. 'GET'.
        `url' specifies the object being requested, e.g. '/index.html'.
        `skip_host' if True does not add automatically a 'Host:' header
        `skip_accept_encoding' if True does not add automatically an
           'Accept-Encoding:' header
        """

        # if a prior response has been completed, then forget about it.
        if self.__response and self.__response.isclosed():
            self.__response = None


        # in certain cases, we cannot issue another request on this connection.
        # this occurs when:
        #   1) we are in the process of sending a request.   (_CS_REQ_STARTED)
        #   2) a response to a previous request has signalled that it is going
        #      to close the connection upon completion.
        #   3) the headers for the previous response have not been read, thus
        #      we cannot determine whether point (2) is true.   (_CS_REQ_SENT)
        #
        # if there is no prior response, then we can request at will.
        #
        # if point (2) is true, then we will have passed the socket to the
        # response (effectively meaning, "there is no prior response"), and
        # will open a new one when a new request is made.
        #
        # Note: if a prior response exists, then we *can* start a new request.
        #       We are not allowed to begin fetching the response to this new
        #       request, however, until that prior response is complete.
        #
        if self.__state == _CS_IDLE:
            self.__state = _CS_REQ_STARTED
        else:
            raise CannotSendRequest()
        ...

Also, I think this:

    ...
    def getresponse(self, buffering=False):
        "Get the response from the server."

        # if a prior response has been completed, then forget about it.
        if self.__response and self.__response.isclosed():
            self.__response = None

        #
        # if a prior response exists, then it must be completed (otherwise, we
        # cannot read this response's header to determine the connection-close
        # behavior)
        #
        # note: if a prior response existed, but was connection-close, then the
        # socket and response were made independent of this HTTPConnection
        # object since a new request requires that we open a whole new
        # connection
        #
        # this means the prior response had one of two states:
        #   1) will_close: this connection was reset and the prior socket and
        #                  response operate independently
        #   2) persistent: the response was retained and we await its
        #                  isclosed() status to become true.
        #
        if self.__state != _CS_REQ_SENT or self.__response:
            raise ResponseNotReady()
        ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment