pytest_https_request.py 8.8 KB

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