v / vlib / net / mbedtls / mbedtls_concurrent_large_response_test.v
222 lines · 199 sloc · 11.53 KB · f59323023d4e8a9487098049c4828021534a7ff4
Raw
1// vtest flaky: true
2// vtest retry: 3
3module main
4
5import net
6import net.html
7import net.http
8import net.mbedtls
9import sync.pool
10import time
11
12const concurrent_large_response_requests = 6
13const concurrent_large_response_workers = 3
14const concurrent_large_response_links = 2_000
15const concurrent_large_response_chunk_size = 8_192
16const concurrent_large_response_test_cert = '-----BEGIN CERTIFICATE-----\nMIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp6MA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwODAyMTcyOTQyWhgPMjA1MDEyMTcx\nNzI5NDJaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD\nVQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALqAI4fqUi+QBVWcsXglouLdOML5+w0+1hSR1KdO0Q5XPdQAs/yYWJ+KUkDw\nG++rfy9DUPq7FNRBVurXQkcAtn6gXdllGUSjwUiDo/N4mMOyS/2sufBuaeww7jVi\nrppH+zwP1tUnjRd6khl6bi1Ian9VSzr3Iy9CkXIg1GU4CPXkOydLeoQfepXxWoK1\nOUNwT3VKC/stAfY3j/NIIeiJYkyuRGFCkxn/BUjN+AsXiTugRcYKEFHdIPkOuCXp\nYbhf+lLsczpxCs3rdZG9b/N6mEDCzXTmeHkmsjdPTf+1k5DZZvKzVBBrgdxCgBb7\n5RwjF5v9WmnIc33wWgfJC6FaUzj9NYxYUbPHD+jTz0rJB/jj4u/xJlM/e5NRmXdW\n70pOMKXtWjRSolLOFIPKLY1qs3KMTAZxKKWPDDF7WlMJxMRt7nnnks5yw43Nog4C\njDLk1ZgETnPpLgo3jbmJdIv+OHKTJrBlVvDq7VTyixCoS5G8KoOmyQJhaXG6NwE2\niVhH5JIKgzgCfetfDsnjxqJ/qtrFXPa8FF2TsomD0NK/GZmIcs+9OeVB75Jn5uhF\nfLHScpiTbuu5w3P/LI/MqihLRB6RRNnRzPH8fIg5bYC9b770ta/8GcFRuYE8t+UR\nGtqXJoIKixbDlqV54kal8FQzYzhETf9+NM6Kb/lKEfG/pslvAgMBAAEwDQYJKoZI\nhvcNAQELBQADggEBALI3uNiNO0QE1brA3QYFK+d9ZroB72NrJ0UNkzYHDg2Fc6xg\n4aVVfaxY08+TmKc0JlMOW+pUxeCW/+UBSngdQiR9EE9xm0k0XIrAsy9RXxRvEtPu\nM1VI2h7ayp1Y2BrnQinevTSgtqLRyS1VbOFRl1FiyVvinw2I0KsDdAMNevAPXcOa\nQ8pUgUq6f56DkhocQaj+hxD/uV8HryNxuoSXnPhvfTN3z4YRGzsaWevJ9EYJliOM\n+XugcqfFJ+W7/QCEcAHCL+Bw6OydG5NFORr3p57PXjjcL/uKmxPBrWg2Bz6uT4uR\nMhj0zttiFHLAt9jGfyk6W57UNUja1e1ggftJJhs=\n-----END CERTIFICATE-----\n'
17const concurrent_large_response_test_key = '-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n'
18
19struct ConcurrentLargeResponseContext {
20 expected_body string
21 expected_link_count int
22}
23
24struct ConcurrentLargeResponseResult {
25 status_code int
26 body_matches bool
27 link_count int
28 err string
29}
30
31fn test_https_large_bodies_can_be_processed_concurrently() {
32 // Watchdog thread to prevent the test from hanging indefinitely on CI.
33 spawn fn () {
34 time.sleep(120 * time.second)
35 eprintln('mbedtls concurrent large response test: timeout reached, exiting.')
36 exit(1)
37 }()
38
39 mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
40 port := port_listener.addr()!.port()!
41 port_listener.close()!
42
43 mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
44 cert: concurrent_large_response_test_cert
45 cert_key: concurrent_large_response_test_key
46 validate: false
47 in_memory_verification: true
48 })!
49 server := spawn serve_large_html_responses(mut listener, concurrent_large_response_body(),
50 concurrent_large_response_requests)
51
52 mut urls := []string{cap: concurrent_large_response_requests}
53 for i in 0 .. concurrent_large_response_requests {
54 urls << 'https://127.0.0.1:${port}/?req=${i}'
55 }
56 expected_body := concurrent_large_response_body()
57 mut pp := pool.new_pool_processor(callback: concurrent_large_response_worker)
58 pp.set_max_jobs(concurrent_large_response_workers)
59 pp.set_shared_context(&ConcurrentLargeResponseContext{
60 expected_body: expected_body
61 expected_link_count: concurrent_large_response_links
62 })
63 pp.work_on_items(urls)
64 server.wait()
65
66 for result in pp.get_results_ref[ConcurrentLargeResponseResult]() {
67 assert result.err == ''
68 assert result.status_code == 200
69 assert result.body_matches
70 assert result.link_count == concurrent_large_response_links
71 }
72}
73
74fn test_https_request_timeout_closes_the_connection() {
75 mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
76 port := port_listener.addr()!.port()!
77 port_listener.close()!
78
79 mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
80 cert: concurrent_large_response_test_cert
81 cert_key: concurrent_large_response_test_key
82 validate: false
83 in_memory_verification: true
84 })!
85 server := spawn serve_incomplete_https_response(mut listener)
86
87 mut req := http.new_request(.get, 'https://127.0.0.1:${port}', '')
88 req.read_timeout = 250 * time.millisecond
89 req.validate = false
90 req.do() or {
91 assert server.wait()
92 return
93 }
94 panic('expected the HTTPS request to time out')
95}
96
97fn test_https_chunked_body_is_returned_by_http_get_text() {
98 mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
99 port := port_listener.addr()!.port()!
100 port_listener.close()!
101
102 mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
103 cert: concurrent_large_response_test_cert
104 cert_key: concurrent_large_response_test_key
105 validate: false
106 in_memory_verification: true
107 })!
108 expected_body := concurrent_large_response_body()
109 server := spawn serve_chunked_html_response(mut listener, expected_body)
110
111 body := http.get_text('https://127.0.0.1:${port}/chunked')
112 server.wait()
113
114 assert body == expected_body
115}
116
117fn concurrent_large_response_body() string {
118 return '<html><body>' +
119 '<article><a href="/item">payload</a></article>'.repeat(concurrent_large_response_links) +
120 '</body></html>'
121}
122
123fn serve_incomplete_https_response(mut listener mbedtls.SSLListener) bool {
124 defer {
125 listener.shutdown() or {}
126 }
127 mut conn := listener.accept() or { panic(err) }
128 defer {
129 conn.shutdown() or {}
130 }
131 mut request_buf := []u8{len: 2048}
132 _ = conn.read(mut request_buf) or { return false }
133 conn.write_string('HTTP/1.1 200 OK\r\nContent-Length: 2\r\n') or { return false }
134 conn.set_read_timeout(time.second)
135 mut drain_buf := []u8{len: 128}
136 _ = conn.read(mut drain_buf) or { return err.code() != net.err_timed_out_code }
137 return false
138}
139
140fn serve_large_html_responses(mut listener mbedtls.SSLListener, body string, request_count int) {
141 defer {
142 listener.shutdown() or {}
143 }
144 mut handlers := []thread{cap: request_count}
145 for _ in 0 .. request_count {
146 mut conn := listener.accept() or { panic(err) }
147 handlers << spawn write_large_html_response(mut conn, body)
148 }
149 handlers.wait()
150}
151
152fn serve_chunked_html_response(mut listener mbedtls.SSLListener, body string) {
153 defer {
154 listener.shutdown() or {}
155 }
156 mut conn := listener.accept() or { panic(err) }
157 write_chunked_html_response(mut conn, body)
158}
159
160fn write_large_html_response(mut conn mbedtls.SSLConn, body string) {
161 defer {
162 conn.shutdown() or {}
163 }
164 mut request_buf := []u8{len: 2048}
165 _ = conn.read(mut request_buf) or { return }
166 header := 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ${body.len}\r\nConnection: close\r\n\r\n'
167 conn.write_string(header) or { return }
168 mut start := 0
169 for start < body.len {
170 end := if start + concurrent_large_response_chunk_size > body.len {
171 body.len
172 } else {
173 start + concurrent_large_response_chunk_size
174 }
175 conn.write_string(body[start..end]) or { return }
176 start = end
177 time.sleep(1 * time.millisecond)
178 }
179}
180
181fn write_chunked_html_response(mut conn mbedtls.SSLConn, body string) {
182 defer {
183 conn.shutdown() or {}
184 }
185 mut request_buf := []u8{len: 2048}
186 _ = conn.read(mut request_buf) or { return }
187 header := 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n'
188 conn.write_string(header) or { return }
189 mut start := 0
190 for start < body.len {
191 end := if start + concurrent_large_response_chunk_size > body.len {
192 body.len
193 } else {
194 start + concurrent_large_response_chunk_size
195 }
196 chunk := body[start..end]
197 conn.write_string('${chunk.len:x}\r\n') or { return }
198 conn.write_string(chunk) or { return }
199 conn.write_string('\r\n') or { return }
200 start = end
201 time.sleep(1 * time.millisecond)
202 }
203 conn.write_string('0\r\n\r\n') or { return }
204}
205
206fn concurrent_large_response_worker(mut pp pool.PoolProcessor, idx int, _wid int) &ConcurrentLargeResponseResult {
207 url := pp.get_item[string](idx)
208 ctx := unsafe { &ConcurrentLargeResponseContext(pp.get_shared_context()) }
209 resp := http.fetch(
210 method: .get
211 url: url
212 validate: false
213 ) or { return &ConcurrentLargeResponseResult{
214 err: '${err}'
215 } }
216 doc := html.parse(resp.body)
217 return &ConcurrentLargeResponseResult{
218 status_code: resp.status_code
219 body_matches: resp.body == ctx.expected_body
220 link_count: doc.get_tags(name: 'a').len
221 }
222}
223