def read_headers(cls, fp): headers = httputil.HeaderMap() while True: line = fp.readline() if not line: # No more data--illegal end of headers raise EOFError("Illegal end of headers.") if line == ntob('\n'): # Normal end of headers break # if not line.endswith(ntob('\r\n')): # raise ValueError("MIME requires CRLF terminators: %r" % line) if line[0] in ntob(' \t'): # It's a continuation line. v = line.strip().decode('ISO-8859-1') else: k, v = line.split(ntob(":"), 1) k = k.strip().decode('ISO-8859-1') v = v.strip().decode('ISO-8859-1') existing = headers.get(k) if existing: v = ", ".join((existing, v)) headers[k] = v return headers
def test_empty_string_app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [ ntob('Hello'), ntob(''), ntob(' '), ntob(''), ntob('world') ]
def test_encoded_headers(self): # First, make sure the innards work like expected. self.assertEqual(httputil.decode_TEXT(ntou("=?utf-8?q?f=C3=BCr?=")), ntou("f\xfcr")) if cherrypy.server.protocol_version == "HTTP/1.1": # Test RFC-2047-encoded request and response header values u = ntou('\u212bngstr\xf6m', 'escape') c = ntou("=E2=84=ABngstr=C3=B6m") self.getPage("/headers/ifmatch", [('If-Match', ntou('=?utf-8?q?%s?=') % c)]) # The body should be utf-8 encoded. self.assertBody(ntob("\xe2\x84\xabngstr\xc3\xb6m")) # But the Etag header should be RFC-2047 encoded (binary) self.assertHeader("ETag", ntou('=?utf-8?b?4oSrbmdzdHLDtm0=?=')) # Test a *LONG* RFC-2047-encoded request and response header value self.getPage("/headers/ifmatch", [('If-Match', ntou('=?utf-8?q?%s?=') % (c * 10))]) self.assertBody(ntob("\xe2\x84\xabngstr\xc3\xb6m") * 10) # Note: this is different output for Python3, but it decodes fine. etag = self.assertHeader("ETag", '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt' '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt' '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt' '4oSrbmdzdHLDtm0=?=') self.assertEqual(httputil.decode_TEXT(etag), u * 10)
def test_decode_tool(self): # An extra charset should be tried first, and succeed if it matches. # Here, we add utf-16 as a charset and pass a utf-16 body. body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00") self.getPage('/decode/extra_charset', method='POST', headers=[("Content-Type", "application/x-www-form-urlencoded"), ("Content-Length", str(len(body))), ], body=body), self.assertBody(ntob("q: \xc2\xa3")) # An extra charset should be tried first, and continue to other default # charsets if it doesn't match. # Here, we add utf-16 as a charset but still pass a utf-8 body. body = ntob("q=\xc2\xa3") self.getPage('/decode/extra_charset', method='POST', headers=[("Content-Type", "application/x-www-form-urlencoded"), ("Content-Length", str(len(body))), ], body=body), self.assertBody(ntob("q: \xc2\xa3")) # An extra charset should error if force is True and it doesn't match. # Here, we force utf-16 as a charset but still pass a utf-8 body. body = ntob("q=\xc2\xa3") self.getPage('/decode/force_charset', method='POST', headers=[("Content-Type", "application/x-www-form-urlencoded"), ("Content-Length", str(len(body))), ], body=body), self.assertErrorPage(400, "The request entity could not be decoded. The following charsets " "were attempted: ['utf-16']")
def finish(self): self.done = True if self.has_trailers and hasattr(self.fp, 'read_trailer_lines'): self.trailers = {} try: for line in self.fp.read_trailer_lines(): if line[0] in ntob(' \t'): # It's a continuation line. v = line.strip() else: try: k, v = line.split(ntob(":"), 1) except ValueError: raise ValueError("Illegal header line.") k = k.strip().title() v = v.strip() if k in comma_separated_headers: existing = self.trailers.get(envname) if existing: v = ntob(", ").join((existing, v)) self.trailers[k] = v except Exception: e = sys.exc_info()[1] if e.__class__.__name__ == 'MaxSizeExceeded': # Post data is too big raise cherrypy.HTTPError( 413, "Maximum request length: %r" % e.args[1]) else: raise
def set_response(self): """Modify cherrypy.response status, headers, and body to represent self. CherryPy uses this internally, but you can also use it to create an HTTPRedirect object and set its output without *raising* the exception. """ import cherrypy response = cherrypy.serving.response response.status = status = self.status if status in (300, 301, 302, 303, 307): response.headers['Content-Type'] = 'text/html;charset=utf-8' # "The ... URI SHOULD be given by the Location field # in the response." response.headers['Location'] = self.urls[0] # "Unless the request method was HEAD, the entity of the response # SHOULD contain a short hypertext note with a hyperlink to the # new URI(s)." msg = { 300: 'This resource can be found at ', 301: 'This resource has permanently moved to ', 302: 'This resource resides temporarily at ', 303: 'This resource can be found at ', 307: 'This resource has moved temporarily to ', }[status] msg += '<a href=%s>%s</a>.' msgs = [msg % (saxutils.quoteattr(u), u) for u in self.urls] response.body = ntob('<br />\n'.join(msgs), 'utf-8') # Previous code may have set C-L, so we have to reset it # (allow finalize to set it). response.headers.pop('Content-Length', None) elif status == 304: # Not Modified. # "The response MUST include the following header fields: # Date, unless its omission is required by section 14.18.1" # The "Date" header should have been set in Response.__init__ # "...the response SHOULD NOT include other entity-headers." for key in ('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-Location', 'Content-MD5', 'Content-Range', 'Content-Type', 'Expires', 'Last-Modified'): if key in response.headers: del response.headers[key] # "The 304 response MUST NOT contain a message-body." response.body = None # Previous code may have set C-L, so we have to reset it. response.headers.pop('Content-Length', None) elif status == 305: # Use Proxy. # self.urls[0] should be the URI of the proxy. response.headers['Location'] = ntob(self.urls[0], 'utf-8') response.body = None # Previous code may have set C-L, so we have to reset it. response.headers.pop('Content-Length', None) else: raise ValueError('The %s status code is unknown.' % status)
def test_file_stream(self): if cherrypy.server.protocol_version != "HTTP/1.1": return self.skip() self.PROTOCOL = "HTTP/1.1" # Make an initial request self.persistent = True conn = self.HTTP_CONN conn.putrequest("GET", "/bigfile", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) body = ntob('') remaining = BIGFILE_SIZE while remaining > 0: data = response.fp.read(65536) if not data: break body += data remaining -= len(data) if self.scheme == "https": newconn = HTTPSConnection else: newconn = HTTPConnection s, h, b = helper.webtest.openURL( ntob("/tell"), headers=[], host=self.HOST, port=self.PORT, http_conn=newconn) if not b: # The file was closed on the server. tell_position = BIGFILE_SIZE else: tell_position = int(b) expected = len(body) if tell_position >= BIGFILE_SIZE: # We can't exactly control how much content the server asks # for. # Fudge it by only checking the first half of the reads. if expected < (BIGFILE_SIZE / 2): self.fail( "The file should have advanced to position %r, but " "has already advanced to the end of the file. It " "may not be streamed as intended, or at the wrong " "chunk size (64k)" % expected) elif tell_position < expected: self.fail( "The file should have advanced to position %r, but has " "only advanced to position %r. It may not be streamed " "as intended, or at the wrong chunk size (65536)" % (expected, tell_position)) if body != ntob("x" * BIGFILE_SIZE): self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." % (BIGFILE_SIZE, body[:50], len(body))) conn.close()
def test_file_stream_deadlock(self): if cherrypy.server.protocol_version != "HTTP/1.1": return self.skip() self.PROTOCOL = "HTTP/1.1" # Make an initial request but abort early. self.persistent = True conn = self.HTTP_CONN conn.putrequest("GET", "/bigfile", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() response = conn.response_class(conn.sock, method="GET") response.begin() self.assertEqual(response.status, 200) body = response.fp.read(65536) if body != ntob("x" * len(body)): self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." % (65536, body[:50], len(body))) response.close() conn.close() # Make a second request, which should fetch the whole file. self.persistent = False self.getPage("/bigfile") if self.body != ntob("x" * BIGFILE_SIZE): self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." % (BIGFILE_SIZE, self.body[:50], len(body)))
def finalize(self): try: code, reason, _ = httputil.valid_status(self.status) except ValueError: raise cherrypy.HTTPError(500, sys.exc_info()[1].args[0]) headers = self.headers self.output_status = ntob(str(code), 'ascii') + ntob(' ') + headers.encode(reason) if self.stream: if dict.get(headers, 'Content-Length') is None: dict.pop(headers, 'Content-Length', None) elif code < 200 or code in (204, 205, 304): dict.pop(headers, 'Content-Length', None) self.body = ntob('') elif dict.get(headers, 'Content-Length') is None: content = self.collapse_body() dict.__setitem__(headers, 'Content-Length', len(content)) self.header_list = h = headers.output() cookie = self.cookie.output() if cookie: for line in cookie.split('\n'): if line.endswith('\r'): line = line[:-1] name, value = line.split(': ', 1) if isinstance(name, unicodestr): name = name.encode('ISO-8859-1') if isinstance(value, unicodestr): value = headers.encode(value) h.append((name, value))
def test_HTTP11_pipelining(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' self.persistent = True conn = self.HTTP_CONN conn.putrequest('GET', '/hello', skip_host=True) conn.putheader('Host', self.HOST) conn.endheaders() for trial in range(5): conn._output(ntob('GET /hello HTTP/1.1')) conn._output(ntob('Host: %s' % self.HOST, 'ascii')) conn._send_output() response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, ntob('Hello, world!')) response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, ntob('Hello, world!')) conn.close()
def test_Chunked_Encoding(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() if hasattr(self, 'harness') and 'modpython' in self.harness.__class__.__name__.lower(): return self.skip() self.PROTOCOL = 'HTTP/1.1' self.persistent = True conn = self.HTTP_CONN body = ntob('8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\nContent-Type: application/json\r\n\r\n') conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Transfer-Encoding', 'chunked') conn.putheader('Trailer', 'Content-Type') conn.putheader('Content-Length', '3') conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus('200 OK') self.assertBody("thanks for '%s'" % ntob('xx\r\nxxxxyyyyy')) body = ntob('3e3\r\n' + 'x' * 995 + '\r\n0\r\n\r\n') conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Transfer-Encoding', 'chunked') conn.putheader('Content-Type', 'text/plain') conn.endheaders() conn.send(body) response = conn.getresponse() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(413) conn.close()
def testCaching(self): elapsed = 0.0 for trial in range(10): self.getPage('/') self.assertBody('visit #1') if trial != 0: age = int(self.assertHeader('Age')) self.assert_(age >= elapsed) elapsed = age self.getPage('/', method='POST') self.assertBody('visit #2') self.assertHeader('Vary', 'Accept-Encoding') self.getPage('/', method='GET') self.assertBody('visit #3') self.getPage('/', method='GET') self.assertBody('visit #3') self.getPage('/', method='DELETE') self.assertBody('visit #4') self.getPage('/', method='GET', headers=[('Accept-Encoding', 'gzip')]) self.assertHeader('Content-Encoding', 'gzip') self.assertHeader('Vary') self.assertEqual(cherrypy.lib.encoding.decompress(self.body), ntob('visit #5')) self.getPage('/', method='GET', headers=[('Accept-Encoding', 'gzip')]) self.assertHeader('Content-Encoding', 'gzip') self.assertEqual(cherrypy.lib.encoding.decompress(self.body), ntob('visit #5')) self.getPage('/', method='GET') self.assertNoHeader('Content-Encoding') self.assertBody('visit #6')
def assertErrorPage(self, status, message=None, pattern=''): """Compare the response body with a built in error page. The function will optionally look for the regexp pattern, within the exception embedded in the error page.""" # This will never contain a traceback page = cherrypy._cperror.get_error_page(status, message=message) # First, test the response body without checking the traceback. # Stick a match-all group (.*) in to grab the traceback. def esc(text): return re.escape(ntob(text)) epage = re.escape(page) epage = epage.replace( esc('<pre id="traceback"></pre>'), esc('<pre id="traceback">') + ntob('(.*)') + esc('</pre>')) m = re.match(epage, self.body, re.DOTALL) if not m: self._handlewebError( 'Error page does not match; expected:\n' + page) return # Now test the pattern against the traceback if pattern is None: # Special-case None to mean that there should be *no* traceback. if m and m.group(1): self._handlewebError('Error page contains traceback') else: if (m is None) or ( not re.search(ntob(re.escape(pattern), self.encoding), m.group(1))): msg = 'Error page does not contain %s in traceback' self._handlewebError(msg % repr(pattern))
def file_ranges(): # Apache compatibility: yield b'\r\n' for start, stop in r: if debug: cherrypy.log( 'Multipart; start: %r, stop: %r' % ( start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob( '\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % ( start, stop - 1, content_length), 'ascii') fileobj.seek(start) gen = file_generator_limited(fileobj, stop - start) for chunk in gen: yield chunk yield b'\r\n' # Final boundary yield ntob('--' + boundary + '--', 'ascii') # Apache compatibility: yield b'\r\n'
def read(self, size = None, fp_out = None): if self.length is None: if size is None: remaining = inf else: remaining = size else: remaining = self.length - self.bytes_read if size and size < remaining: remaining = size if remaining == 0: self.finish() if fp_out is None: return ntob('') else: return chunks = [] if self.buffer: if remaining is inf: data = self.buffer self.buffer = ntob('') else: data = self.buffer[:remaining] self.buffer = self.buffer[remaining:] datalen = len(data) remaining -= datalen self.bytes_read += datalen if self.maxbytes and self.bytes_read > self.maxbytes: raise cherrypy.HTTPError(413) if fp_out is None: chunks.append(data) else: fp_out.write(data) while remaining > 0: chunksize = min(remaining, self.bufsize) try: data = self.fp.read(chunksize) except Exception: e = sys.exc_info()[1] if e.__class__.__name__ == 'MaxSizeExceeded': raise cherrypy.HTTPError(413, 'Maximum request length: %r' % e.args[1]) else: raise if not data: self.finish() break datalen = len(data) remaining -= datalen self.bytes_read += datalen if self.maxbytes and self.bytes_read > self.maxbytes: raise cherrypy.HTTPError(413) if fp_out is None: chunks.append(data) else: fp_out.write(data) if fp_out is None: return ntob('').join(chunks)
def test_100_Continue(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' self.persistent = True conn = self.HTTP_CONN # Try a page without an Expect request header first. # Note that httplib's response.begin automatically ignores # 100 Continue responses, so we must manually check for it. try: conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '4') conn.endheaders() conn.send(ntob("d'oh")) response = conn.response_class(conn.sock, method='POST') version, status, reason = response._read_status() self.assertNotEqual(status, 100) finally: conn.close() # Now try a page with an Expect header... try: conn.connect() conn.putrequest('POST', '/upload', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Type', 'text/plain') conn.putheader('Content-Length', '17') conn.putheader('Expect', '100-continue') conn.endheaders() response = conn.response_class(conn.sock, method='POST') # ...assert and then skip the 100 response version, status, reason = response._read_status() self.assertEqual(status, 100) while True: line = response.fp.readline().strip() if line: self.fail( '100 Continue should not output any headers. Got %r' % line) else: break # ...send the body body = ntob('I am a small file') conn.send(body) # ...get the final response response.begin() self.status, self.headers, self.body = webtest.shb(response) self.assertStatus(200) self.assertBody("thanks for '%s'" % body) finally: conn.close()
def test_query_string_decoding(self): europoundUtf8 = europoundUnicode.encode('utf-8') self.getPage(ntob('/?param=') + europoundUtf8) self.assertBody(europoundUtf8) self.getPage('/reqparams?q=%C2%A3') self.assertBody(ntob('q: \xc2\xa3')) self.getPage('/reqparams?q=%A3') self.assertStatus(404) self.assertErrorPage(404, "The given query string could not be processed. Query strings for this resource must be encoded with 'utf8'.")
def test_decode_tool(self): body = ntob('\xff\xfeq\x00=\xff\xfe\xa3\x00') (self.getPage('/decode/extra_charset', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('q: \xc2\xa3')) body = ntob('q=\xc2\xa3') (self.getPage('/decode/extra_charset', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('q: \xc2\xa3')) body = ntob('q=\xc2\xa3') (self.getPage('/decode/force_charset', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded'), ('Content-Length', str(len(body)))], body=body),) self.assertErrorPage(400, "The request entity could not be decoded. The following charsets were attempted: ['utf-16']")
def test_config_errors(self): # Check that we get an error if no .file or .dir self.getPage("/error/thing.html") self.assertErrorPage(500) if sys.version_info >= (3, 3): errmsg = ntob("TypeError: staticdir\(\) missing 2 " "required positional arguments") else: errmsg = ntob("TypeError: staticdir\(\) takes at least 2 " "(positional )?arguments \(0 given\)") self.assertMatchesBody(errmsg)
def test_app(environ, start_response): status = ntob('200 OK') response_headers = [(ntob('Content-type'), ntob('text/plain'))] start_response(status, response_headers) output = ['Hello, world!\n', 'This is a wsgi app running within CherryPy!\n\n'] keys = list(environ.keys()) keys.sort() for k in keys: output.append('%s: %s\n' % (k,environ[k])) return [ntob(x, 'utf-8') for x in output]
def encode(self, v): for enc in self.encodings: try: return v.encode(enc) except UnicodeEncodeError: continue if self.protocol == (1, 1) and self.use_rfc_2047: v = b2a_base64(v.encode('utf-8')) return ntob('=?utf-8?b?') + v.strip(ntob('\n')) + ntob('?=') raise ValueError('Could not encode header part %r using any of the encodings %r.' % (v, self.encodings))
def urljoin_bytes(*atoms): """Return the given path *atoms, joined into a single URL. This will correctly join a SCRIPT_NAME and PATH_INFO into the original URL, even if either atom is blank. """ url = ntob("/").join([x for x in atoms if x]) while ntob("//") in url: url = url.replace(ntob("//"), ntob("/")) # Special-case the final url of "", and return "/" instead. return url or ntob("/")
def test_multipart_decoding_no_charset(self): body = ntob('\r\n'.join(['--X', 'Content-Disposition: form-data; name="text"', '', '\xe2\x80\x9c', '--X', 'Content-Disposition: form-data; name="submit"', '', 'Create', '--X--'])) (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'multipart/form-data;boundary=X'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('text: \xe2\x80\x9c, submit: Create'))
def unquote_plus(bs): """Bytes version of urllib.parse.unquote_plus.""" bs = bs.replace(ntob('+'), ntob(' ')) atoms = bs.split(ntob('%')) for i in range(1, len(atoms)): item = atoms[i] try: pct = int(item[:2], 16) atoms[i] = bytes([pct]) + item[2:] except ValueError: pass return ntob('').join(atoms)
def test_basic_HTTPMethods(self): helper.webtest.methods_with_bodies = ('POST', 'PUT', 'PROPFIND') for m in defined_http_methods: self.getPage('/method/', method=m) if m == 'HEAD': self.assertBody('') elif m == 'TRACE': self.assertEqual(self.body[:5], ntob('TRACE')) else: self.assertBody(m) self.getPage('/method/parameterized', method='PUT', body='data=on+top+of+other+things') self.assertBody('on top of other things') b = 'one thing on top of another' h = [('Content-Type', 'text/plain'), ('Content-Length', str(len(b)))] self.getPage('/method/request_body', headers=h, method='PUT', body=b) self.assertStatus(200) self.assertBody(b) b = ntob('one thing on top of another') self.persistent = True try: conn = self.HTTP_CONN conn.putrequest('PUT', '/method/request_body', skip_host=True) conn.putheader('Host', self.HOST) conn.putheader('Content-Length', str(len(b))) conn.endheaders() conn.send(b) response = conn.response_class(conn.sock, method='PUT') response.begin() self.assertEqual(response.status, 200) self.body = response.read() self.assertBody(b) finally: self.persistent = False h = [('Content-Type', 'text/plain')] self.getPage('/method/reachable', headers=h, method='PUT') self.assertStatus(411) b = '<?xml version="1.0" encoding="utf-8" ?>\n\n<propfind xmlns="DAV:"><prop><getlastmodified/></prop></propfind>' h = [('Content-Type', 'text/xml'), ('Content-Length', str(len(b)))] self.getPage('/method/request_body', headers=h, method='PROPFIND', body=b) self.assertStatus(200) self.assertBody(b) self.getPage('/method/', method='LINK') self.assertStatus(405) self.getPage('/method/', method='SEARCH') self.assertStatus(501) self.getPage('/divorce/get?ID=13') self.assertBody('Divorce document 13: empty') self.assertStatus(200) self.getPage('/divorce/', method='GET') self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>') self.assertStatus(200)
def digest(password, conf=None): if conf is None: conf = cherrypy.request.app.root.pipa p = ntob(password) s = ntob(conf['salt']) digest = sha256(b'36'*16).digest() for i in range(5000): digest = sha256(digest + s + p + digest).digest() return sha256(digest + s + p + digest).hexdigest()
def test_Flash_Upload(self): headers = [('Accept', 'text/*'), ('Content-Type', 'multipart/form-data; boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'), ('User-Agent', 'Shockwave Flash'), ('Host', 'www.example.com:8080'), ('Content-Length', '499'), ('Connection', 'Keep-Alive'), ('Cache-Control', 'no-cache')] filedata = ntob('<?xml version="1.0" encoding="UTF-8"?>\r\n<projectDescription>\r\n</projectDescription>\r\n') body = ntob('------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\nContent-Disposition: form-data; name="Filename"\r\n\r\n.project\r\n------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\nContent-Disposition: form-data; name="Filedata"; filename=".project"\r\nContent-Type: application/octet-stream\r\n\r\n') + filedata + ntob('\r\n------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\nContent-Disposition: form-data; name="Upload"\r\n\r\nSubmit Query\r\n------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--') self.getPage('/flashupload', headers, 'POST', body) self.assertBody("Upload: u'Submit Query', Filename: u'.project', Filedata: %r" % filedata)
def read_lines_to_boundary(self, fp_out=None): """Read bytes from self.fp and return or write them to a file. If the 'fp_out' argument is None (the default), all bytes read are returned in a single byte string. If the 'fp_out' argument is not None, it must be a file-like object that supports the 'write' method; all bytes read will be written to the fp, and that fp is returned. """ endmarker = self.boundary + ntob('--') delim = ntob('') prev_lf = True lines = [] seen = 0 while True: line = self.fp.readline(1 << 16) if not line: raise EOFError('Illegal end of multipart body.') if line.startswith(ntob('--')) and prev_lf: strippedline = line.strip() if strippedline == self.boundary: break if strippedline == endmarker: self.fp.finish() break line = delim + line if line.endswith(ntob('\r\n')): delim = ntob('\r\n') line = line[:-2] prev_lf = True elif line.endswith(ntob('\n')): delim = ntob('\n') line = line[:-1] prev_lf = True else: delim = ntob('') prev_lf = False if fp_out is None: lines.append(line) seen += len(line) if seen > self.maxrambytes: fp_out = self.make_file() for line in lines: fp_out.write(line) else: fp_out.write(line) if fp_out is None: result = ntob('').join(lines) return result else: fp_out.seek(0) return fp_out
def read_lines_to_boundary(self, fp_out = None): endmarker = self.boundary + ntob('--') delim = ntob('') prev_lf = True lines = [] seen = 0 while True: line = self.fp.readline(65536) if not line: raise EOFError('Illegal end of multipart body.') if line.startswith(ntob('--')) and prev_lf: strippedline = line.strip() if strippedline == self.boundary: break if strippedline == endmarker: self.fp.finish() break line = delim + line if line.endswith(ntob('\r\n')): delim = ntob('\r\n') line = line[:-2] prev_lf = True elif line.endswith(ntob('\n')): delim = ntob('\n') line = line[:-1] prev_lf = True else: delim = ntob('') prev_lf = False if fp_out is None: lines.append(line) seen += len(line) if seen > self.maxrambytes: fp_out = self.make_file() for line in lines: fp_out.write(line) else: fp_out.write(line) if fp_out is None: result = ntob('').join(lines) for charset in self.attempt_charsets: try: result = result.decode(charset) except UnicodeDecodeError: pass else: self.charset = charset return result else: raise cherrypy.HTTPError(400, 'The request entity could not be decoded. The following charsets were attempted: %s' % repr(self.attempt_charsets)) else: fp_out.seek(0) return fp_out
def test_multipart_decoding(self): body = ntob('\r\n'.join(['--X', 'Content-Type: text/plain;charset=utf-16', 'Content-Disposition: form-data; name="text"', '', '\xff\xfea\x00b\x00\x1c c\x00', '--X', 'Content-Type: text/plain;charset=utf-16', 'Content-Disposition: form-data; name="submit"', '', '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00', '--X--'])) (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'multipart/form-data;boundary=X'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('text: ab\xe2\x80\x9cc, submit: Create'))
def test_Flash_Upload(self): headers = [ ('Accept', 'text/*'), ('Content-Type', 'multipart/form-data; ' 'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'), ('User-Agent', 'Shockwave Flash'), ('Host', 'www.example.com:8080'), ('Content-Length', '499'), ('Connection', 'Keep-Alive'), ('Cache-Control', 'no-cache'), ] filedata = ntob('<?xml version="1.0" encoding="UTF-8"?>\r\n' '<projectDescription>\r\n' '</projectDescription>\r\n') body = (ntob( '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 'Content-Disposition: form-data; name="Filename"\r\n' '\r\n' '.project\r\n' '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 'Content-Disposition: form-data; ' 'name="Filedata"; filename=".project"\r\n' 'Content-Type: application/octet-stream\r\n' '\r\n') + filedata + ntob('\r\n' '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 'Content-Disposition: form-data; name="Upload"\r\n' '\r\n' 'Submit Query\r\n' # Flash apps omit the trailing \r\n on the last line: '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--' )) self.getPage('/flashupload', headers, "POST", body) self.assertBody("Upload: Submit Query, Filename: .project, " "Filedata: %r" % filedata)
def __init__(self, fp, length, maxbytes, bufsize=io.DEFAULT_BUFFER_SIZE, has_trailers=False): # Wrap our fp in a buffer so peek() works self.fp = fp self.length = length self.maxbytes = maxbytes self.buffer = ntob('') self.bufsize = bufsize self.bytes_read = 0 self.done = False self.has_trailers = has_trailers
def collapse_body(self): """Collapse self.body to a single string; replace it and return it.""" if isinstance(self.body, text_or_bytes): return self.body newbody = [] for chunk in self.body: if six.PY3 and not isinstance(chunk, bytes): raise TypeError("Chunk %s is not of type 'bytes'." % repr(chunk)) newbody.append(chunk) newbody = ntob('').join(newbody) self.body = newbody return newbody
def test_HTTP11_pipelining(self): if cherrypy.server.protocol_version != "HTTP/1.1": return self.skip() self.PROTOCOL = "HTTP/1.1" # Test pipelining. httplib doesn't support this directly. self.persistent = True conn = self.HTTP_CONN # Put request 1 conn.putrequest("GET", "/hello", skip_host=True) conn.putheader("Host", self.HOST) conn.endheaders() for trial in range(5): # Put next request conn._output(ntob('GET /hello HTTP/1.1')) conn._output(ntob("Host: %s" % self.HOST, 'ascii')) conn._send_output() # Retrieve previous response response = conn.response_class(conn.sock, method="GET") response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, ntob("Hello, world!")) # Retrieve final response response = conn.response_class(conn.sock, method="GET") response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, ntob("Hello, world!")) conn.close()
def test_malformed_header(self): if self.scheme == 'https': c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.putrequest('GET', '/') c.putheader('Content-Type', 'text/plain') # See http://www.cherrypy.org/ticket/941 c._output(ntob('Re, 1.2.3.4#015#012')) c.endheaders() response = c.getresponse() self.status = str(response.status) self.assertStatus(400) self.body = response.fp.read(20) self.assertBody("Illegal header line.")
def tee(body): if 'no-cache' in response.headers.values('Pragma') or 'no-store' in response.headers.values('Cache-Control'): for chunk in body: yield chunk return output = [] for chunk in body: output.append(chunk) yield chunk body = ntob('').join(output) cherrypy._cache.put((response.status, response.headers or {}, body, response.time), len(body))
def getpage(): host = '%s:%s' % (self.interface(), self.PORT) if self.scheme == 'https': c = HTTPSConnection(host) else: c = HTTPConnection(host) try: c.putrequest('GET', '/') c.endheaders() response = c.getresponse() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, ntob("Hello world!")) finally: c.close() success.append(True)
def read_process(cmd, args=''): fullcmd = '%s %s' % (cmd, args) pipeout = popen(fullcmd) try: firstline = pipeout.readline() cmd_not_found = re.search( ntob('(not recognized|No such file|not found)'), firstline, re.IGNORECASE ) if cmd_not_found: raise IOError('%s must be on your system path.' % cmd) output = firstline + pipeout.read() finally: pipeout.close() return output
def test_urlencoded_decoding(self): # Test the decoding of an application/x-www-form-urlencoded entity. europoundUtf8 = europoundUnicode.encode('utf-8') body = ntob("param=") + europoundUtf8 self.getPage('/', method='POST', headers=[ ("Content-Type", "application/x-www-form-urlencoded"), ("Content-Length", str(len(body))), ], body=body), self.assertBody(europoundUtf8) # Encoded utf8 entities MUST be parsed and decoded correctly. # Here, q is the POUND SIGN U+00A3 encoded in utf8 body = ntob("q=\xc2\xa3") self.getPage('/reqparams', method='POST', headers=[ ("Content-Type", "application/x-www-form-urlencoded"), ("Content-Length", str(len(body))), ], body=body), self.assertBody(ntob("q: \xc2\xa3")) # ...and in utf16, which is not in the default attempt_charsets list: body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00") self.getPage('/reqparams', method='POST', headers=[ ("Content-Type", "application/x-www-form-urlencoded;charset=utf-16"), ("Content-Length", str(len(body))), ], body=body), self.assertBody(ntob("q: \xc2\xa3")) # Entities that are incorrectly encoded MUST raise 400. # Here, q is the POUND SIGN U+00A3 encoded in utf16, but # the Content-Type incorrectly labels it utf-8. body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00") self.getPage('/reqparams', method='POST', headers=[ ("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"), ("Content-Length", str(len(body))), ], body=body), self.assertStatus(400) self.assertErrorPage( 400, "The request entity could not be decoded. The following charsets " "were attempted: ['utf-8']")
def test_multipart_decoding_no_charset(self): # Test the decoding of a multipart entity when the charset (utf8) is # NOT explicitly given, but is in the list of charsets to attempt. body = ntob('\r\n'.join([ '--X', 'Content-Disposition: form-data; name="text"', '', '\xe2\x80\x9c', '--X', 'Content-Disposition: form-data; name="submit"', '', 'Create', '--X--' ])) self.getPage('/reqparams', method='POST', headers=[ ('Content-Type', 'multipart/form-data;boundary=X'), ('Content-Length', str(len(body))), ], body=body), self.assertBody(b'submit: Create, text: \xe2\x80\x9c')
def setup_server(): class Root: def index(self): return "This is public." index.exposed = True class BasicProtected: def index(self): return "Hello %s, you've been authorized." % ( cherrypy.request.login) index.exposed = True class BasicProtected2: def index(self): return "Hello %s, you've been authorized." % ( cherrypy.request.login) index.exposed = True userpassdict = {'xuser': '******'} userhashdict = {'xuser': md5(ntob('xpassword')).hexdigest()} def checkpasshash(realm, user, password): p = userhashdict.get(user) return p and p == md5(ntob(password)).hexdigest() or False basic_checkpassword_dict = auth_basic.checkpassword_dict(userpassdict) conf = { '/basic': { 'tools.auth_basic.on': True, 'tools.auth_basic.realm': 'wonderland', 'tools.auth_basic.checkpassword': basic_checkpassword_dict }, '/basic2': { 'tools.auth_basic.on': True, 'tools.auth_basic.realm': 'wonderland', 'tools.auth_basic.checkpassword': checkpasshash }, } root = Root() root.basic = BasicProtected() root.basic2 = BasicProtected2() cherrypy.tree.mount(root, config=conf)
def read_process(cmd, args=""): fullcmd = "%s %s" % (cmd, args) p = subprocess.Popen(fullcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) pipeout = p.stdout try: firstline = pipeout.readline() if (re.search(ntob("(not recognized|No such file|not found)"), firstline, re.IGNORECASE)): raise IOError('%s must be on your system path.' % cmd) output = firstline + pipeout.read() finally: pipeout.close() return output
def tee(body): """Tee response.body into a list.""" if ('no-cache' in response.headers.values('Pragma') or 'no-store' in response.headers.values('Cache-Control')): for chunk in body: yield chunk return output = [] for chunk in body: output.append(chunk) yield chunk # save the cache data body = ntob('').join(output) cherrypy._cache.put((response.status, response.headers or {}, body, response.time), len(body))
def setup_server(): class Root: def index(self): return "This is public." index.exposed = True class DigestProtected: def index(self): return "Hello %s, you've been authorized." % cherrypy.request.login index.exposed = True class BasicProtected: def index(self): return "Hello %s, you've been authorized." % cherrypy.request.login index.exposed = True class BasicProtected2: def index(self): return "Hello %s, you've been authorized." % cherrypy.request.login index.exposed = True def fetch_users(): return {'test': 'test'} def sha_password_encrypter(password): return sha(ntob(password)).hexdigest() def fetch_password(username): return sha(ntob('test')).hexdigest() conf = {'/digest': {'tools.digest_auth.on': True, 'tools.digest_auth.realm': 'localhost', 'tools.digest_auth.users': fetch_users}, '/basic': {'tools.basic_auth.on': True, 'tools.basic_auth.realm': 'localhost', 'tools.basic_auth.users': {'test': md5(ntob('test')).hexdigest()}}, '/basic2': {'tools.basic_auth.on': True, 'tools.basic_auth.realm': 'localhost', 'tools.basic_auth.users': fetch_password, 'tools.basic_auth.encrypt': sha_password_encrypter}} root = Root() root.digest = DigestProtected() root.basic = BasicProtected() root.basic2 = BasicProtected2() cherrypy.tree.mount(root, config=conf)
def file_ranges(): yield ntob('\r\n') for start, stop in r: if debug: cherrypy.log('Multipart; start: %r, stop: %r' % (start, stop), 'TOOLS.STATIC') yield ntob('--' + boundary, 'ascii') yield ntob('\r\nContent-type: %s' % content_type, 'ascii') yield ntob('\r\nContent-range: bytes %s-%s/%s\r\n\r\n' % (start, stop - 1, content_length), 'ascii') fileobj.seek(start) for chunk in file_generator_limited(fileobj, stop - start): yield chunk yield ntob('\r\n') yield ntob('--' + boundary + '--', 'ascii') yield ntob('\r\n')
def finalize(self): """Transform headers (and cookies) into self.header_list. (Core)""" try: code, reason, _ = httputil.valid_status(self.status) except ValueError: raise cherrypy.HTTPError(500, sys.exc_info()[1].args[0]) headers = self.headers self.status = '%s %s' % (code, reason) self.output_status = ntob(str(code), 'ascii') + \ b' ' + headers.encode(reason) if self.stream: # The upshot: wsgiserver will chunk the response if # you pop Content-Length (or set it explicitly to None). # Note that lib.static sets C-L to the file's st_size. if dict.get(headers, 'Content-Length') is None: dict.pop(headers, 'Content-Length', None) elif code < 200 or code in (204, 205, 304): # "All 1xx (informational), 204 (no content), # and 304 (not modified) responses MUST NOT # include a message-body." dict.pop(headers, 'Content-Length', None) self._flush_body() self.body = b'' else: # Responses which are not streamed should have a Content-Length, # but allow user code to set Content-Length if desired. if dict.get(headers, 'Content-Length') is None: content = self.collapse_body() dict.__setitem__(headers, 'Content-Length', len(content)) # Transform our header dict into a list of tuples. self.header_list = h = headers.output() cookie = self.cookie.output() if cookie: for line in cookie.split('\r\n'): name, value = line.split(': ', 1) if isinstance(name, six.text_type): name = name.encode('ISO-8859-1') if isinstance(value, six.text_type): value = headers.encode(value) h.append((name, value))
def start_apache(self): fcgiconf = CONF_PATH if not os.path.isabs(fcgiconf): fcgiconf = os.path.join(curdir, fcgiconf) f = open(fcgiconf, 'wb') try: server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1] output = self.template % {'port': self.port, 'root': curdir, 'server': server} output = ntob(output.replace('\r\n', '\n')) f.write(output) finally: f.close() result = read_process(APACHE_PATH, '-k start -f %s' % fcgiconf) if result: print result
def test_598(self): remote_data_conn = urlopen('%s://%s:%s/one_megabyte_of_a/' % (self.scheme, self.HOST, self.PORT)) buf = remote_data_conn.read(512) time.sleep(timeout * 0.6) remaining = 1048064 while remaining: data = remote_data_conn.read(remaining) if not data: break else: buf += data remaining -= len(data) self.assertEqual(len(buf), 1048576) self.assertEqual(buf, ntob('a' * 1024 * 1024)) self.assertEqual(remaining, 0) remote_data_conn.close()
def test_multipart_decoding_no_successful_charset(self): body = ntob('\r\n'.join([ '--X', 'Content-Disposition: form-data; name="text"', '', '\xff\xfea\x00b\x00\x1c c\x00', '--X', 'Content-Disposition: form-data; name="submit"', '', '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00', '--X--' ])) (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'multipart/form-data;boundary=X'), ('Content-Length', str(len(body)))], body=body), ) self.assertStatus(400) self.assertErrorPage( 400, "The request entity could not be decoded. The following charsets were attempted: ['us-ascii', 'utf-8']" )
def write_conf(self, extra=''): if self.ssl: serverpem = os.path.join(thisdir, 'test.pem') ssl = "\nserver.ssl_certificate: r'%s'\nserver.ssl_private_key: r'%s'\n" % ( serverpem, serverpem) else: ssl = '' conf = self.config_template % { 'host': self.host, 'port': self.port, 'error_log': self.error_log, 'access_log': self.access_log, 'ssl': ssl, 'extra': extra } f = open(self.config_file, 'wb') f.write(ntob(conf, 'utf-8')) f.close()
def test_multipart_decoding(self): # Test the decoding of a multipart entity when the charset (utf16) is # explicitly given. body = ntob('\r\n'.join([ '--X', 'Content-Type: text/plain;charset=utf-16', 'Content-Disposition: form-data; name="text"', '', '\xff\xfea\x00b\x00\x1c c\x00', '--X', 'Content-Type: text/plain;charset=utf-16', 'Content-Disposition: form-data; name="submit"', '', '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00', '--X--' ])) self.getPage('/reqparams', method='POST', headers=[ ('Content-Type', 'multipart/form-data;boundary=X'), ('Content-Length', str(len(body))), ], body=body), self.assertBody(b'submit: Create, text: ab\xe2\x80\x9cc')
def test_signal_handler_unsubscribe(self): try: from signal import SIGTERM except ImportError: return self.skip('skipped (no SIGTERM) ') try: from os import kill except ImportError: return self.skip('skipped (no os.kill) ') p = helper.CPProcess(ssl=self.scheme.lower() == 'https') p.write_conf(extra='unsubsig: True\ntest_case_name: "test_signal_handler_unsubscribe"\n') p.start(imports='cherrypy.test._test_states_demo') os.kill(p.get_pid(), SIGTERM) p.join() target_line = open(p.error_log, 'rb').readlines()[-10] if ntob('I am an old SIGTERM handler.') not in target_line: self.fail('Old SIGTERM handler did not run.\n%r' % target_line)
def login_screen(self, from_page='..', username='', error_msg='', **kwargs): return ntob( """<html><body> Message: %(error_msg)s <form method="post" action="do_login"> Login: <input type="text" name="username" value="%(username)s" size="10" /><br /> Password: <input type="password" name="password" size="10" /><br /> <input type="hidden" name="from_page" value="%(from_page)s" /><br /> <input type="submit" /> </form> </body></html>""" % { 'from_page': from_page, 'username': username, 'error_msg': error_msg }, "utf-8")
def test_no_content_length(self): if self.scheme == 'https': c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.request('POST', '/no_body') response = c.getresponse() self.body = response.fp.read() self.status = str(response.status) self.assertStatus(200) self.assertBody(ntob('Hello world!')) if self.scheme == 'https': c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT)) else: c = HTTPConnection('%s:%s' % (self.interface(), self.PORT)) c.request('POST', '/') response = c.getresponse() self.body = response.fp.read() self.status = str(response.status) self.assertStatus(411)
def test_urlencoded_decoding(self): europoundUtf8 = europoundUnicode.encode('utf-8') body = ntob('param=') + europoundUtf8 (self.getPage('/', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(europoundUtf8) body = ntob('q=\xc2\xa3') (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('q: \xc2\xa3')) body = ntob('\xff\xfeq\x00=\xff\xfe\xa3\x00') (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded;charset=utf-16'), ('Content-Length', str(len(body)))], body=body),) self.assertBody(ntob('q: \xc2\xa3')) body = ntob('\xff\xfeq\x00=\xff\xfe\xa3\x00') (self.getPage('/reqparams', method='POST', headers=[('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8'), ('Content-Length', str(len(body)))], body=body),) self.assertStatus(400) self.assertErrorPage(400, "The request entity could not be decoded. The following charsets were attempted: ['utf-8']")
def test_HTTP11_pipelining(self): if cherrypy.server.protocol_version != 'HTTP/1.1': return self.skip() self.PROTOCOL = 'HTTP/1.1' # Test pipelining. httplib doesn't support this directly. self.persistent = True conn = self.HTTP_CONN # Put request 1 conn.putrequest('GET', '/hello', skip_host=True) conn.putheader('Host', self.HOST) conn.endheaders() for trial in range(5): # Put next request conn._output(b'GET /hello HTTP/1.1') conn._output(ntob('Host: %s' % self.HOST, 'ascii')) conn._send_output() # Retrieve previous response response = conn.response_class(conn.sock, method='GET') # there is a bug in python3 regarding the buffering of # ``conn.sock``. Until that bug get's fixed we will # monkey patch the ``response`` instance. # https://bugs.python.org/issue23377 response.fp = conn.sock.makefile('rb', 0) response.begin() body = response.read(13) self.assertEqual(response.status, 200) self.assertEqual(body, b'Hello, world!') # Retrieve final response response = conn.response_class(conn.sock, method='GET') response.begin() body = response.read() self.assertEqual(response.status, 200) self.assertEqual(body, b'Hello, world!') conn.close()
def _be_ie_unfriendly(status): response = cherrypy.serving.response # For some statuses, Internet Explorer 5+ shows "friendly error # messages" instead of our response.body if the body is smaller # than a given size. Fix this by returning a body over that size # (by adding whitespace). # See http://support.microsoft.com/kb/q218155/ s = _ie_friendly_error_sizes.get(status, 0) if s: s += 1 # Since we are issuing an HTTP error status, we assume that # the entity is short, and we should just collapse it. content = response.collapse_body() content_length = len(content) if content_length and content_length < s: # IN ADDITION: the response must be written to IE # in one chunk or it will still get replaced! Bah. content = content + (ntob(' ') * (s - content_length)) response.body = content response.headers['Content-Length'] = str(len(content))
def test_multipart_decoding_no_successful_charset(self): # Test the decoding of a multipart entity when the charset (utf16) is # NOT explicitly given, and is NOT in the list of charsets to attempt. body = ntob('\r\n'.join([ '--X', 'Content-Disposition: form-data; name="text"', '', '\xff\xfea\x00b\x00\x1c c\x00', '--X', 'Content-Disposition: form-data; name="submit"', '', '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00', '--X--' ])) self.getPage('/reqparams', method='POST', headers=[ ("Content-Type", "multipart/form-data;boundary=X"), ("Content-Length", str(len(body))), ], body=body), self.assertStatus(400) self.assertErrorPage( 400, "The request entity could not be decoded. The following charsets " "were attempted: ['us-ascii', 'utf-8']")
def test_query_string_decoding(self): URI_TMPL = '/reqparams?q={q}' europoundUtf8_2_bytes = europoundUnicode.encode('utf-8') europoundUtf8_2nd_byte = europoundUtf8_2_bytes[1:2] # Encoded utf8 query strings MUST be parsed correctly. # Here, q is the POUND SIGN U+00A3 encoded in utf8 and then %HEX self.getPage(URI_TMPL.format(q=url_quote(europoundUtf8_2_bytes))) # The return value will be encoded as utf8. self.assertBody(ntob('q: ') + europoundUtf8_2_bytes) # Query strings that are incorrectly encoded MUST raise 404. # Here, q is the second byte of POUND SIGN U+A3 encoded in utf8 # and then %HEX # TODO: check whether this shouldn't raise 400 Bad Request instead self.getPage(URI_TMPL.format(q=url_quote(europoundUtf8_2nd_byte))) self.assertStatus(404) self.assertErrorPage( 404, 'The given query string could not be processed. Query ' "strings for this resource must be encoded with 'utf8'.")
def trap(self, func, *args, **kwargs): try: return func(*args, **kwargs) except self.throws: raise except StopIteration: raise except: tb = _cperror.format_exc() #print('trapped (started %s):' % self.started_response, tb) _cherrypy.log(tb, severity=40) if not _cherrypy.request.show_tracebacks: tb = "" s, h, b = _cperror.bare_error(tb) if py3k: # What fun. s = s.decode('ISO-8859-1') h = [(k.decode('ISO-8859-1'), v.decode('ISO-8859-1')) for k, v in h] if self.started_response: # Empty our iterable (so future calls raise StopIteration) self.iter_response = iter([]) else: self.iter_response = iter(b) try: self.start_response(s, h, _sys.exc_info()) except: # "The application must not trap any exceptions raised by # start_response, if it called start_response with exc_info. # Instead, it should allow such exceptions to propagate # back to the server or gateway." # But we still log and call close() to clean up ourselves. _cherrypy.log(traceback=True, severity=40) raise if self.started_response: return ntob("").join(b) else: return b