PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/Lib/test/test_cgi.py

https://bitbucket.org/glix/python
Python | 388 lines | 367 code | 13 blank | 8 comment | 12 complexity | 46c20f1698ff444f14259177b84401db MD5 | raw file
  1. from test.test_support import run_unittest
  2. import cgi
  3. import os
  4. import sys
  5. import tempfile
  6. import unittest
  7. from StringIO import StringIO
  8. class HackedSysModule:
  9. # The regression test will have real values in sys.argv, which
  10. # will completely confuse the test of the cgi module
  11. argv = []
  12. stdin = sys.stdin
  13. cgi.sys = HackedSysModule()
  14. try:
  15. from cStringIO import StringIO
  16. except ImportError:
  17. from StringIO import StringIO
  18. class ComparableException:
  19. def __init__(self, err):
  20. self.err = err
  21. def __str__(self):
  22. return str(self.err)
  23. def __cmp__(self, anExc):
  24. if not isinstance(anExc, Exception):
  25. return -1
  26. x = cmp(self.err.__class__, anExc.__class__)
  27. if x != 0:
  28. return x
  29. return cmp(self.err.args, anExc.args)
  30. def __getattr__(self, attr):
  31. return getattr(self.err, attr)
  32. def do_test(buf, method):
  33. env = {}
  34. if method == "GET":
  35. fp = None
  36. env['REQUEST_METHOD'] = 'GET'
  37. env['QUERY_STRING'] = buf
  38. elif method == "POST":
  39. fp = StringIO(buf)
  40. env['REQUEST_METHOD'] = 'POST'
  41. env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
  42. env['CONTENT_LENGTH'] = str(len(buf))
  43. else:
  44. raise ValueError, "unknown method: %s" % method
  45. try:
  46. return cgi.parse(fp, env, strict_parsing=1)
  47. except StandardError, err:
  48. return ComparableException(err)
  49. parse_strict_test_cases = [
  50. ("", ValueError("bad query field: ''")),
  51. ("&", ValueError("bad query field: ''")),
  52. ("&&", ValueError("bad query field: ''")),
  53. (";", ValueError("bad query field: ''")),
  54. (";&;", ValueError("bad query field: ''")),
  55. # Should the next few really be valid?
  56. ("=", {}),
  57. ("=&=", {}),
  58. ("=;=", {}),
  59. # This rest seem to make sense
  60. ("=a", {'': ['a']}),
  61. ("&=a", ValueError("bad query field: ''")),
  62. ("=a&", ValueError("bad query field: ''")),
  63. ("=&a", ValueError("bad query field: 'a'")),
  64. ("b=a", {'b': ['a']}),
  65. ("b+=a", {'b ': ['a']}),
  66. ("a=b=a", {'a': ['b=a']}),
  67. ("a=+b=a", {'a': [' b=a']}),
  68. ("&b=a", ValueError("bad query field: ''")),
  69. ("b&=a", ValueError("bad query field: 'b'")),
  70. ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}),
  71. ("a=a+b&a=b+a", {'a': ['a b', 'b a']}),
  72. ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
  73. ("x=1;y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
  74. ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}),
  75. ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env",
  76. {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'],
  77. 'cuyer': ['r'],
  78. 'expire': ['964546263'],
  79. 'kid': ['130003.300038'],
  80. 'lobale': ['en-US'],
  81. 'order_id': ['0bb2e248638833d48cb7fed300000f1b'],
  82. 'ss': ['env'],
  83. 'view': ['bustomer'],
  84. }),
  85. ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse",
  86. {'SUBMIT': ['Browse'],
  87. '_assigned_to': ['31392'],
  88. '_category': ['100'],
  89. '_status': ['1'],
  90. 'group_id': ['5470'],
  91. 'set': ['custom'],
  92. })
  93. ]
  94. def norm(list):
  95. if type(list) == type([]):
  96. list.sort()
  97. return list
  98. def first_elts(list):
  99. return map(lambda x:x[0], list)
  100. def first_second_elts(list):
  101. return map(lambda p:(p[0], p[1][0]), list)
  102. def gen_result(data, environ):
  103. fake_stdin = StringIO(data)
  104. fake_stdin.seek(0)
  105. form = cgi.FieldStorage(fp=fake_stdin, environ=environ)
  106. result = {}
  107. for k, v in dict(form).items():
  108. result[k] = type(v) is list and form.getlist(k) or v.value
  109. return result
  110. class CgiTests(unittest.TestCase):
  111. def test_strict(self):
  112. for orig, expect in parse_strict_test_cases:
  113. # Test basic parsing
  114. d = do_test(orig, "GET")
  115. self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
  116. d = do_test(orig, "POST")
  117. self.assertEqual(d, expect, "Error parsing %s" % repr(orig))
  118. env = {'QUERY_STRING': orig}
  119. fcd = cgi.FormContentDict(env)
  120. sd = cgi.SvFormContentDict(env)
  121. fs = cgi.FieldStorage(environ=env)
  122. if type(expect) == type({}):
  123. # test dict interface
  124. self.assertEqual(len(expect), len(fcd))
  125. self.assertEqual(norm(expect.keys()), norm(fcd.keys()))
  126. self.assertEqual(norm(expect.values()), norm(fcd.values()))
  127. self.assertEqual(norm(expect.items()), norm(fcd.items()))
  128. self.assertEqual(fcd.get("nonexistent field", "default"), "default")
  129. self.assertEqual(len(sd), len(fs))
  130. self.assertEqual(norm(sd.keys()), norm(fs.keys()))
  131. self.assertEqual(fs.getvalue("nonexistent field", "default"), "default")
  132. # test individual fields
  133. for key in expect.keys():
  134. expect_val = expect[key]
  135. self.assert_(fcd.has_key(key))
  136. self.assertEqual(norm(fcd[key]), norm(expect[key]))
  137. self.assertEqual(fcd.get(key, "default"), fcd[key])
  138. self.assert_(fs.has_key(key))
  139. if len(expect_val) > 1:
  140. single_value = 0
  141. else:
  142. single_value = 1
  143. try:
  144. val = sd[key]
  145. except IndexError:
  146. self.failIf(single_value)
  147. self.assertEqual(fs.getvalue(key), expect_val)
  148. else:
  149. self.assert_(single_value)
  150. self.assertEqual(val, expect_val[0])
  151. self.assertEqual(fs.getvalue(key), expect_val[0])
  152. self.assertEqual(norm(sd.getlist(key)), norm(expect_val))
  153. if single_value:
  154. self.assertEqual(norm(sd.values()),
  155. first_elts(norm(expect.values())))
  156. self.assertEqual(norm(sd.items()),
  157. first_second_elts(norm(expect.items())))
  158. def test_weird_formcontentdict(self):
  159. # Test the weird FormContentDict classes
  160. env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"}
  161. expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'}
  162. d = cgi.InterpFormContentDict(env)
  163. for k, v in expect.items():
  164. self.assertEqual(d[k], v)
  165. for k, v in d.items():
  166. self.assertEqual(expect[k], v)
  167. self.assertEqual(norm(expect.values()), norm(d.values()))
  168. def test_log(self):
  169. cgi.log("Testing")
  170. cgi.logfp = StringIO()
  171. cgi.initlog("%s", "Testing initlog 1")
  172. cgi.log("%s", "Testing log 2")
  173. self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n")
  174. if os.path.exists("/dev/null"):
  175. cgi.logfp = None
  176. cgi.logfile = "/dev/null"
  177. cgi.initlog("%s", "Testing log 3")
  178. cgi.log("Testing log 4")
  179. def test_fieldstorage_readline(self):
  180. # FieldStorage uses readline, which has the capacity to read all
  181. # contents of the input file into memory; we use readline's size argument
  182. # to prevent that for files that do not contain any newlines in
  183. # non-GET/HEAD requests
  184. class TestReadlineFile:
  185. def __init__(self, file):
  186. self.file = file
  187. self.numcalls = 0
  188. def readline(self, size=None):
  189. self.numcalls += 1
  190. if size:
  191. return self.file.readline(size)
  192. else:
  193. return self.file.readline()
  194. def __getattr__(self, name):
  195. file = self.__dict__['file']
  196. a = getattr(file, name)
  197. if not isinstance(a, int):
  198. setattr(self, name, a)
  199. return a
  200. f = TestReadlineFile(tempfile.TemporaryFile())
  201. f.write('x' * 256 * 1024)
  202. f.seek(0)
  203. env = {'REQUEST_METHOD':'PUT'}
  204. fs = cgi.FieldStorage(fp=f, environ=env)
  205. # if we're not chunking properly, readline is only called twice
  206. # (by read_binary); if we are chunking properly, it will be called 5 times
  207. # as long as the chunksize is 1 << 16.
  208. self.assert_(f.numcalls > 2)
  209. def test_fieldstorage_multipart(self):
  210. #Test basic FieldStorage multipart parsing
  211. env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'}
  212. postdata = """-----------------------------721837373350705526688164684
  213. Content-Disposition: form-data; name="id"
  214. 1234
  215. -----------------------------721837373350705526688164684
  216. Content-Disposition: form-data; name="title"
  217. -----------------------------721837373350705526688164684
  218. Content-Disposition: form-data; name="file"; filename="test.txt"
  219. Content-Type: text/plain
  220. Testing 123.
  221. -----------------------------721837373350705526688164684
  222. Content-Disposition: form-data; name="submit"
  223. Add\x20
  224. -----------------------------721837373350705526688164684--
  225. """
  226. fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env)
  227. self.assertEquals(len(fs.list), 4)
  228. expect = [{'name':'id', 'filename':None, 'value':'1234'},
  229. {'name':'title', 'filename':None, 'value':''},
  230. {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'},
  231. {'name':'submit', 'filename':None, 'value':' Add '}]
  232. for x in range(len(fs.list)):
  233. for k, exp in expect[x].items():
  234. got = getattr(fs.list[x], k)
  235. self.assertEquals(got, exp)
  236. _qs_result = {
  237. 'key1': 'value1',
  238. 'key2': ['value2x', 'value2y'],
  239. 'key3': 'value3',
  240. 'key4': 'value4'
  241. }
  242. def testQSAndUrlEncode(self):
  243. data = "key2=value2x&key3=value3&key4=value4"
  244. environ = {
  245. 'CONTENT_LENGTH': str(len(data)),
  246. 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
  247. 'QUERY_STRING': 'key1=value1&key2=value2y',
  248. 'REQUEST_METHOD': 'POST',
  249. }
  250. v = gen_result(data, environ)
  251. self.assertEqual(self._qs_result, v)
  252. def testQSAndFormData(self):
  253. data = """
  254. ---123
  255. Content-Disposition: form-data; name="key2"
  256. value2y
  257. ---123
  258. Content-Disposition: form-data; name="key3"
  259. value3
  260. ---123
  261. Content-Disposition: form-data; name="key4"
  262. value4
  263. ---123--
  264. """
  265. environ = {
  266. 'CONTENT_LENGTH': str(len(data)),
  267. 'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
  268. 'QUERY_STRING': 'key1=value1&key2=value2x',
  269. 'REQUEST_METHOD': 'POST',
  270. }
  271. v = gen_result(data, environ)
  272. self.assertEqual(self._qs_result, v)
  273. def testQSAndFormDataFile(self):
  274. data = """
  275. ---123
  276. Content-Disposition: form-data; name="key2"
  277. value2y
  278. ---123
  279. Content-Disposition: form-data; name="key3"
  280. value3
  281. ---123
  282. Content-Disposition: form-data; name="key4"
  283. value4
  284. ---123
  285. Content-Disposition: form-data; name="upload"; filename="fake.txt"
  286. Content-Type: text/plain
  287. this is the content of the fake file
  288. ---123--
  289. """
  290. environ = {
  291. 'CONTENT_LENGTH': str(len(data)),
  292. 'CONTENT_TYPE': 'multipart/form-data; boundary=-123',
  293. 'QUERY_STRING': 'key1=value1&key2=value2x',
  294. 'REQUEST_METHOD': 'POST',
  295. }
  296. result = self._qs_result.copy()
  297. result.update({
  298. 'upload': 'this is the content of the fake file\n'
  299. })
  300. v = gen_result(data, environ)
  301. self.assertEqual(result, v)
  302. def test_deprecated_parse_qs(self):
  303. # this func is moved to urlparse, this is just a sanity check
  304. self.assertEqual({'a': ['A1'], 'B': ['B3'], 'b': ['B2']},
  305. cgi.parse_qs('a=A1&b=B2&B=B3'))
  306. def test_deprecated_parse_qsl(self):
  307. # this func is moved to urlparse, this is just a sanity check
  308. self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')],
  309. cgi.parse_qsl('a=A1&b=B2&B=B3'))
  310. def test_parse_header(self):
  311. self.assertEqual(
  312. cgi.parse_header("text/plain"),
  313. ("text/plain", {}))
  314. self.assertEqual(
  315. cgi.parse_header("text/vnd.just.made.this.up ; "),
  316. ("text/vnd.just.made.this.up", {}))
  317. self.assertEqual(
  318. cgi.parse_header("text/plain;charset=us-ascii"),
  319. ("text/plain", {"charset": "us-ascii"}))
  320. self.assertEqual(
  321. cgi.parse_header('text/plain ; charset="us-ascii"'),
  322. ("text/plain", {"charset": "us-ascii"}))
  323. self.assertEqual(
  324. cgi.parse_header('text/plain ; charset="us-ascii"; another=opt'),
  325. ("text/plain", {"charset": "us-ascii", "another": "opt"}))
  326. self.assertEqual(
  327. cgi.parse_header('attachment; filename="silly.txt"'),
  328. ("attachment", {"filename": "silly.txt"}))
  329. self.assertEqual(
  330. cgi.parse_header('attachment; filename="strange;name"'),
  331. ("attachment", {"filename": "strange;name"}))
  332. self.assertEqual(
  333. cgi.parse_header('attachment; filename="strange;name";size=123;'),
  334. ("attachment", {"filename": "strange;name", "size": "123"}))
  335. def test_main():
  336. run_unittest(CgiTests)
  337. if __name__ == '__main__':
  338. test_main()