Merge pull request #2678 from redapple/double-content-length-0-post

Set bodyproducer with empty content for POST
This commit is contained in:
Daniel Graña 2017-04-26 16:10:21 -03:00 committed by GitHub
commit 2d66c58e01
3 changed files with 28 additions and 10 deletions

View File

@ -279,8 +279,7 @@ class ScrapyAgent(object):
headers.removeHeader(b'Proxy-Authorization')
if request.body:
bodyproducer = _RequestBodyProducer(request.body)
else:
bodyproducer = None
elif method == b'POST':
# Setting Content-Length: 0 even for POST requests is not a
# MUST per HTTP RFCs, but it's common behavior, and some
# servers require this, otherwise returning HTTP 411 Length required
@ -289,10 +288,13 @@ class ScrapyAgent(object):
# "a Content-Length header field is normally sent in a POST
# request even when the value is 0 (indicating an empty payload body)."
#
# Twisted Agent will not add "Content-Length: 0" by itself
if method == b'POST':
headers.addRawHeader(b'Content-Length', b'0')
# Twisted < 17 will not add "Content-Length: 0" by itself;
# Twisted >= 17 fixes this;
# Using a producer with an empty-string sends `0` as Content-Length
# for all versions of Twisted.
bodyproducer = _RequestBodyProducer(b'')
else:
bodyproducer = None
start_time = time()
d = agent.request(
method, to_bytes(url, encoding='ascii'), headers, bodyproducer)

View File

@ -15,7 +15,6 @@ from twisted.internet.task import deferLater
from scrapy.utils.python import to_bytes, to_unicode
from tests import tests_datadir
def getarg(request, name, default=None, type=None):
@ -122,6 +121,7 @@ class Echo(LeafResource):
'body': to_unicode(request.content.read()),
}
return to_bytes(json.dumps(output))
render_POST = render_GET
class RedirectTo(LeafResource):
@ -174,7 +174,11 @@ class Root(Resource):
self.putChild(b"echo", Echo())
self.putChild(b"payload", PayloadResource())
self.putChild(b"xpayload", EncodingResourceWrapper(PayloadResource(), [GzipEncoderFactory()]))
self.putChild(b"files", File(os.path.join(tests_datadir, 'test_site/files/')))
try:
from tests import tests_datadir
self.putChild(b"files", File(os.path.join(tests_datadir, 'test_site/files/')))
except:
pass
self.putChild(b"redirect-to", RedirectTo())
def getChild(self, name, request):

View File

@ -29,7 +29,7 @@ from scrapy.core.downloader.handlers.http11 import HTTP11DownloadHandler
from scrapy.core.downloader.handlers.s3 import S3DownloadHandler
from scrapy.spiders import Spider
from scrapy.http import Request
from scrapy.http import Headers, Request
from scrapy.http.response.text import TextResponse
from scrapy.responsetypes import responsetypes
from scrapy.settings import Settings
@ -37,7 +37,7 @@ from scrapy.utils.test import get_crawler, skip_if_no_boto
from scrapy.utils.python import to_bytes
from scrapy.exceptions import NotConfigured
from tests.mockserver import MockServer, ssl_context_factory
from tests.mockserver import MockServer, ssl_context_factory, Echo
from tests.spiders import SingleRequestSpider
class DummyDH(object):
@ -202,6 +202,7 @@ class HttpTestCase(unittest.TestCase):
r.putChild(b"broken-chunked", BrokenChunkedResource())
r.putChild(b"contentlength", ContentLengthHeaderResource())
r.putChild(b"nocontenttype", EmptyContentTypeHeaderResource())
r.putChild(b"echo", Echo())
self.site = server.Site(r, timeout=None)
self.wrapper = WrappingFactory(self.site)
self.host = 'localhost'
@ -310,6 +311,17 @@ class HttpTestCase(unittest.TestCase):
request = Request(self.getURL('contentlength'), method='POST', headers={'Host': 'example.com'})
return self.download_request(request, Spider('foo')).addCallback(_test)
def test_content_length_zero_bodyless_post_only_one(self):
def _test(response):
import json
headers = Headers(json.loads(response.text)['headers'])
contentlengths = headers.getlist('Content-Length')
self.assertEquals(len(contentlengths), 1)
self.assertEquals(contentlengths, [b"0"])
request = Request(self.getURL('echo'), method='POST')
return self.download_request(request, Spider('foo')).addCallback(_test)
def test_payload(self):
body = b'1'*100 # PayloadResource requires body length to be 100
request = Request(self.getURL('payload'), method='POST', body=body)