pytest_https_request.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: Unlicense OR CC0-1.0
  3. import http.server
  4. import logging
  5. import multiprocessing
  6. import os
  7. import socket
  8. import ssl
  9. from typing import Callable
  10. import pexpect
  11. import pytest
  12. from pytest_embedded import Dut
  13. from RangeHTTPServer import RangeRequestHandler
  14. def get_my_ip() -> str:
  15. s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  16. s1.connect(('8.8.8.8', 80))
  17. my_ip = ''
  18. my_ip = s1.getsockname()[0]
  19. s1.close()
  20. return my_ip
  21. def get_server_status(host_ip: str, port: int) -> bool:
  22. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  23. server_status = sock.connect_ex((host_ip, port))
  24. sock.close()
  25. if server_status == 0:
  26. return True
  27. return False
  28. def https_request_handler() -> Callable[...,http.server.BaseHTTPRequestHandler]:
  29. """
  30. Returns a request handler class that handles broken pipe exception
  31. """
  32. class RequestHandler(RangeRequestHandler):
  33. protocol_version = 'HTTP/1.1'
  34. def finish(self) -> None:
  35. try:
  36. if not self.wfile.closed:
  37. self.wfile.flush()
  38. self.wfile.close()
  39. except socket.error:
  40. pass
  41. self.rfile.close()
  42. def handle(self) -> None:
  43. try:
  44. RangeRequestHandler.handle(self)
  45. except socket.error:
  46. pass
  47. def do_GET(self) -> None:
  48. self.close_connection = True
  49. self.send_response(200)
  50. self.end_headers()
  51. return RequestHandler
  52. def start_https_server(server_file: str, key_file: str, server_ip: str, server_port: int) -> None:
  53. requestHandler = https_request_handler()
  54. httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
  55. httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=key_file,
  56. certfile=server_file, server_side=True)
  57. httpd.serve_forever()
  58. @pytest.mark.esp32
  59. @pytest.mark.esp32c3
  60. @pytest.mark.esp32s2
  61. @pytest.mark.esp32s3
  62. @pytest.mark.ethernet
  63. @pytest.mark.parametrize('config', ['cli_ses_tkt',], indirect=True)
  64. def test_examples_protocol_https_request_cli_session_tickets(dut: Dut) -> None:
  65. logging.info("Testing for \"esp_tls client session tickets\"")
  66. # check and log bin size
  67. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  68. bin_size = os.path.getsize(binary_file)
  69. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  70. # start test
  71. host_ip = get_my_ip()
  72. server_port = 8070
  73. server_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_cert.pem')
  74. key_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_key.pem')
  75. if (get_server_status(host_ip, server_port) is False):
  76. thread1 = multiprocessing.Process(target=start_https_server, args=(server_file, key_file, host_ip, server_port))
  77. thread1.daemon = True
  78. thread1.start()
  79. logging.info('The server started on {}:{}'.format(host_ip, server_port))
  80. dut.expect('Loaded app from partition at offset', timeout=30)
  81. try:
  82. ip_address = dut.expect(r' (sta|eth) ip: (\d+\.\d+\.\d+\.\d+)', timeout=60)[2].decode()
  83. print('Connected to AP with IP: {}'.format(ip_address))
  84. except pexpect.exceptions.TIMEOUT:
  85. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  86. dut.expect('Start https_request example', timeout=30)
  87. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port)))
  88. dut.write('https://' + host_ip + ':' + str(server_port))
  89. logging.info("Testing for \"https_request using saved session\"")
  90. # Check for connection using already saved client session
  91. try:
  92. dut.expect('https_request to local server', timeout=30)
  93. dut.expect(['Connection established...',
  94. 'Reading HTTP response...',
  95. 'HTTP/1.1 200 OK',
  96. 'connection closed'], expect_all=True)
  97. except Exception:
  98. logging.info("Failed to connect to local https server\"")
  99. raise
  100. try:
  101. dut.expect('https_request using saved client session', timeout=20)
  102. dut.expect(['Connection established...',
  103. 'Reading HTTP response...',
  104. 'HTTP/1.1 200 OK',
  105. 'connection closed'], expect_all=True)
  106. except Exception:
  107. logging.info("Failed the test for \"https_request using saved client session\"")
  108. raise
  109. logging.info("Passed the test for \"https_request using saved client session\"")
  110. thread1.terminate()
  111. @pytest.mark.esp32
  112. @pytest.mark.esp32c3
  113. @pytest.mark.esp32s2
  114. @pytest.mark.esp32s3
  115. @pytest.mark.ethernet
  116. @pytest.mark.parametrize('config', ['ssldyn',], indirect=True)
  117. def test_examples_protocol_https_request_dynamic_buffers(dut: Dut) -> None:
  118. # Check for connection using crt bundle with mbedtls dynamic resource enabled
  119. # check and log bin size
  120. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  121. bin_size = os.path.getsize(binary_file)
  122. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  123. dut.expect('Loaded app from partition at offset', timeout=30)
  124. try:
  125. ip_address = dut.expect(r' (sta|eth) ip: (\d+\.\d+\.\d+\.\d+)', timeout=60)[2].decode()
  126. print('Connected to AP with IP: {}'.format(ip_address))
  127. except pexpect.exceptions.TIMEOUT:
  128. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  129. # only check if one connection is established
  130. logging.info("Testing for \"https_request using crt bundle\" with mbedtls dynamic resource enabled")
  131. try:
  132. dut.expect('https_request using crt bundle', timeout=30)
  133. dut.expect(['Connection established...',
  134. 'Reading HTTP response...',
  135. 'HTTP/1.1 200 OK',
  136. 'connection closed'], expect_all=True)
  137. except Exception:
  138. logging.info("Failed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled")
  139. raise
  140. logging.info("Passed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled")
  141. @pytest.mark.supported_targets
  142. @pytest.mark.ethernet
  143. def test_examples_protocol_https_request(dut: Dut) -> None:
  144. """
  145. steps: |
  146. 1. join AP
  147. 2. establish TLS connection to www.howsmyssl.com:443 with multiple
  148. certificate verification options
  149. 3. send http request
  150. """
  151. # check and log bin size
  152. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  153. bin_size = os.path.getsize(binary_file)
  154. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  155. logging.info('Starting https_request simple test app')
  156. dut.expect('Loaded app from partition at offset', timeout=30)
  157. try:
  158. ip_address = dut.expect(r' (sta|eth) ip: (\d+\.\d+\.\d+\.\d+)', timeout=60)[2].decode()
  159. print('Connected to AP with IP: {}'.format(ip_address))
  160. except pexpect.exceptions.TIMEOUT:
  161. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  162. # Check for connection using crt bundle
  163. logging.info("Testing for \"https_request using crt bundle\"")
  164. try:
  165. dut.expect('https_request using crt bundle', timeout=30)
  166. dut.expect(['Certificate validated',
  167. 'Connection established...',
  168. 'Reading HTTP response...',
  169. 'HTTP/1.1 200 OK',
  170. 'connection closed'], expect_all=True)
  171. except Exception:
  172. logging.info("Failed the test for \"https_request using crt bundle\"")
  173. raise
  174. logging.info("Passed the test for \"https_request using crt bundle\"")
  175. # Check for connection using cacert_buf
  176. logging.info("Testing for \"https_request using cacert_buf\"")
  177. try:
  178. dut.expect('https_request using cacert_buf', timeout=20)
  179. dut.expect(['Connection established...',
  180. 'Reading HTTP response...',
  181. 'HTTP/1.1 200 OK',
  182. 'connection closed'], expect_all=True)
  183. except Exception:
  184. logging.info("Passed the test for \"https_request using cacert_buf\"")
  185. raise
  186. logging.info("Passed the test for \"https_request using cacert_buf\"")
  187. # Check for connection using global ca_store
  188. logging.info("Testing for \"https_request using global ca_store\"")
  189. try:
  190. dut.expect('https_request using global ca_store', timeout=20)
  191. dut.expect(['Connection established...',
  192. 'Reading HTTP response...',
  193. 'HTTP/1.1 200 OK',
  194. 'connection closed'], expect_all=True)
  195. except Exception:
  196. logging.info("Failed the test for \"https_request using global ca_store\"")
  197. raise
  198. logging.info("Passed the test for \"https_request using global ca_store\"")