1 """Tests for managing HTTP issues (malformed requests, etc)."""
6 from cherrypy
._cpcompat
import HTTPConnection
, HTTPSConnection
, ntob
9 def encode_multipart_formdata(files
):
10 """Return (content_type, body) ready for httplib.HTTP instance.
12 files: a sequence of (name, filename, value) tuples for multipart uploads.
14 BOUNDARY
= '________ThIs_Is_tHe_bouNdaRY_$'
16 for key
, filename
, value
in files
:
17 L
.append('--' + BOUNDARY
)
18 L
.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
20 ct
= mimetypes
.guess_type(filename
)[0] or 'application/octet-stream'
21 L
.append('Content-Type: %s' % ct
)
24 L
.append('--' + BOUNDARY
+ '--')
27 content_type
= 'multipart/form-data; boundary=%s' % BOUNDARY
28 return content_type
, body
33 from cherrypy
.test
import helper
35 class HTTPTests(helper
.CPWebCase
):
39 def index(self
, *args
, **kwargs
):
43 def no_body(self
, *args
, **kwargs
):
45 no_body
.exposed
= True
46 no_body
._cp
_config
= {'request.process_request_body': False}
48 def post_multipart(self
, file):
49 """Return a summary ("a * 65536\nb * 65536") of the uploaded file."""
50 contents
= file.file.read()
59 summary
.append("%s * %d" % (curchar
, count
))
63 summary
.append("%s * %d" % (curchar
, count
))
64 return ", ".join(summary
)
65 post_multipart
.exposed
= True
67 cherrypy
.tree
.mount(Root())
68 cherrypy
.config
.update({'server.max_request_body_size': 30000000})
69 setup_server
= staticmethod(setup_server
)
71 def test_no_content_length(self
):
72 # "The presence of a message-body in a request is signaled by the
73 # inclusion of a Content-Length or Transfer-Encoding header field in
74 # the request's message-headers."
76 # Send a message with neither header and no body. Even though
77 # the request is of method POST, this should be OK because we set
78 # request.process_request_body to False for our handler.
79 if self
.scheme
== "https":
80 c
= HTTPSConnection('%s:%s' % (self
.interface(), self
.PORT
))
82 c
= HTTPConnection('%s:%s' % (self
.interface(), self
.PORT
))
83 c
.request("POST", "/no_body")
84 response
= c
.getresponse()
85 self
.body
= response
.fp
.read()
86 self
.status
= str(response
.status
)
87 self
.assertStatus(200)
88 self
.assertBody(ntob('Hello world!'))
90 # Now send a message that has no Content-Length, but does send a body.
91 # Verify that CP times out the socket and responds
92 # with 411 Length Required.
93 if self
.scheme
== "https":
94 c
= HTTPSConnection('%s:%s' % (self
.interface(), self
.PORT
))
96 c
= HTTPConnection('%s:%s' % (self
.interface(), self
.PORT
))
97 c
.request("POST", "/")
98 response
= c
.getresponse()
99 self
.body
= response
.fp
.read()
100 self
.status
= str(response
.status
)
101 self
.assertStatus(411)
103 def test_post_multipart(self
):
104 alphabet
= "abcdefghijklmnopqrstuvwxyz"
105 # generate file contents for a large post
106 contents
= "".join([c
* 65536 for c
in alphabet
])
108 # encode as multipart form data
109 files
=[('file', 'file.txt', contents
)]
110 content_type
, body
= encode_multipart_formdata(files
)
111 body
= body
.encode('Latin-1')
114 if self
.scheme
== 'https':
115 c
= HTTPSConnection('%s:%s' % (self
.interface(), self
.PORT
))
117 c
= HTTPConnection('%s:%s' % (self
.interface(), self
.PORT
))
118 c
.putrequest('POST', '/post_multipart')
119 c
.putheader('Content-Type', content_type
)
120 c
.putheader('Content-Length', str(len(body
)))
124 response
= c
.getresponse()
125 self
.body
= response
.fp
.read()
126 self
.status
= str(response
.status
)
127 self
.assertStatus(200)
128 self
.assertBody(", ".join(["%s * 65536" % c
for c
in alphabet
]))
130 def test_malformed_request_line(self
):
131 if getattr(cherrypy
.server
, "using_apache", False):
132 return self
.skip("skipped due to known Apache differences...")
134 # Test missing version in Request-Line
135 if self
.scheme
== 'https':
136 c
= HTTPSConnection('%s:%s' % (self
.interface(), self
.PORT
))
138 c
= HTTPConnection('%s:%s' % (self
.interface(), self
.PORT
))
139 c
._output
(ntob('GET /'))
141 if hasattr(c
, 'strict'):
142 response
= c
.response_class(c
.sock
, strict
=c
.strict
, method
='GET')
144 # Python 3.2 removed the 'strict' feature, saying:
145 # "http.client now always assumes HTTP/1.x compliant servers."
146 response
= c
.response_class(c
.sock
, method
='GET')
148 self
.assertEqual(response
.status
, 400)
149 self
.assertEqual(response
.fp
.read(22), ntob("Malformed Request-Line"))
152 def test_malformed_header(self
):
153 if self
.scheme
== 'https':
154 c
= HTTPSConnection('%s:%s' % (self
.interface(), self
.PORT
))
156 c
= HTTPConnection('%s:%s' % (self
.interface(), self
.PORT
))
157 c
.putrequest('GET', '/')
158 c
.putheader('Content-Type', 'text/plain')
159 # See http://www.cherrypy.org/ticket/941
160 c
._output
(ntob('Re, 1.2.3.4#015#012'))
163 response
= c
.getresponse()
164 self
.status
= str(response
.status
)
165 self
.assertStatus(400)
166 self
.body
= response
.fp
.read(20)
167 self
.assertBody("Illegal header line.")