Last active
December 10, 2015 17:58
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
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
On one of my VM's (with Python 2.6.5) this prints:
But on another, newer VM (with Python 2.7.1) this fails with:
It works, however, on both VMs if I comment out line the last line of my script and don't connect the XMPP component. It also works if I only make a single add_rosteritem call, but in my application I need to be able to make several at once.
When running code from my actual application on the new VM, I see errors like this as well: