test.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2018 Espressif Systems (Shanghai) PTE LTD
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. # Utility for testing the web server. Test cases:
  17. # Assume the device supports 'n' simultaneous open sockets
  18. #
  19. # HTTP Server Tests
  20. #
  21. # 0. Firmware Settings:
  22. # - Create a dormant thread whose sole job is to call httpd_stop() when instructed
  23. # - Measure the following before httpd_start() is called:
  24. # - current free memory
  25. # - current free sockets
  26. # - Measure the same whenever httpd_stop is called
  27. # - Register maximum possible URI handlers: should be successful
  28. # - Register one more URI handler: should fail
  29. # - Deregister on URI handler: should be successful
  30. # - Register on more URI handler: should succeed
  31. # - Register separate handlers for /hello, /hello/type_html. Also
  32. # ensure that /hello/type_html is registered BEFORE /hello. (tests
  33. # that largest matching URI is picked properly)
  34. # - Create URI handler /adder. Make sure it uses a custom free_ctx
  35. # structure to free it up
  36. # 1. Using Standard Python HTTP Client
  37. # - simple GET on /hello (returns Hello World. Ensures that basic
  38. # firmware tests are complete, or returns error)
  39. # - POST on /hello (should fail)
  40. # - PUT on /hello (should fail)
  41. # - simple POST on /echo (returns whatever the POST data)
  42. # - simple PUT on /echo (returns whatever the PUT data)
  43. # - GET on /echo (should fail)
  44. # - simple GET on /hello/type_html (returns Content type as text/html)
  45. # - simple GET on /hello/status_500 (returns HTTP status 500)
  46. # - simple GET on /false_uri (returns HTTP status 404)
  47. # - largest matching URI handler is picked is already verified because
  48. # of /hello and /hello/type_html tests
  49. #
  50. #
  51. # 2. Session Tests
  52. # - Sessions + Pipelining basics:
  53. # - Create max supported sessions
  54. # - On session i,
  55. # - send 3 back-to-back POST requests with data i on /adder
  56. # - read back 3 responses. They should be i, 2i and 3i
  57. # - Tests that
  58. # - pipelining works
  59. # - per-session context is maintained for all supported
  60. # sessions
  61. # - Close all sessions
  62. #
  63. # - Cleanup leftover data: Tests that the web server properly cleans
  64. # up leftover data
  65. # - Create a session
  66. # - POST on /leftover_data with 52 bytes of data (data includes
  67. # \r\n)(the handler only
  68. # reads first 10 bytes and returns them, leaving the rest of the
  69. # bytes unread)
  70. # - GET on /hello (should return 'Hello World')
  71. # - POST on /false_uri with 52 bytes of data (data includes \r\n)
  72. # (should return HTTP 404)
  73. # - GET on /hello (should return 'Hello World')
  74. #
  75. # - Test HTTPd Asynchronous response
  76. # - Create a session
  77. # - GET on /async_data
  78. # - returns 'Hello World!' as a response
  79. # - the handler schedules an async response, which generates a second
  80. # response 'Hello Double World!'
  81. #
  82. # - Spillover test
  83. # - Create max supported sessions with the web server
  84. # - GET /hello on all the sessions (should return Hello World)
  85. # - Create one more session, this should fail
  86. # - GET /hello on all the sessions (should return Hello World)
  87. #
  88. # - Timeout test
  89. # - Create a session and only Send 'GE' on the same (simulates a
  90. # client that left the network halfway through a request)
  91. # - Wait for recv-wait-timeout
  92. # - Server should automatically close the socket
  93. ############# TODO TESTS #############
  94. # 3. Stress Tests
  95. #
  96. # - httperf
  97. # - Run the following httperf command:
  98. # httperf --server=10.31.130.126 --wsess=8,50,0.5 --rate 8 --burst-length 2
  99. #
  100. # - The above implies that the test suite will open
  101. # - 8 simultaneous connections with the server
  102. # - the rate of opening the sessions will be 8 per sec. So in our
  103. # case, a new connection will be opened every 0.2 seconds for 1 second
  104. # - The burst length 2 indicates that 2 requests will be sent
  105. # simultaneously on the same connection in a single go
  106. # - 0.5 seconds is the time between sending out 2 bursts
  107. # - 50 is the total number of requests that will be sent out
  108. #
  109. # - So in the above example, the test suite will open 8
  110. # connections, each separated by 0.2 seconds. On each connection
  111. # it will send 2 requests in a single burst. The bursts on a
  112. # single connection will be separated by 0.5 seconds. A total of
  113. # 25 bursts (25 x 2 = 50) will be sent out
  114. # 4. Leak Tests
  115. # - Simple Leak test
  116. # - Simple GET on /hello/restart (returns success, stop web server, measures leaks, restarts webserver)
  117. # - Simple GET on /hello/restart_results (returns the leak results)
  118. # - Leak test with open sockets
  119. # - Open 8 sessions
  120. # - Simple GET on /hello/restart (returns success, stop web server,
  121. # measures leaks, restarts webserver)
  122. # - All sockets should get closed
  123. # - Simple GET on /hello/restart_results (returns the leak results)
  124. import threading
  125. import socket
  126. import time
  127. import argparse
  128. import httplib
  129. import sys
  130. import string
  131. import random
  132. _verbose_ = False
  133. class Session:
  134. def __init__(self, addr, port, timeout = 15):
  135. self.client = socket.create_connection((addr, int(port)), timeout = timeout)
  136. self.target = addr
  137. self.status = 0
  138. self.encoding = ''
  139. self.content_type = ''
  140. self.content_len = 0
  141. def send_err_check(self, request, data=None):
  142. rval = True
  143. try:
  144. self.client.sendall(request);
  145. if data:
  146. self.client.sendall(data)
  147. except socket.error as err:
  148. self.client.close()
  149. print "Socket Error in send :", err
  150. rval = False
  151. return rval
  152. def send_get(self, path, headers=None):
  153. request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
  154. if headers:
  155. for field, value in headers.iteritems():
  156. request += "\r\n"+field+": "+value
  157. request += "\r\n\r\n"
  158. return self.send_err_check(request)
  159. def send_put(self, path, data, headers=None):
  160. request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
  161. if headers:
  162. for field, value in headers.iteritems():
  163. request += "\r\n"+field+": "+value
  164. request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
  165. return self.send_err_check(request, data)
  166. def send_post(self, path, data, headers=None):
  167. request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
  168. if headers:
  169. for field, value in headers.iteritems():
  170. request += "\r\n"+field+": "+value
  171. request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
  172. return self.send_err_check(request, data)
  173. def read_resp_hdrs(self):
  174. try:
  175. state = 'nothing'
  176. resp_read = ''
  177. while True:
  178. char = self.client.recv(1)
  179. if char == '\r' and state == 'nothing':
  180. state = 'first_cr'
  181. elif char == '\n' and state == 'first_cr':
  182. state = 'first_lf'
  183. elif char == '\r' and state == 'first_lf':
  184. state = 'second_cr'
  185. elif char == '\n' and state == 'second_cr':
  186. state = 'second_lf'
  187. else:
  188. state = 'nothing'
  189. resp_read += char
  190. if state == 'second_lf':
  191. break
  192. # Handle first line
  193. line_hdrs = resp_read.splitlines()
  194. line_comp = line_hdrs[0].split()
  195. self.status = line_comp[1]
  196. del line_hdrs[0]
  197. self.encoding = ''
  198. self.content_type = ''
  199. headers = dict()
  200. # Process other headers
  201. for h in range(len(line_hdrs)):
  202. line_comp = line_hdrs[h].split(':')
  203. if line_comp[0] == 'Content-Length':
  204. self.content_len = int(line_comp[1])
  205. if line_comp[0] == 'Content-Type':
  206. self.content_type = line_comp[1].lstrip()
  207. if line_comp[0] == 'Transfer-Encoding':
  208. self.encoding = line_comp[1].lstrip()
  209. if len(line_comp) == 2:
  210. headers[line_comp[0]] = line_comp[1].lstrip()
  211. return headers
  212. except socket.error as err:
  213. self.client.close()
  214. print "Socket Error in recv :", err
  215. return None
  216. def read_resp_data(self):
  217. try:
  218. read_data = ''
  219. if self.encoding != 'chunked':
  220. while len(read_data) != self.content_len:
  221. read_data += self.client.recv(self.content_len)
  222. else:
  223. chunk_data_buf = ''
  224. while (True):
  225. # Read one character into temp buffer
  226. read_ch = self.client.recv(1)
  227. # Check CRLF
  228. if (read_ch == '\r'):
  229. read_ch = self.client.recv(1)
  230. if (read_ch == '\n'):
  231. # If CRLF decode length of chunk
  232. chunk_len = int(chunk_data_buf, 16)
  233. # Keep adding to contents
  234. self.content_len += chunk_len
  235. rem_len = chunk_len
  236. while (rem_len):
  237. new_data = self.client.recv(rem_len)
  238. read_data += new_data
  239. rem_len -= len(new_data)
  240. chunk_data_buf = ''
  241. # Fetch remaining CRLF
  242. if self.client.recv(2) != "\r\n":
  243. # Error in packet
  244. print "Error in chunked data"
  245. return None
  246. if not chunk_len:
  247. # If last chunk
  248. break
  249. continue
  250. chunk_data_buf += '\r'
  251. # If not CRLF continue appending
  252. # character to chunked data buffer
  253. chunk_data_buf += read_ch
  254. return read_data
  255. except socket.error as err:
  256. self.client.close()
  257. print "Socket Error in recv :", err
  258. return None
  259. def close(self):
  260. self.client.close()
  261. def test_val(text, expected, received):
  262. if expected != received:
  263. print " Fail!"
  264. print " [reason] " + text + ":"
  265. print " expected: " + str(expected)
  266. print " received: " + str(received)
  267. return False
  268. return True
  269. class adder_thread (threading.Thread):
  270. def __init__(self, id, dut, port):
  271. threading.Thread.__init__(self)
  272. self.id = id
  273. self.dut = dut
  274. self.depth = 3
  275. self.session = Session(dut, port)
  276. def run(self):
  277. self.response = []
  278. # Pipeline 3 requests
  279. if (_verbose_):
  280. print " Thread: Using adder start " + str(self.id)
  281. for _ in range(self.depth):
  282. self.session.send_post('/adder', str(self.id))
  283. time.sleep(2)
  284. for _ in range(self.depth):
  285. self.session.read_resp_hdrs()
  286. self.response.append(self.session.read_resp_data())
  287. def adder_result(self):
  288. if len(self.response) != self.depth:
  289. print "Error : missing response packets"
  290. return False
  291. for i in range(len(self.response)):
  292. if not test_val("Thread" + str(self.id) + " response[" + str(i) + "]",
  293. str(self.id * (i + 1)), str(self.response[i])):
  294. return False
  295. return True
  296. def close(self):
  297. self.session.close()
  298. def get_hello(dut, port):
  299. # GET /hello should return 'Hello World!'
  300. print "[test] GET /hello returns 'Hello World!' =>",
  301. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  302. conn.request("GET", "/hello")
  303. resp = conn.getresponse()
  304. if not test_val("status_code", 200, resp.status):
  305. conn.close()
  306. return False
  307. if not test_val("data", "Hello World!", resp.read()):
  308. conn.close()
  309. return False
  310. if not test_val("data", "application/json", resp.getheader('Content-Type')):
  311. conn.close()
  312. return False
  313. print "Success"
  314. conn.close()
  315. return True
  316. def put_hello(dut, port):
  317. # PUT /hello returns 405'
  318. print "[test] PUT /hello returns 405' =>",
  319. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  320. conn.request("PUT", "/hello", "Hello")
  321. resp = conn.getresponse()
  322. if not test_val("status_code", 405, resp.status):
  323. conn.close()
  324. return False
  325. print "Success"
  326. conn.close()
  327. return True
  328. def post_hello(dut, port):
  329. # POST /hello returns 405'
  330. print "[test] POST /hello returns 404' =>",
  331. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  332. conn.request("POST", "/hello", "Hello")
  333. resp = conn.getresponse()
  334. if not test_val("status_code", 405, resp.status):
  335. conn.close()
  336. return False
  337. print "Success"
  338. conn.close()
  339. return True
  340. def post_echo(dut, port):
  341. # POST /echo echoes data'
  342. print "[test] POST /echo echoes data' =>",
  343. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  344. conn.request("POST", "/echo", "Hello")
  345. resp = conn.getresponse()
  346. if not test_val("status_code", 200, resp.status):
  347. conn.close()
  348. return False
  349. if not test_val("data", "Hello", resp.read()):
  350. conn.close()
  351. return False
  352. print "Success"
  353. conn.close()
  354. return True
  355. def put_echo(dut, port):
  356. # PUT /echo echoes data'
  357. print "[test] PUT /echo echoes data' =>",
  358. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  359. conn.request("PUT", "/echo", "Hello")
  360. resp = conn.getresponse()
  361. if not test_val("status_code", 200, resp.status):
  362. conn.close()
  363. return False
  364. if not test_val("data", "Hello", resp.read()):
  365. conn.close()
  366. return False
  367. print "Success"
  368. conn.close()
  369. return True
  370. def get_echo(dut, port):
  371. # GET /echo returns 404'
  372. print "[test] GET /echo returns 405' =>",
  373. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  374. conn.request("GET", "/echo")
  375. resp = conn.getresponse()
  376. if not test_val("status_code", 405, resp.status):
  377. conn.close()
  378. return False
  379. print "Success"
  380. conn.close()
  381. return True
  382. def get_hello_type(dut, port):
  383. # GET /hello/type_html returns text/html as Content-Type'
  384. print "[test] GET /hello/type_html has Content-Type of text/html =>",
  385. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  386. conn.request("GET", "/hello/type_html")
  387. resp = conn.getresponse()
  388. if not test_val("status_code", 200, resp.status):
  389. conn.close()
  390. return False
  391. if not test_val("data", "Hello World!", resp.read()):
  392. conn.close()
  393. return False
  394. if not test_val("data", "text/html", resp.getheader('Content-Type')):
  395. conn.close()
  396. return False
  397. print "Success"
  398. conn.close()
  399. return True
  400. def get_hello_status(dut, port):
  401. # GET /hello/status_500 returns status 500'
  402. print "[test] GET /hello/status_500 returns status 500 =>",
  403. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  404. conn.request("GET", "/hello/status_500")
  405. resp = conn.getresponse()
  406. if not test_val("status_code", 500, resp.status):
  407. conn.close()
  408. return False
  409. print "Success"
  410. conn.close()
  411. return True
  412. def get_false_uri(dut, port):
  413. # GET /false_uri returns status 404'
  414. print "[test] GET /false_uri returns status 404 =>",
  415. conn = httplib.HTTPConnection(dut, int(port), timeout=15)
  416. conn.request("GET", "/false_uri")
  417. resp = conn.getresponse()
  418. if not test_val("status_code", 404, resp.status):
  419. conn.close()
  420. return False
  421. print "Success"
  422. conn.close()
  423. return True
  424. def parallel_sessions_adder(dut, port, max_sessions):
  425. # POSTs on /adder in parallel sessions
  426. print "[test] POST {pipelined} on /adder in " + str(max_sessions) + " sessions =>",
  427. t = []
  428. # Create all sessions
  429. for i in xrange(max_sessions):
  430. t.append(adder_thread(i, dut, port))
  431. for i in xrange(len(t)):
  432. t[i].start()
  433. for i in xrange(len(t)):
  434. t[i].join()
  435. res = True
  436. for i in xrange(len(t)):
  437. if not test_val("Thread" + str(i) + " Failed", t[i].adder_result(), True):
  438. res = False
  439. t[i].close()
  440. if (res):
  441. print "Success"
  442. return res
  443. def async_response_test(dut, port):
  444. # Test that an asynchronous work is executed in the HTTPD's context
  445. # This is tested by reading two responses over the same session
  446. print "[test] Test HTTPD Work Queue (Async response) =>",
  447. s = Session(dut, port)
  448. s.send_get('/async_data')
  449. s.read_resp_hdrs()
  450. if not test_val("First Response", "Hello World!", s.read_resp_data()):
  451. s.close()
  452. return False
  453. s.read_resp_hdrs()
  454. if not test_val("Second Response", "Hello Double World!", s.read_resp_data()):
  455. s.close()
  456. return False
  457. s.close()
  458. print "Success"
  459. return True
  460. def leftover_data_test(dut, port):
  461. # Leftover data in POST is purged (valid and invalid URIs)
  462. print "[test] Leftover data in POST is purged (valid and invalid URIs) =>",
  463. s = httplib.HTTPConnection(dut + ":" + port, timeout=15)
  464. s.request("POST", url='/leftover_data', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
  465. resp = s.getresponse()
  466. if not test_val("Partial data", "abcdefghij", resp.read()):
  467. s.close()
  468. return False
  469. s.request("GET", url='/hello')
  470. resp = s.getresponse()
  471. if not test_val("Hello World Data", "Hello World!", resp.read()):
  472. s.close()
  473. return False
  474. s.request("POST", url='/false_uri', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
  475. resp = s.getresponse()
  476. if not test_val("False URI Status", str(404), str(resp.status)):
  477. s.close()
  478. return False
  479. resp.read()
  480. s.request("GET", url='/hello')
  481. resp = s.getresponse()
  482. if not test_val("Hello World Data", "Hello World!", resp.read()):
  483. s.close()
  484. return False
  485. s.close()
  486. print "Success"
  487. return True
  488. def spillover_session(dut, port, max_sess):
  489. # Session max_sess_sessions + 1 is rejected
  490. print "[test] Session max_sess_sessions (" + str(max_sess) + ") + 1 is rejected =>",
  491. s = []
  492. _verbose_ = True
  493. for i in xrange(max_sess + 1):
  494. if (_verbose_):
  495. print "Executing " + str(i)
  496. try:
  497. a = httplib.HTTPConnection(dut + ":" + port, timeout=15)
  498. a.request("GET", url='/hello')
  499. resp = a.getresponse()
  500. if not test_val("Connection " + str(i), "Hello World!", resp.read()):
  501. a.close()
  502. break
  503. s.append(a)
  504. except:
  505. if (_verbose_):
  506. print "Connection " + str(i) + " rejected"
  507. a.close()
  508. break
  509. # Close open connections
  510. for a in s:
  511. a.close()
  512. # Check if number of connections is equal to max_sess
  513. print ["Fail","Success"][len(s) == max_sess]
  514. return (len(s) == max_sess)
  515. def recv_timeout_test(dut, port):
  516. print "[test] Timeout occurs if partial packet sent =>",
  517. s = Session(dut, port)
  518. s.client.sendall("GE")
  519. s.read_resp_hdrs()
  520. resp = s.read_resp_data()
  521. if not test_val("Request Timeout", "Server closed this connection", resp):
  522. s.close()
  523. return False
  524. s.close()
  525. print "Success"
  526. return True
  527. def packet_size_limit_test(dut, port, test_size):
  528. print "[test] send size limit test =>",
  529. retry = 5
  530. while (retry):
  531. retry -= 1
  532. print "data size = ", test_size
  533. s = httplib.HTTPConnection(dut + ":" + port, timeout=15)
  534. random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(test_size))
  535. path = "/echo"
  536. s.request("POST", url=path, body=random_data)
  537. resp = s.getresponse()
  538. if not test_val("Error", "200", str(resp.status)):
  539. if test_val("Error", "408", str(resp.status)):
  540. print "Data too large to be allocated"
  541. test_size = test_size/10
  542. else:
  543. print "Unexpected error"
  544. s.close()
  545. print "Retry..."
  546. continue
  547. resp = resp.read()
  548. result = (resp == random_data)
  549. if not result:
  550. test_val("Data size", str(len(random_data)), str(len(resp)))
  551. s.close()
  552. print "Retry..."
  553. continue
  554. s.close()
  555. print "Success"
  556. return True
  557. print "Failed"
  558. return False
  559. def code_500_server_error_test(dut, port):
  560. print "[test] 500 Server Error test =>",
  561. s = Session(dut, port)
  562. s.client.sendall("abcdefgh\0")
  563. s.read_resp_hdrs()
  564. resp = s.read_resp_data()
  565. # Presently server sends back 400 Bad Request
  566. #if not test_val("Server Error", "500", s.status):
  567. #s.close()
  568. #return False
  569. if not test_val("Server Error", "400", s.status):
  570. s.close()
  571. return False
  572. s.close()
  573. print "Success"
  574. return True
  575. def code_501_method_not_impl(dut, port):
  576. print "[test] 501 Method Not Implemented =>",
  577. s = Session(dut, port)
  578. path = "/hello"
  579. s.client.sendall("ABC " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
  580. s.read_resp_hdrs()
  581. resp = s.read_resp_data()
  582. # Presently server sends back 400 Bad Request
  583. #if not test_val("Server Error", "501", s.status):
  584. #s.close()
  585. #return False
  586. if not test_val("Server Error", "400", s.status):
  587. s.close()
  588. return False
  589. s.close()
  590. print "Success"
  591. return True
  592. def code_505_version_not_supported(dut, port):
  593. print "[test] 505 Version Not Supported =>",
  594. s = Session(dut, port)
  595. path = "/hello"
  596. s.client.sendall("GET " + path + " HTTP/2.0\r\nHost: " + dut + "\r\n\r\n")
  597. s.read_resp_hdrs()
  598. resp = s.read_resp_data()
  599. if not test_val("Server Error", "505", s.status):
  600. s.close()
  601. return False
  602. s.close()
  603. print "Success"
  604. return True
  605. def code_400_bad_request(dut, port):
  606. print "[test] 400 Bad Request =>",
  607. s = Session(dut, port)
  608. path = "/hello"
  609. s.client.sendall("XYZ " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
  610. s.read_resp_hdrs()
  611. resp = s.read_resp_data()
  612. if not test_val("Client Error", "400", s.status):
  613. s.close()
  614. return False
  615. s.close()
  616. print "Success"
  617. return True
  618. def code_404_not_found(dut, port):
  619. print "[test] 404 Not Found =>",
  620. s = Session(dut, port)
  621. path = "/dummy"
  622. s.client.sendall("GET " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
  623. s.read_resp_hdrs()
  624. resp = s.read_resp_data()
  625. if not test_val("Client Error", "404", s.status):
  626. s.close()
  627. return False
  628. s.close()
  629. print "Success"
  630. return True
  631. def code_405_method_not_allowed(dut, port):
  632. print "[test] 405 Method Not Allowed =>",
  633. s = Session(dut, port)
  634. path = "/hello"
  635. s.client.sendall("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
  636. s.read_resp_hdrs()
  637. resp = s.read_resp_data()
  638. if not test_val("Client Error", "405", s.status):
  639. s.close()
  640. return False
  641. s.close()
  642. print "Success"
  643. return True
  644. def code_408_req_timeout(dut, port):
  645. print "[test] 408 Request Timeout =>",
  646. s = Session(dut, port)
  647. s.client.sendall("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 10\r\n\r\nABCD")
  648. s.read_resp_hdrs()
  649. resp = s.read_resp_data()
  650. if not test_val("Client Error", "408", s.status):
  651. s.close()
  652. return False
  653. s.close()
  654. print "Success"
  655. return True
  656. def code_411_length_required(dut, port):
  657. print "[test] 411 Length Required =>",
  658. s = Session(dut, port)
  659. path = "/echo"
  660. s.client.sendall("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n")
  661. s.read_resp_hdrs()
  662. resp = s.read_resp_data()
  663. # Presently server sends back 400 Bad Request
  664. #if not test_val("Client Error", "411", s.status):
  665. #s.close()
  666. #return False
  667. if not test_val("Client Error", "400", s.status):
  668. s.close()
  669. return False
  670. s.close()
  671. print "Success"
  672. return True
  673. def send_getx_uri_len(dut, port, length):
  674. s = Session(dut, port)
  675. method = "GET "
  676. version = " HTTP/1.1\r\n"
  677. path = "/"+"x"*(length - len(method) - len(version) - len("/"))
  678. s.client.sendall(method)
  679. time.sleep(1)
  680. s.client.sendall(path)
  681. time.sleep(1)
  682. s.client.sendall(version + "Host: " + dut + "\r\n\r\n")
  683. s.read_resp_hdrs()
  684. resp = s.read_resp_data()
  685. s.close()
  686. return s.status
  687. def code_414_uri_too_long(dut, port, max_uri_len):
  688. print "[test] 414 URI Too Long =>",
  689. status = send_getx_uri_len(dut, port, max_uri_len)
  690. if not test_val("Client Error", "404", status):
  691. return False
  692. status = send_getx_uri_len(dut, port, max_uri_len + 1)
  693. if not test_val("Client Error", "414", status):
  694. return False
  695. print "Success"
  696. return True
  697. def send_postx_hdr_len(dut, port, length):
  698. s = Session(dut, port)
  699. path = "/echo"
  700. host = "Host: " + dut
  701. custom_hdr_field = "\r\nCustom: "
  702. custom_hdr_val = "x"*(length - len(host) - len(custom_hdr_field) - len("\r\n\r\n") + len("0"))
  703. request = "POST " + path + " HTTP/1.1\r\n" + host + custom_hdr_field + custom_hdr_val + "\r\n\r\n"
  704. s.client.sendall(request[:length/2])
  705. time.sleep(1)
  706. s.client.sendall(request[length/2:])
  707. hdr = s.read_resp_hdrs()
  708. resp = s.read_resp_data()
  709. s.close()
  710. if "Custom" in hdr:
  711. return (hdr["Custom"] == custom_hdr_val), resp
  712. return False, s.status
  713. def code_431_hdr_too_long(dut, port, max_hdr_len):
  714. print "[test] 431 Header Too Long =>",
  715. res, status = send_postx_hdr_len(dut, port, max_hdr_len)
  716. if not res:
  717. return False
  718. res, status = send_postx_hdr_len(dut, port, max_hdr_len + 1)
  719. if not test_val("Client Error", "431", status):
  720. return False
  721. print "Success"
  722. return True
  723. def test_upgrade_not_supported(dut, port):
  724. print "[test] Upgrade Not Supported =>",
  725. s = Session(dut, port)
  726. path = "/hello"
  727. s.client.sendall("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n");
  728. s.read_resp_hdrs()
  729. resp = s.read_resp_data()
  730. if not test_val("Client Error", "200", s.status):
  731. s.close()
  732. return False
  733. s.close()
  734. print "Success"
  735. return True
  736. if __name__ == '__main__':
  737. ########### Execution begins here...
  738. # Configuration
  739. # Max number of threads/sessions
  740. max_sessions = 7
  741. max_uri_len = 512
  742. max_hdr_len = 512
  743. parser = argparse.ArgumentParser(description='Run HTTPD Test')
  744. parser.add_argument('-4','--ipv4', help='IPv4 address')
  745. parser.add_argument('-6','--ipv6', help='IPv6 address')
  746. parser.add_argument('-p','--port', help='Port')
  747. args = vars(parser.parse_args())
  748. dut4 = args['ipv4']
  749. dut6 = args['ipv6']
  750. port = args['port']
  751. dut = dut4
  752. _verbose_ = True
  753. print "### Basic HTTP Client Tests"
  754. get_hello(dut, port)
  755. post_hello(dut, port)
  756. put_hello(dut, port)
  757. post_echo(dut, port)
  758. get_echo(dut, port)
  759. put_echo(dut, port)
  760. get_hello_type(dut, port)
  761. get_hello_status(dut, port)
  762. get_false_uri(dut, port)
  763. print "### Error code tests"
  764. code_500_server_error_test(dut, port)
  765. code_501_method_not_impl(dut, port)
  766. code_505_version_not_supported(dut, port)
  767. code_400_bad_request(dut, port)
  768. code_404_not_found(dut, port)
  769. code_405_method_not_allowed(dut, port)
  770. code_408_req_timeout(dut, port)
  771. code_414_uri_too_long(dut, port, max_uri_len)
  772. code_431_hdr_too_long(dut, port, max_hdr_len)
  773. test_upgrade_not_supported(dut, port)
  774. # Not supported yet (Error on chunked request)
  775. ###code_411_length_required(dut, port)
  776. print "### Sessions and Context Tests"
  777. parallel_sessions_adder(dut, port, max_sessions)
  778. leftover_data_test(dut, port)
  779. async_response_test(dut, port)
  780. spillover_session(dut, port, max_sessions)
  781. recv_timeout_test(dut, port)
  782. packet_size_limit_test(dut, port, 50*1024)
  783. get_hello(dut, port)
  784. sys.exit()