mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
check if Request Line is too large.
You can now pass the parameter --limit-request-line or set the limit_request_line in your configuration file to set the max size of the request line in bytes. This parameter is used to limit the allowed size of a client's HTTP request-line. Since the request-line consists of the HTTP method, URI, and protocol version, this directive places a restriction on the length of a request-URI allowed for a request on the server. A server needs this value to be large enough to hold any of its resource names, including any information that might be passed in the query part of a GET request. By default this value is 4094 and can't be larger than 8190. This parameter can be used to prevent any DDOS attack.
This commit is contained in:
parent
6766c14793
commit
b7b0979ad9
@ -425,6 +425,29 @@ class Keepalive(Setting):
|
||||
Generally set in the 1-5 seconds range.
|
||||
"""
|
||||
|
||||
class LimitRequestLine(Setting):
|
||||
name = "limit_request_line"
|
||||
section = "Security"
|
||||
cli = ["--limit-request-line"]
|
||||
meta = "INT"
|
||||
validator = validate_pos_int
|
||||
type = "int"
|
||||
default = 4094
|
||||
desc = """\
|
||||
The maximum size of HTTP request line in bytes.
|
||||
|
||||
This parameter is used to limit the allowed size of a client's
|
||||
HTTP request-line. Since the request-line consists of the HTTP
|
||||
method, URI, and protocol version, this directive places a
|
||||
restriction on the length of a request-URI allowed for a request
|
||||
on the server. A server needs this value to be large enough to
|
||||
hold any of its resource names, including any information that
|
||||
might be passed in the query part of a GET request. By default
|
||||
this value is 4094 and can't be larger than 8190.
|
||||
|
||||
This parameter can be used to prevent any DDOS attack.
|
||||
"""
|
||||
|
||||
class Debug(Setting):
|
||||
name = "debug"
|
||||
section = "Debugging"
|
||||
|
||||
@ -61,3 +61,12 @@ class ChunkMissingTerminator(ParseException):
|
||||
|
||||
def __str__(self):
|
||||
return "Invalid chunk terminator is not '\\r\\n': %r" % self.term
|
||||
|
||||
|
||||
class LimitRequestLine(ParseException):
|
||||
def __init__(self, size, max_size):
|
||||
self.size = size
|
||||
self.max_size = max_size
|
||||
|
||||
def __str__(self):
|
||||
return "Request Line is too large (%s > %s)" % (self.size, self.max_size)
|
||||
|
||||
@ -13,7 +13,9 @@ except ImportError:
|
||||
|
||||
from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body
|
||||
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, NoMoreData, \
|
||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion
|
||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, LimitRequestLine
|
||||
|
||||
MAX_REQUEST_LINE = 8190
|
||||
|
||||
class Message(object):
|
||||
def __init__(self, cfg, unreader):
|
||||
@ -107,6 +109,12 @@ class Request(Message):
|
||||
self.query = None
|
||||
self.fragment = None
|
||||
|
||||
# get max request line size
|
||||
self.limit_request_line = max(cfg.limit_request_line,
|
||||
MAX_REQUEST_LINE)
|
||||
if self.limit_request_line <= 0:
|
||||
self.limit_request_line = MAX_REQUEST_LINE
|
||||
|
||||
super(Request, self).__init__(cfg, unreader)
|
||||
|
||||
|
||||
@ -123,32 +131,39 @@ class Request(Message):
|
||||
self.get_data(unreader, buf, stop=True)
|
||||
|
||||
# Request line
|
||||
idx = buf.getvalue().find("\r\n")
|
||||
while idx < 0:
|
||||
data = buf.getvalue()
|
||||
while True:
|
||||
idx = data.find("\r\n")
|
||||
if idx >= 0:
|
||||
break
|
||||
self.get_data(unreader, buf)
|
||||
idx = buf.getvalue().find("\r\n")
|
||||
self.parse_request_line(buf.getvalue()[:idx])
|
||||
rest = buf.getvalue()[idx+2:] # Skip \r\n
|
||||
buf = StringIO()
|
||||
buf.write(rest)
|
||||
data = buf.getvalue()
|
||||
|
||||
if len(data) - 2 > self.limit_request_line:
|
||||
raise LimitRequestLine(len(data), self.cfg.limit_request_line)
|
||||
|
||||
self.parse_request_line(data[:idx])
|
||||
buf = StringIO()
|
||||
buf.write(data[idx+2:]) # Skip \r\n
|
||||
|
||||
# Headers
|
||||
idx = buf.getvalue().find("\r\n\r\n")
|
||||
data = buf.getvalue()
|
||||
idx = data.find("\r\n\r\n")
|
||||
|
||||
done = buf.getvalue()[:2] == "\r\n"
|
||||
done = data[:2] == "\r\n"
|
||||
while idx < 0 and not done:
|
||||
self.get_data(unreader, buf)
|
||||
idx = buf.getvalue().find("\r\n\r\n")
|
||||
done = buf.getvalue()[:2] == "\r\n"
|
||||
data = buf.getvalue()
|
||||
idx = data.find("\r\n\r\n")
|
||||
done = data[:2] == "\r\n"
|
||||
|
||||
if done:
|
||||
self.unreader.unread(buf.getvalue()[2:])
|
||||
self.unreader.unread(data[2:])
|
||||
return ""
|
||||
|
||||
self.headers = self.parse_headers(buf.getvalue()[:idx])
|
||||
self.headers = self.parse_headers(data[:idx])
|
||||
|
||||
ret = buf.getvalue()[idx+4:]
|
||||
ret = data[idx+4:]
|
||||
buf = StringIO()
|
||||
return ret
|
||||
|
||||
|
||||
@ -15,7 +15,8 @@ from gunicorn import util
|
||||
from gunicorn.workers.workertmp import WorkerTmp
|
||||
|
||||
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \
|
||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion
|
||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
|
||||
LimitRequestLine
|
||||
|
||||
|
||||
class Worker(object):
|
||||
@ -147,6 +148,8 @@ class Worker(object):
|
||||
mesg = "<p>Invalid HTTP Version '%s'</p>" % str(exc)
|
||||
elif isinstance(exc, (InvalidHeaderName, InvalidHeader,)):
|
||||
mesg = "<p>Invalid Header '%s'</p>" % str(exc)
|
||||
elif isinstance(exc, LimitRequestLine):
|
||||
msg = str(exc)
|
||||
|
||||
if self.debug:
|
||||
tb = traceback.format_exc()
|
||||
|
||||
@ -1,151 +0,0 @@
|
||||
# -*- coding: utf-8 -
|
||||
#
|
||||
# This file is part of gunicorn released under the MIT license.
|
||||
# See the NOTICE for more information.
|
||||
|
||||
import os
|
||||
import t
|
||||
|
||||
# from gunicorn.http import tee
|
||||
#
|
||||
# @t.http_request("001.http")
|
||||
# def test_001(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['CONTENT_LENGTH'], '14')
|
||||
# t.eq(e['wsgi.version'], (1,0))
|
||||
# t.eq(e['REQUEST_METHOD'], 'PUT')
|
||||
# t.eq(e['PATH_INFO'], '/stuff/here')
|
||||
# t.eq(e['CONTENT_TYPE'], 'application/json')
|
||||
# t.eq(e['QUERY_STRING'], 'foo=bar')
|
||||
#
|
||||
# t.eq(isinstance(e['wsgi.input'], tee.TeeInput), True)
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '{"nom": "nom"}')
|
||||
#
|
||||
# @t.http_request("002.http")
|
||||
# def test_002(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/test")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# t.eq(e['HTTP_ACCEPT'], "*/*")
|
||||
# t.eq(e['HTTP_HOST'], "0.0.0.0=5000")
|
||||
# t.eq(e['HTTP_USER_AGENT'], "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1")
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
# @t.http_request("003.http")
|
||||
# def test_003(req):
|
||||
# e = req.read()
|
||||
#
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/favicon.ico")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# t.eq(e['HTTP_ACCEPT'], "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
|
||||
# t.eq(e['HTTP_KEEP_ALIVE'], "300")
|
||||
#
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
# @t.http_request("004.http")
|
||||
# def test_004(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/dumbfuck")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
#
|
||||
# @t.http_request("005.http")
|
||||
# def test_005(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/forums/1/topics/2375")
|
||||
# t.eq(e['QUERY_STRING'], "page=1")
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
#
|
||||
# @t.http_request("006.http")
|
||||
# def test_006(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/get_no_headers_no_body/world")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
#
|
||||
# @t.http_request("007.http")
|
||||
# def test_007(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/get_one_header_no_body")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# t.eq(e['HTTP_ACCEPT'], "*/*")
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, '')
|
||||
#
|
||||
#
|
||||
# @t.http_request("008.http")
|
||||
# def test_008(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/get_funky_content_length_body_hello")
|
||||
# t.eq(e['QUERY_STRING'], "")
|
||||
# t.eq(e['CONTENT_LENGTH'], '5')
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, "HELLO")
|
||||
#
|
||||
#
|
||||
# @t.http_request("009.http")
|
||||
# def test_009(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'POST')
|
||||
# t.eq(e['PATH_INFO'], "/post_identity_body_world")
|
||||
# t.eq(e['QUERY_STRING'], "q=search")
|
||||
# t.eq(e['CONTENT_LENGTH'], '5')
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, "World")
|
||||
#
|
||||
#
|
||||
# @t.http_request("010.http")
|
||||
# def test_010(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'POST')
|
||||
# t.eq(e['PATH_INFO'], "/post_chunked_all_your_base")
|
||||
# t.eq(e['HTTP_TRANSFER_ENCODING'], "chunked")
|
||||
# t.eq(e['CONTENT_LENGTH'], '30')
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, "all your base are belong to us")
|
||||
#
|
||||
#
|
||||
# @t.http_request("011.http")
|
||||
# def test_011(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'POST')
|
||||
# t.eq(e['PATH_INFO'], "/two_chunks_mult_zero_end")
|
||||
# t.eq(e['HTTP_TRANSFER_ENCODING'], "chunked")
|
||||
# t.eq(e['CONTENT_LENGTH'], '11')
|
||||
# body = e['wsgi.input'].read()
|
||||
# t.eq(body, "hello world")
|
||||
#
|
||||
# @t.http_request("017.http")
|
||||
# def test_017(req):
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['PATH_INFO'], "/stuff/here")
|
||||
# t.eq(e["HTTP_IF_MATCH"], "bazinga!, large-sound")
|
||||
# t.eq(e["wsgi.input"].read(), "")
|
||||
#
|
||||
# @t.http_request("017.http")
|
||||
# def test_018(req):
|
||||
# os.environ['SCRIPT_NAME'] = "/stuff"
|
||||
# e = req.read()
|
||||
# t.eq(e['REQUEST_METHOD'], 'GET')
|
||||
# t.eq(e['SCRIPT_NAME'], "/stuff")
|
||||
# t.eq(e['PATH_INFO'], "/here")
|
||||
# t.eq(e["wsgi.input"].read(), "")
|
||||
#
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
from gunicorn.http.errors import InvalidRequestLine
|
||||
request = InvalidRequestLine
|
||||
request = InvalidRequestLine
|
||||
|
||||
2
tests/requests/invalid/006.http
Normal file
2
tests/requests/invalid/006.http
Normal file
@ -0,0 +1,2 @@
|
||||
PUT /q=08aP8931Ltyl9nqyJvjMaRCOgDV3uONtAdHABjoZUG6KAP6h3Vh97O3GJjjovXYgNdrhxc7TriXoAmeehZMJx88EyhcPXO0f09Nvd128SZnxZ2r5jFDELkn26reKRysODSLBZLfjU3vxLzLXKWeFOFJKcZYRH9V7hC98DDS4ZsS7weUksBuK6m86aLNHHHB0Xbyxv1TiDbOWYIzKxV0eZKyk0CaDLDiR0CRuMOf4rwBeuHoMrumzafrFI5iL72ANQZmOvKdk1qQeXkRqEG11YU0kF7f1hSlmgiIgg5maWiBsA9sAg36IIXZMWwJF63zpMgAyjTT8l4pQhSBfhY2xbGAWmLGpyd1rlBm0O5LCoKpnQuTACm2azi0x6a1Qbry9flQBO4jHge2dXiD1si6Gh5q8fZu8ZQ7LLWii2u4rGB7E4XlhnClrCHg5vJmjYf2AItYPA0ogsiIdEEQGpzMJPqrp8Icn5kAAimWF1aCYaDjcdSgWI48PnoxlzIHX50EPFcPOSLecjkstD9z66H554sUXfWn3Mk9lnOUlse6nx0u1YClFK4UFXp98ru9eBBr7pkAsfZ34yPskayGyXPPyzWyBfVd28UuvdEG47SMdyqEpX0rFdk67fAYij0PWMK79mDmGAS37O821o18XUbu0GQjsqAGVMN9LDIAliD9QqtlwdEnplKkUyyZ7GAFJCFffgzppU9CjA2FbPX6ZjTOi4sPoYEyhyeQKVqAe9keYeDpU2qDwq83XEDQUKvP0w48GyavSmdBcrMXjUsu0PfdYpSaKwarrUB3i93HgoQB3ZJIR4lW6iPRTmm28OEKq2MIJGAoTXxCZYM5UacRldlqQOj6JkYz6y7ppWOjJ9yiCUEenuvfcItgmw9HIgGA59JxO8NDLEZLSONfuIgiV7wjsJnxuTOlU4vkjV7fTuOeU91xez7UKhaTqqEW3XBUSLjhKi3IkZg7ukrGZTWPhijFv2EZwEWDAyLlHvZB4X738zGJUlEX1k52EHwrKVKdLfePcaOjAGKsongHBFYxYC8vBBLuKm9RWexKCT14M25pCGloJXZ4OpBRfDQA2kobLUcEXEpzqRBPGN2JdNSBOFlUtUxWKnnPBM6r9S356l3k1o9zTIPeoIitWRjASs4A0iwYc8p5vv5Kt8KtsmW7Xv8dlU8HbZHsy3LI7O9BpUH8cJubqdEhooKABkx71pdcsZGhZb6epyTiPyvOhdJ7tNtFy3KQOameqTgGyd53Z42eZ0AjaOEvnzermi2E0xo3MMHFhB74TFtNAI3ppxxyqknc1mzUqZ49Wi8YPBg9ids6IgZvddBQYvwEozkmyGAkatQtt9TD4LjU3TyyUlhNG21q7CzEEl8NNsVrV6QyHsfw7E5w7XcoT7OQkBYoZwHIAjfekehnpc2llRtRY5m43fPVasmsVazOR36DRSLZJPHAqUDO0LInu9mgP57Mnz9CgylEmdE2aaYs426rnTFR3G3CfjLofHfjaLOkAegr4W3jx6MNMMOMZw2u46YTCnlfbBK6ZA1UYeAH1DIQJykcSQESinC8HpYIJt9A8g7UT0awzRP1F9nHa3wDnaAHndQYKMrjzlWo8ejQ0XHWgHhqnWHgW4h9sOnJckH00CYK1fHUKASJ3D8kOKax6uplexfz6BCvAoL9zm5TjeB1yxrpLp9NjjTWSKG2HOZhPkGpdEqU4mjnN2AkUVACPGos5YLBmTnSrdOEGZJDlAvJOUt800Mu3BYc1MiDIB6LMSSV5RsIUDFOzNletGQoq4G3yHZmx78uEse5vUTPFF3KT8LCrssqdIU9H97Npgf6N5j8arQ7ykLzN459jJaUzpGIo6uowPnUSatDf9GAvAmWNvsVTz6bYiAV71C7QF0C7UolYIQY6DHJEHejgX2YMEovWNLPL50eeC51h4DdPNv5G4ZdNtQTRVybYBZMpetGDiFmXN0JKa1sKHOSZxdrhKjxDIhrYVyCcRUMQ0sjGGHFuOcRszr6E5igEMtsebHQ3KYiGd5B27LikpUHhk61rgZlulHdMoS6YgQs6SV6UMVNku6sCw529xhUciDwRMhsbAjDlahYbrGa3NryxyV5LrXONGGKCchCqv7vDMdAtPrVr8M2vL5MySQAC3g90iugGQcLH3hCf9f1Kn5X0hM4KZTfwOPJhlfJsMRNhssiDoXaycUvOUS58266yPDlitPIAzO03XClm4EDPXGIwcwiFr7FcDo3tQIMZVy87i48Zb80s3zAYRiBIS0vO3RKGx3OGN5zid2B7MfnfLzvpvgZoirHhAqXffnym5abpZNzGuo5GowTRA2Ptk4Ve2JFoHACWpD6HiGnRZ9QVOmPICoQrSUQw45Jlk9onKJz5Erhnx0943Uno6tMJ5jbrWBNiIO7i04xzRBgujeiAJvuQkVDX2QLKRxZ7s6rhdfOaq6R6uL108gEzzlXOLqTTJXgM63rcUWNbE7wsIXcCFSF59LLJ7G5Qea33suxdDX6DcK4a0VMZoxmWPtCi1dAT9ggJqc2Sh7mkAqizaB16RXZvSydchpdVj6s4qn4ivr0HKHdAstX0XZ0FFU6lOiNmU3vasMg2uaVG8tyuG8N8VsuXIOQs7xtFxDhilYb8MQ9vES9pWfWPSXFlJAq4XKPY8a0JOIx57EQuWHo3uWgRTIRThvZP9YYzSnjGIHwjS8JeppICHofADXZhJ0uDQaQs7MiXEALpGmT3W6w0G3tBdZcuTDkWx1HsT5jd9jQeJpgD2VxdKh8U4Q3vANTAuwBXLJ2P0stS8Q72JWgNPwKYTY9cPoaGZlUFGgVsq8CdEFH9yW0c27G5s5sfHsyep6t4VxIHHMOX2GmMRyGxDI33am1J7ZmJ1NyXiwkHxtPH5QBpU2PMu2Guf3xIxlk3snMkMAsGO0vYfqO9tdIgdxMYO3HZTYv99OXaHcNQ5u0pRZZyVrNOIPurkEOdJy0nowPemIgUuHWh8vQCuDZav1m35AOl6ftSFuChSm5KstEWnC7q8mJ0juJEBkCRmQphP3V1pqiDjz6YA90qEe7MA3nzT0nHG8A1hWlqcPVPNz4qWNF6Fq1ub4075aXO0H7Krb6rhWGb3ZRPjpb4BKN8jGFQrBUMZprtjAJ67BnfmYgE0mmGLV2QP10gYS1T06kBRyrtp7he6wsPiBPJ7wxPLHNUN2SGQHBTSKagndM99fuaga5Sw9OT8Fzdo7xUJXfhJ97gUnNDrknal0B00NMNvajZeQQTJyBsVSwBZtZ45ZCcq1idc7GWC0MITSk58cIVkSPXbrERUaygyY13dPeEVzjVi9aVJwUF6eJu1s8u3FCJqp2GoWIItwvZO69asX75fekFkmFpNavxM0X0dZC01TTPpV6E6PJoIfW8C06CKNHV7Gk2mkTWGSwUG4xD2L3G3XarodHDcmumFJX9Xviv0rvm38SCtin6OpjH8MHYDrj1OxTJbC2VclJxv73z2BDBquosKOik0fmgbPZN0FUTmjBEwHTvqd5QHTwb3nOpEz3X6YCF0lrcrQc0uhyr7gBGBs86nUBWFRp1LKjIRVTVXDipajqNDTQGNZtzvR9MUf1yJJV07inbrlPOENd7rHpKCrJtoZXOkDqInaIqoMCG3DVd353BGmZNJEKOa3DnL7fb9zwuHlvHAfCco7ZS4wAV87trWkp6skXux9v5WhkumbUyGq4ia6DM1PuqqnFfBTAWDzJsnggAJrzr8O7JbDtaXwcW9sqaOb0S6NvnUDZqiNdDQPMDOKvXRJJJQdf1FSrPCCSPEEWO1SeVwictj7rTbpWGRoukwhgJALys95pGGOQxCPzRGrtVFnGcsLN1CwI3wLbmDnNKUv3KpOLEOPRxQXeXuJRIiYCFum44c0wNr731DvHn3YEJMH4iwFONl1rolEL4w6KFUOCq7ekrE5iyUt1V32PNtuUshXRjOYjBval29JMH5GoqZlGhCczzHMA61cmuzqdFwiPCB9yzqvJTg8TqMNvwKJztFIQK4mc5Ev5rRVSozD796AVRKT8rZF39IA1kmCLdXqz7CCC8x4QjjDpxjKCXP5HkWf9mp2FNBjE3pAeaEc6Vk2ENLlW8WVCe HTTP/1.0\r\n
|
||||
\r\n
|
||||
2
tests/requests/invalid/006.py
Normal file
2
tests/requests/invalid/006.py
Normal file
@ -0,0 +1,2 @@
|
||||
from gunicorn.http.errors import LimitRequestLine
|
||||
request = LimitRequestLine
|
||||
@ -3,4 +3,4 @@ Server: http://127.0.0.1:5984\r\n
|
||||
Content-Type: application/json\r\n
|
||||
Content-Length: 14\r\n
|
||||
\r\n
|
||||
{"nom": "nom"}
|
||||
{"nom": "nom"}
|
||||
|
||||
@ -12,6 +12,7 @@ import os
|
||||
import random
|
||||
import urlparse
|
||||
|
||||
from gunicorn.config import Config
|
||||
from gunicorn.http.errors import ParseException
|
||||
from gunicorn.http.parser import RequestParser
|
||||
|
||||
@ -78,27 +79,27 @@ class request(object):
|
||||
def send_bytes(self):
|
||||
for d in self.data:
|
||||
yield d
|
||||
|
||||
|
||||
def send_random(self):
|
||||
maxs = len(self.data) / 10
|
||||
read = 0
|
||||
while read < len(self.data):
|
||||
chunk = random.randint(1, maxs)
|
||||
yield self.data[read:read+chunk]
|
||||
read += chunk
|
||||
read += chunk
|
||||
|
||||
# These functions define the sizes that the
|
||||
# read functions will read with.
|
||||
|
||||
def size_all(self):
|
||||
return -1
|
||||
|
||||
|
||||
def size_bytes(self):
|
||||
return 1
|
||||
|
||||
|
||||
def size_small_random(self):
|
||||
return random.randint(0, 4)
|
||||
|
||||
|
||||
def size_random(self):
|
||||
return random.randint(1, 4096)
|
||||
|
||||
@ -130,7 +131,7 @@ class request(object):
|
||||
if len(body):
|
||||
raise AssertionError("Failed to read entire body: %r" % body)
|
||||
elif len(data):
|
||||
raise AssertionError("Read beyond expected body: %r" % data)
|
||||
raise AssertionError("Read beyond expected body: %r" % data)
|
||||
data = req.body.read(sizes())
|
||||
if data:
|
||||
raise AssertionError("Read after body finished: %r" % data)
|
||||
@ -152,7 +153,7 @@ class request(object):
|
||||
if len(body):
|
||||
raise AssertionError("Failed to read entire body: %r" % body)
|
||||
elif len(data):
|
||||
raise AssertionError("Read beyond expected body: %r" % data)
|
||||
raise AssertionError("Read beyond expected body: %r" % data)
|
||||
data = req.body.readline(sizes())
|
||||
if data:
|
||||
raise AssertionError("Read data after body finished: %r" % data)
|
||||
@ -174,7 +175,7 @@ class request(object):
|
||||
data = req.body.readlines(sizes())
|
||||
if data:
|
||||
raise AssertionError("Read data after body finished: %r" % data)
|
||||
|
||||
|
||||
def match_iter(self, req, body, sizes):
|
||||
"""\
|
||||
This skips sizes because there's its not part of the iter api.
|
||||
@ -196,7 +197,7 @@ class request(object):
|
||||
|
||||
# Construct a series of test cases from the permutations of
|
||||
# send, size, and match functions.
|
||||
|
||||
|
||||
def gen_cases(self):
|
||||
def get_funs(p):
|
||||
return [v for k, v in inspect.getmembers(self) if k.startswith(p)]
|
||||
@ -224,7 +225,7 @@ class request(object):
|
||||
|
||||
def check(self, sender, sizer, matcher):
|
||||
cases = self.expect[:]
|
||||
p = RequestParser(sender())
|
||||
p = RequestParser(Config(), sender())
|
||||
for req in p:
|
||||
self.same(req, sizer, matcher, cases.pop(0))
|
||||
t.eq(len(cases), 0)
|
||||
@ -232,9 +233,6 @@ class request(object):
|
||||
def same(self, req, sizer, matcher, exp):
|
||||
t.eq(req.method, exp["method"])
|
||||
t.eq(req.uri, exp["uri"]["raw"])
|
||||
t.eq(req.scheme, exp["uri"]["scheme"])
|
||||
t.eq(req.host, exp["uri"]["host"])
|
||||
t.eq(req.port, exp["uri"]["port"])
|
||||
t.eq(req.path, exp["uri"]["path"])
|
||||
t.eq(req.query, exp["uri"]["query"])
|
||||
t.eq(req.fragment, exp["uri"]["fragment"])
|
||||
@ -263,58 +261,18 @@ class badrequest(object):
|
||||
while read < len(self.data):
|
||||
chunk = random.randint(1, maxs)
|
||||
yield self.data[read:read+chunk]
|
||||
read += chunk
|
||||
|
||||
def size(self):
|
||||
return random.randint(0, 4)
|
||||
|
||||
def match(self, req, body):
|
||||
data = req.body.read(self.size())
|
||||
count = 1000
|
||||
while len(body):
|
||||
if body[:len(data)] != data:
|
||||
raise AssertionError("Invalid body data read: %r != %r" % (
|
||||
data, body[:len(data)]))
|
||||
body = body[len(data):]
|
||||
data = req.body.read(self.size())
|
||||
if not data:
|
||||
count -= 1
|
||||
if count <= 0:
|
||||
raise AssertionError("Unexpected apparent EOF")
|
||||
|
||||
if len(body):
|
||||
raise AssertionError("Failed to read entire body: %r" % body)
|
||||
elif len(data):
|
||||
raise AssertionError("Read beyond expected body: %r" % data)
|
||||
data = req.body.read(sizes())
|
||||
if data:
|
||||
raise AssertionError("Read after body finished: %r" % data)
|
||||
|
||||
def same(self, req, sizer, matcher, exp):
|
||||
t.eq(req.method, exp["method"])
|
||||
t.eq(req.uri, exp["uri"]["raw"])
|
||||
t.eq(req.scheme, exp["uri"]["scheme"])
|
||||
t.eq(req.host, exp["uri"]["host"])
|
||||
t.eq(req.port, exp["uri"]["port"])
|
||||
t.eq(req.path, exp["uri"]["path"])
|
||||
t.eq(req.query, exp["uri"]["query"])
|
||||
t.eq(req.fragment, exp["uri"]["fragment"])
|
||||
t.eq(req.version, exp["version"])
|
||||
t.eq(req.headers, exp["headers"])
|
||||
self.match(req, exp["body"])
|
||||
t.eq(req.trailers, exp.get("trailers", []))
|
||||
read += chunk
|
||||
|
||||
def check(self):
|
||||
cases = self.expect[:]
|
||||
p = RequestParser(self.send())
|
||||
p = RequestParser(Config(), self.send())
|
||||
try:
|
||||
for req in p:
|
||||
self.same(req, cases.pop(0))
|
||||
[req for req in p]
|
||||
except Exception, inst:
|
||||
exp = cases.pop(0)
|
||||
if not issubclass(exp, Exception):
|
||||
raise TypeError("Test case is not an exception calss: %s" % exp)
|
||||
if not isinstance(inst, exp):
|
||||
raise TypeError("Invalid error result: %s: %s" % (exp, inst))
|
||||
t.eq(len(cases), 0)
|
||||
t.istype(inst, exp)
|
||||
return
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user