pytest_https_request.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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. def test_examples_protocol_https_request_cli_session_tickets(dut: Dut) -> None:
  52. logging.info("Testing for \"esp_tls client session tickets\"")
  53. # check and log bin size
  54. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  55. bin_size = os.path.getsize(binary_file)
  56. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  57. # start https server
  58. server_port = 8070
  59. server_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_cert.pem')
  60. key_file = os.path.join(os.path.dirname(__file__), 'main', 'local_server_key.pem')
  61. thread1 = multiprocessing.Process(target=start_https_server, args=(server_file, key_file, '0.0.0.0', server_port))
  62. thread1.daemon = True
  63. thread1.start()
  64. logging.info('The server started on localhost:{}'.format(server_port))
  65. try:
  66. # start test
  67. dut.expect('Loaded app from partition at offset', timeout=30)
  68. try:
  69. ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
  70. print('Connected to AP/Ethernet with IP: {}'.format(ip_address))
  71. except pexpect.exceptions.TIMEOUT:
  72. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
  73. host_ip = get_host_ip4_by_dest_ip(ip_address)
  74. dut.expect('Start https_request example', timeout=30)
  75. print('writing to device: {}'.format('https://' + host_ip + ':' + str(server_port)))
  76. dut.write('https://' + host_ip + ':' + str(server_port))
  77. logging.info("Testing for \"https_request using saved session\"")
  78. # Check for connection using already saved client session
  79. try:
  80. dut.expect('https_request to local server', timeout=30)
  81. dut.expect(['Connection established...',
  82. 'Reading HTTP response...',
  83. 'HTTP/1.1 200 OK',
  84. 'connection closed'], expect_all=True)
  85. except Exception:
  86. logging.info("Failed to connect to local https server\"")
  87. raise
  88. try:
  89. dut.expect('https_request using saved client session', timeout=20)
  90. dut.expect(['Connection established...',
  91. 'Reading HTTP response...',
  92. 'HTTP/1.1 200 OK',
  93. 'connection closed'], expect_all=True)
  94. except Exception:
  95. logging.info("Failed the test for \"https_request using saved client session\"")
  96. raise
  97. logging.info("Passed the test for \"https_request using saved client session\"")
  98. finally:
  99. thread1.terminate()
  100. @pytest.mark.esp32
  101. @pytest.mark.esp32c3
  102. @pytest.mark.esp32s2
  103. @pytest.mark.esp32s3
  104. @pytest.mark.ethernet
  105. @pytest.mark.parametrize('config', ['ssldyn',], indirect=True)
  106. def test_examples_protocol_https_request_dynamic_buffers(dut: Dut) -> None:
  107. # Check for connection using crt bundle with mbedtls dynamic resource enabled
  108. # check and log bin size
  109. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  110. bin_size = os.path.getsize(binary_file)
  111. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  112. dut.expect('Loaded app from partition at offset', timeout=30)
  113. try:
  114. ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
  115. print('Connected to AP/Ethernet with IP: {}'.format(ip_address))
  116. except pexpect.exceptions.TIMEOUT:
  117. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet')
  118. # only check if one connection is established
  119. logging.info("Testing for \"https_request using crt bundle\" with mbedtls dynamic resource enabled")
  120. try:
  121. dut.expect('https_request using crt bundle', timeout=30)
  122. dut.expect(['Connection established...',
  123. 'Reading HTTP response...',
  124. 'HTTP/1.1 200 OK',
  125. 'connection closed'], expect_all=True)
  126. except Exception:
  127. logging.info("Failed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled")
  128. raise
  129. logging.info("Passed the test for \"https_request using crt bundle\" when mbedtls dynamic resource was enabled")
  130. @pytest.mark.supported_targets
  131. @pytest.mark.ethernet
  132. def test_examples_protocol_https_request(dut: Dut) -> None:
  133. """
  134. steps: |
  135. 1. join AP
  136. 2. establish TLS connection to www.howsmyssl.com:443 with multiple
  137. certificate verification options
  138. 3. send http request
  139. """
  140. # check and log bin size
  141. binary_file = os.path.join(dut.app.binary_path, 'https_request.bin')
  142. bin_size = os.path.getsize(binary_file)
  143. logging.info('https_request_bin_size : {}KB'.format(bin_size // 1024))
  144. logging.info('Starting https_request simple test app')
  145. dut.expect('Loaded app from partition at offset', timeout=30)
  146. try:
  147. ip_address = dut.expect(r'IPv4 address: (\d+\.\d+\.\d+\.\d+)[^\d]', timeout=60)[1].decode()
  148. print('Connected to AP/Ethernet with IP: {}'.format(ip_address))
  149. except pexpect.exceptions.TIMEOUT:
  150. raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP/Ethernet')
  151. # Check for connection using crt bundle
  152. logging.info("Testing for \"https_request using crt bundle\"")
  153. try:
  154. dut.expect('https_request using crt bundle', timeout=30)
  155. dut.expect(['Certificate validated',
  156. 'Connection established...',
  157. 'Reading HTTP response...',
  158. 'HTTP/1.1 200 OK',
  159. 'connection closed'], expect_all=True)
  160. except Exception:
  161. logging.info("Failed the test for \"https_request using crt bundle\"")
  162. raise
  163. logging.info("Passed the test for \"https_request using crt bundle\"")
  164. # Check for connection using cacert_buf
  165. logging.info("Testing for \"https_request using cacert_buf\"")
  166. try:
  167. dut.expect('https_request using cacert_buf', timeout=20)
  168. dut.expect(['Connection established...',
  169. 'Reading HTTP response...',
  170. 'HTTP/1.1 200 OK',
  171. 'connection closed'], expect_all=True)
  172. except Exception:
  173. logging.info("Passed the test for \"https_request using cacert_buf\"")
  174. raise
  175. logging.info("Passed the test for \"https_request using cacert_buf\"")
  176. # Check for connection using global ca_store
  177. logging.info("Testing for \"https_request using global ca_store\"")
  178. try:
  179. dut.expect('https_request using global ca_store', timeout=20)
  180. dut.expect(['Connection established...',
  181. 'Reading HTTP response...',
  182. 'HTTP/1.1 200 OK',
  183. 'connection closed'], expect_all=True)
  184. except Exception:
  185. logging.info("Failed the test for \"https_request using global ca_store\"")
  186. raise
  187. logging.info("Passed the test for \"https_request using global ca_store\"")