client.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #!/usr/bin/env python
  2. #
  3. # SPDX-FileCopyrightText: 2018-2022 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. import argparse
  6. import errno
  7. import http.client
  8. import logging
  9. def verbose_print(verbosity, *args):
  10. if (verbosity):
  11. logging.info(''.join(str(elems) for elems in args))
  12. def test_val(text, expected, received):
  13. if expected != received:
  14. logging.info(' Fail!')
  15. logging.info(' [reason] {} :'.format(text))
  16. logging.info(' expected: {}'.format(expected))
  17. logging.info(' received: {}'.format(received))
  18. return False
  19. return True
  20. def test_get_handler(ip, port, verbosity=False):
  21. verbose_print(verbosity, '======== GET HANDLER TEST =============')
  22. # Establish HTTP connection
  23. verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
  24. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  25. uri = '/hello?query1=value1&query2=value2&query3=value3'
  26. # GET hello response
  27. test_headers = {'Test-Header-1':'Test-Value-1', 'Test-Header-2':'Test-Value-2'}
  28. verbose_print(verbosity, 'Sending GET to URI : ', uri)
  29. verbose_print(verbosity, 'Sending additional headers : ')
  30. for k, v in test_headers.items():
  31. verbose_print(verbosity, '\t', k, ': ', v)
  32. sess.request('GET', url=uri, headers=test_headers)
  33. resp = sess.getresponse()
  34. resp_hdrs = resp.getheaders()
  35. resp_data = resp.read().decode()
  36. # Close HTTP connection
  37. sess.close()
  38. if not (
  39. test_val('Status code mismatch', 200, resp.status) and
  40. test_val('Response mismatch', 'Custom-Value-1', resp.getheader('Custom-Header-1')) and
  41. test_val('Response mismatch', 'Custom-Value-2', resp.getheader('Custom-Header-2')) and
  42. test_val('Response mismatch', 'Hello World!', resp_data)
  43. ):
  44. return False
  45. verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
  46. verbose_print(verbosity, 'Server response to GET /hello')
  47. verbose_print(verbosity, 'Response Headers : ')
  48. for k, v in resp_hdrs:
  49. verbose_print(verbosity, '\t', k, ': ', v)
  50. verbose_print(verbosity, 'Response Data : ' + resp_data)
  51. verbose_print(verbosity, '========================================\n')
  52. return True
  53. def test_post_handler(ip, port, msg, verbosity=False):
  54. verbose_print(verbosity, '======== POST HANDLER TEST ============')
  55. # Establish HTTP connection
  56. verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
  57. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  58. # POST message to /echo and get back response
  59. sess.request('POST', url='/echo', body=msg)
  60. resp = sess.getresponse()
  61. resp_data = resp.read().decode()
  62. verbose_print(verbosity, 'Server response to POST /echo (' + msg + ')')
  63. verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
  64. verbose_print(verbosity, resp_data)
  65. verbose_print(verbosity, '========================================\n')
  66. # Close HTTP connection
  67. sess.close()
  68. return test_val('Response mismatch', msg, resp_data)
  69. def test_put_handler(ip, port, verbosity=False):
  70. verbose_print(verbosity, '======== PUT HANDLER TEST =============')
  71. # Establish HTTP connection
  72. verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
  73. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  74. # PUT message to /ctrl to disable /hello and /echo URI handlers
  75. # and set 404 error handler to custom http_404_error_handler()
  76. verbose_print(verbosity, 'Disabling /hello and /echo handlers')
  77. sess.request('PUT', url='/ctrl', body='0')
  78. resp = sess.getresponse()
  79. resp.read()
  80. try:
  81. # Send HTTP request to /hello URI
  82. sess.request('GET', url='/hello')
  83. resp = sess.getresponse()
  84. resp_data = resp.read().decode()
  85. # 404 Error must be returned from server as URI /hello is no longer available.
  86. # But the custom error handler http_404_error_handler() will not close the
  87. # session if the requested URI is /hello
  88. if not test_val('Status code mismatch', 404, resp.status):
  89. raise AssertionError
  90. # Compare error response string with expectation
  91. verbose_print(verbosity, 'Response on GET /hello : ' + resp_data)
  92. if not test_val('Response mismatch', '/hello URI is not available', resp_data):
  93. raise AssertionError
  94. # Using same session for sending an HTTP request to /echo, as it is expected
  95. # that the custom error handler http_404_error_handler() would not have closed
  96. # the session
  97. sess.request('POST', url='/echo', body='Some content')
  98. resp = sess.getresponse()
  99. resp_data = resp.read().decode()
  100. # 404 Error must be returned from server as URI /hello is no longer available.
  101. # The custom error handler http_404_error_handler() will close the session
  102. # this time as the requested URI is /echo
  103. if not test_val('Status code mismatch', 404, resp.status):
  104. raise AssertionError
  105. # Compare error response string with expectation
  106. verbose_print(verbosity, 'Response on POST /echo : ' + resp_data)
  107. if not test_val('Response mismatch', '/echo URI is not available', resp_data):
  108. raise AssertionError
  109. try:
  110. # Using same session should fail as by now the session would have closed
  111. sess.request('POST', url='/hello', body='Some content')
  112. resp = sess.getresponse()
  113. resp.read().decode()
  114. # If control reaches this point then the socket was not closed.
  115. # This is not expected
  116. verbose_print(verbosity, 'Socket not closed by server')
  117. raise AssertionError
  118. except http.client.HTTPException:
  119. # Catch socket error as we tried to communicate with an already closed socket
  120. pass
  121. except IOError as err:
  122. if err.errno == errno.EPIPE:
  123. # Sometimes Broken Pipe error is returned
  124. # when sending data to a closed socket
  125. pass
  126. except http.client.HTTPException:
  127. verbose_print(verbosity, 'Socket closed by server')
  128. return False
  129. except AssertionError:
  130. return False
  131. finally:
  132. # Close HTTP connection
  133. sess.close()
  134. verbose_print(verbosity, 'Enabling /hello handler')
  135. # Create new connection
  136. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  137. # PUT message to /ctrl to enable /hello URI handler
  138. # and restore 404 error handler to default
  139. sess.request('PUT', url='/ctrl', body='1')
  140. resp = sess.getresponse()
  141. resp.read()
  142. # Close HTTP connection
  143. sess.close()
  144. # Create new connection
  145. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  146. try:
  147. # Sending HTTP request to /hello should work now
  148. sess.request('GET', url='/hello')
  149. resp = sess.getresponse()
  150. resp_data = resp.read().decode()
  151. if not test_val('Status code mismatch', 200, resp.status):
  152. raise AssertionError
  153. verbose_print(verbosity, 'Response on GET /hello : ' + resp_data)
  154. if not test_val('Response mismatch', 'Hello World!', resp_data):
  155. raise AssertionError
  156. # 404 Error handler should have been restored to default
  157. sess.request('GET', url='/invalid')
  158. resp = sess.getresponse()
  159. resp_data = resp.read().decode()
  160. if not test_val('Status code mismatch', 404, resp.status):
  161. raise AssertionError
  162. verbose_print(verbosity, 'Response on GET /invalid : ' + resp_data)
  163. if not test_val('Response mismatch', 'Nothing matches the given URI', resp_data):
  164. raise AssertionError
  165. except http.client.HTTPException:
  166. verbose_print(verbosity, 'Socket closed by server')
  167. return False
  168. except AssertionError:
  169. return False
  170. finally:
  171. # Close HTTP connection
  172. sess.close()
  173. return True
  174. def test_custom_uri_query(ip, port, query, verbosity=False):
  175. verbose_print(verbosity, '======== GET HANDLER TEST =============')
  176. # Establish HTTP connection
  177. verbose_print(verbosity, 'Connecting to => ' + ip + ':' + port)
  178. sess = http.client.HTTPConnection(ip + ':' + port, timeout=15)
  179. uri = '/hello?' + query
  180. # GET hello response
  181. verbose_print(verbosity, 'Sending GET to URI : ', uri)
  182. sess.request('GET', url=uri, headers={})
  183. resp = sess.getresponse()
  184. resp_data = resp.read().decode()
  185. verbose_print(verbosity, 'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv')
  186. verbose_print(verbosity, 'Server response to GET /hello')
  187. verbose_print(verbosity, 'Response Data : ' + resp_data)
  188. verbose_print(verbosity, '========================================\n')
  189. # Close HTTP connection
  190. sess.close()
  191. return 'Hello World!' == resp_data
  192. if __name__ == '__main__':
  193. # Configure argument parser
  194. parser = argparse.ArgumentParser(description='Run HTTPd Test')
  195. parser.add_argument('IP', metavar='IP', type=str, help='Server IP')
  196. parser.add_argument('port', metavar='port', type=str, help='Server port')
  197. parser.add_argument('msg', metavar='message', type=str, help='Message to be sent to server')
  198. args = vars(parser.parse_args())
  199. # Get arguments
  200. ip = args['IP']
  201. port = args['port']
  202. msg = args['msg']
  203. if not (
  204. test_get_handler(ip, port, True) and
  205. test_put_handler(ip, port, True) and
  206. test_post_handler(ip, port, msg, True)
  207. ):
  208. logging.info('Failed!')