1"""Tests for various MIME issues, including the safe_multipart Tool.""" 2 3import cherrypy 4from cherrypy._cpcompat import ntou 5from cherrypy.test import helper 6 7 8def setup_server(): 9 10 class Root: 11 12 @cherrypy.expose 13 def multipart(self, parts): 14 return repr(parts) 15 16 @cherrypy.expose 17 def multipart_form_data(self, **kwargs): 18 return repr(list(sorted(kwargs.items()))) 19 20 @cherrypy.expose 21 def flashupload(self, Filedata, Upload, Filename): 22 return ('Upload: %s, Filename: %s, Filedata: %r' % 23 (Upload, Filename, Filedata.file.read())) 24 25 cherrypy.config.update({'server.max_request_body_size': 0}) 26 cherrypy.tree.mount(Root()) 27 28 29# Client-side code # 30 31 32class MultipartTest(helper.CPWebCase): 33 setup_server = staticmethod(setup_server) 34 35 def test_multipart(self): 36 text_part = ntou('This is the text version') 37 html_part = ntou( 38 """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 39<html> 40<head> 41 <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type"> 42</head> 43<body bgcolor="#ffffff" text="#000000"> 44 45This is the <strong>HTML</strong> version 46</body> 47</html> 48""") 49 body = '\r\n'.join([ 50 '--123456789', 51 "Content-Type: text/plain; charset='ISO-8859-1'", 52 'Content-Transfer-Encoding: 7bit', 53 '', 54 text_part, 55 '--123456789', 56 "Content-Type: text/html; charset='ISO-8859-1'", 57 '', 58 html_part, 59 '--123456789--']) 60 headers = [ 61 ('Content-Type', 'multipart/mixed; boundary=123456789'), 62 ('Content-Length', str(len(body))), 63 ] 64 self.getPage('/multipart', headers, 'POST', body) 65 self.assertBody(repr([text_part, html_part])) 66 67 def test_multipart_form_data(self): 68 body = '\r\n'.join([ 69 '--X', 70 'Content-Disposition: form-data; name="foo"', 71 '', 72 'bar', 73 '--X', 74 # Test a param with more than one value. 75 # See 76 # https://github.com/cherrypy/cherrypy/issues/1028 77 'Content-Disposition: form-data; name="baz"', 78 '', 79 '111', 80 '--X', 81 'Content-Disposition: form-data; name="baz"', 82 '', 83 '333', 84 '--X--' 85 ]) 86 self.getPage('/multipart_form_data', method='POST', 87 headers=[( 88 'Content-Type', 'multipart/form-data;boundary=X'), 89 ('Content-Length', str(len(body))), 90 ], 91 body=body), 92 self.assertBody( 93 repr([('baz', [ntou('111'), ntou('333')]), ('foo', ntou('bar'))])) 94 95 96class SafeMultipartHandlingTest(helper.CPWebCase): 97 setup_server = staticmethod(setup_server) 98 99 def test_Flash_Upload(self): 100 headers = [ 101 ('Accept', 'text/*'), 102 ('Content-Type', 'multipart/form-data; ' 103 'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'), 104 ('User-Agent', 'Shockwave Flash'), 105 ('Host', 'www.example.com:54583'), 106 ('Content-Length', '499'), 107 ('Connection', 'Keep-Alive'), 108 ('Cache-Control', 'no-cache'), 109 ] 110 filedata = (b'<?xml version="1.0" encoding="UTF-8"?>\r\n' 111 b'<projectDescription>\r\n' 112 b'</projectDescription>\r\n') 113 body = ( 114 b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 115 b'Content-Disposition: form-data; name="Filename"\r\n' 116 b'\r\n' 117 b'.project\r\n' 118 b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 119 b'Content-Disposition: form-data; ' 120 b'name="Filedata"; filename=".project"\r\n' 121 b'Content-Type: application/octet-stream\r\n' 122 b'\r\n' + 123 filedata + 124 b'\r\n' 125 b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 126 b'Content-Disposition: form-data; name="Upload"\r\n' 127 b'\r\n' 128 b'Submit Query\r\n' 129 # Flash apps omit the trailing \r\n on the last line: 130 b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--' 131 ) 132 self.getPage('/flashupload', headers, 'POST', body) 133 self.assertBody('Upload: Submit Query, Filename: .project, ' 134 'Filedata: %r' % filedata) 135