Ok,
This should be simple, since people do it all the time. I want to get the body of a POST request sent a twisted Agent
. This is created with a twisted FileBodyProducer
. On the server side, I get a request
object for my render_POST
method.
How do I retrieve the body?
server:
from twisted.web import server, resource
from twisted.internet import reactor
class Simple(resource.Resource):
isLeaf = True
def render_GET(self, request):
return "{0}".format(request.args.keys())
def render_POST(self, request):
return "{0}".format(request.data)
with open(request.args['filename'][0], 'rb') as fd:
fd.write(request.write())
site = server.Site(Simple())
reactor.listenTCP(8080, site)
reactor.run()
client:
from StringIO import StringIO
from twisted.internet import reactor
from twisted.web.client import Agent
from twisted.web.http_headers import Headers
from twisted.web.client import FileBodyProducer
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Protocol
from pprint import pformat
class BeginningPrinter(Protocol):
def __init__(self, finished):
self.finished = finished
self.remaining = 1024 * 10
def dataReceived(self, bytes):
if self.remaining:
display = bytes[:self.remaining]
print 'Some data received:'
print display
self.remaining -= len(display)
def connectionLost(self, reason):
print 'Finished receiving body:', reason.getErrorMessage()
self.finished.callback(None)
agent = Agent(reactor)
body = FileBodyProducer(StringIO("hello, world"))
d = agent.request(
'POST',
'http://127.0.0.1:8080/',
Headers({'User-Agent': ['Twisted Web Client Example'],
'Content-Type': ['text/x-greeting']}),
body)
def cbRequest(response):
print 'Response version:', response.version
print 'Response code:', response.code
print 'Response phrase:', response.phrase
print 'Response headers:'
print pformat(list(response.headers.getAllRawHeaders()))
finished = Deferred()
response.deliverBody(BeginningPrinter(finished))
return finished
d.addCallback(cbRequest)
def cbShutdown(ignored):
reactor.stop()
d.addBoth(cbShutdown)
reactor.run()
The only docs I can find for setting up the consumer side leave something to be desired. Primarily, how can a consumer use the write(data)
method to receive results?
Which bit am I missing to plug these two components together?
Spencer Rathbun :
All right, so it's as simple as calling request.content.read(). This, as far as I can tell, is undocumented in the API. \n\nHere's the updated code for the client:\n\nfrom twisted.internet import reactor\nfrom twisted.web.client import Agent\nfrom twisted.web.http_headers import Headers\n\nfrom twisted.web.client import FileBodyProducer\nfrom twisted.internet.defer import Deferred\nfrom twisted.internet.protocol import Protocol\nfrom pprint import pformat\n\nclass BeginningPrinter(Protocol):\n def __init__(self, finished):\n self.finished = finished\n self.remaining = 1024 * 10\n\n def dataReceived(self, bytes):\n if self.remaining:\n display = bytes[:self.remaining]\n print 'Some data received:'\n print display\n self.remaining -= len(display)\n\n def connectionLost(self, reason):\n print 'Finished receiving body:', reason.getErrorMessage()\n self.finished.callback(None)\n\nclass SaveContents(Protocol):\n def __init__(self, finished, filesize, filename):\n self.finished = finished\n self.remaining = filesize\n self.outfile = open(filename, 'wb')\n\n def dataReceived(self, bytes):\n if self.remaining:\n display = bytes[:self.remaining]\n self.outfile.write(display)\n self.remaining -= len(display)\n else:\n self.outfile.close()\n\n def connectionLost(self, reason):\n print 'Finished receiving body:', reason.getErrorMessage()\n self.outfile.close()\n self.finished.callback(None)\n\nagent = Agent(reactor)\nf = open('70935-new_barcode.pdf', 'rb')\nbody = FileBodyProducer(f)\nd = agent.request(\n 'POST',\n 'http://127.0.0.1:8080?filename=test.pdf',\n Headers({'User-Agent': ['Twisted Web Client Example'],\n 'Content-Type': ['multipart/form-data; boundary=1024'.format()]}),\n body)\n\ndef cbRequest(response):\n print 'Response version:', response.version\n print 'Response code:', response.code\n print 'Response phrase:', response.phrase\n print 'Response headers:'\n print 'Response length:', response.length\n print pformat(list(response.headers.getAllRawHeaders()))\n finished = Deferred()\n response.deliverBody(SaveContents(finished, response.length, 'test2.pdf'))\n return finished\nd.addCallback(cbRequest)\n\ndef cbShutdown(ignored):\n reactor.stop()\nd.addBoth(cbShutdown)\n\nreactor.run()\n\n\nAnd here's the server:\n\nfrom twisted.web import server, resource\nfrom twisted.internet import reactor\nimport os\n\n# multi part encoding example: http://marianoiglesias.com.ar/python/file-uploading-with-multi-part-encoding-using-twisted/\nclass Simple(resource.Resource):\n isLeaf = True\n def render_GET(self, request):\n return \"{0}\".format(request.args.keys())\n def render_POST(self, request):\n with open(request.args['filename'][0], 'wb') as fd:\n fd.write(request.content.read())\n request.setHeader('Content-Length', os.stat(request.args['filename'][0]).st_size)\n with open(request.args['filename'][0], 'rb') as fd:\n request.write(fd.read())\n request.finish()\n return server.NOT_DONE_YET\n\nsite = server.Site(Simple())\nreactor.listenTCP(8080, site)\nreactor.run()\n\n\nI can now write the file contents I receive, and read back the results. ",
2012-07-18T20:19:45
Yavor Atov :
If the content type is application/x-www-form-urlencoded or multipart/form-data,\nthe body will be parsed and put in the request.args dict.\n\nIf the body is too big, it is written in temp file, otherwise in StringIO.\n\nAfter the body is read, the method finish() is called. You can subclass Request and\npares the body in this method or do sth else.",
2012-07-31T10:04:45