v4 / vlib / net / http / server_tls_test.v
687 lines · 659 sloc · 23.03 KB · 6e2d911004257b2b6384e3a12ad0c050275f4ed6
Raw
1// vtest build: !sanitize-memory-clang
2// Hermetic TLS-termination test for net.http.Server: spin up a local HTTPS
3// server with an in-memory cert/key, hit it with http.fetch (validate: false),
4// and assert the round-trip.
5
6module main
7
8import net
9import net.http
10import net.mbedtls
11import time
12
13const server_tls_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'
14
15const server_tls_key = '-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n'
16
17struct EchoHandler {
18mut:
19 last_path string
20}
21
22fn (mut h EchoHandler) handle(req http.Request) http.Response {
23 h.last_path = req.url
24 return http.Response{
25 status_code: 200
26 body: 'tls hello ${req.url}'
27 }
28}
29
30struct BlockingHandler {
31 started chan bool
32 release chan bool
33}
34
35fn testsuite_end() {
36 http.close_idle_connections()
37}
38
39fn (mut h BlockingHandler) handle(req http.Request) http.Response {
40 h.started <- true
41 _ := <-h.release
42 return http.Response{
43 status_code: 200
44 header: http.new_header(key: .connection, value: 'close')
45 body: 'released'
46 }
47}
48
49fn pick_port() !int {
50 mut l := net.listen_tcp(.ip, '127.0.0.1:0')!
51 port := l.addr()!.port()!
52 l.close()!
53 return port
54}
55
56fn test_server_tls_round_trip() {
57 $if use_openssl ? {
58 // TLS termination for net.http.Server is not yet supported on the
59 // OpenSSL backend; the listener stub reports a clear runtime error and
60 // the test is skipped here so the suite stays green under
61 // `-d use_openssl`.
62 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
63 return
64 }
65 port := pick_port() or {
66 assert false, 'pick_port: ${err}'
67 return
68 }
69 mut srv := &http.Server{
70 addr: '127.0.0.1:${port}'
71 cert: server_tls_cert
72 cert_key: server_tls_key
73 in_memory_verification: true
74 accept_timeout: time.second
75 handler: EchoHandler{}
76 show_startup_message: false
77 }
78 t := spawn srv.listen_and_serve()
79 srv.wait_till_running() or {
80 srv.close()
81 t.wait()
82 assert false, 'server failed to start: ${err}'
83 return
84 }
85 defer {
86 srv.close()
87 t.wait()
88 }
89 // Give the listener a beat to come up.
90 time.sleep(50 * time.millisecond)
91
92 resp := http.fetch(
93 url: 'https://127.0.0.1:${port}/hello'
94 validate: false
95 ) or {
96 assert false, 'fetch failed: ${err}'
97 return
98 }
99 assert resp.status_code == 200
100 assert resp.body == 'tls hello /hello'
101}
102
103fn test_server_tls_stop() {
104 $if use_openssl ? {
105 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
106 return
107 }
108 port := pick_port() or {
109 assert false, 'pick_port: ${err}'
110 return
111 }
112 mut srv := &http.Server{
113 addr: '127.0.0.1:${port}'
114 cert: server_tls_cert
115 cert_key: server_tls_key
116 in_memory_verification: true
117 accept_timeout: 100 * time.millisecond
118 handler: EchoHandler{}
119 show_startup_message: false
120 }
121 t := spawn srv.listen_and_serve()
122 srv.wait_till_running() or {
123 srv.close()
124 t.wait()
125 assert false, 'server failed to start: ${err}'
126 return
127 }
128 srv.stop()
129 assert srv.status() == .stopped
130 t.wait()
131 assert srv.status() == .closed
132}
133
134fn test_server_tls_close_caps_default_accept_poll() {
135 $if use_openssl ? {
136 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
137 return
138 }
139 port := pick_port() or {
140 assert false, 'pick_port: ${err}'
141 return
142 }
143 mut srv := &http.Server{
144 addr: '127.0.0.1:${port}'
145 cert: server_tls_cert
146 cert_key: server_tls_key
147 in_memory_verification: true
148 handler: EchoHandler{}
149 show_startup_message: false
150 }
151 t := spawn srv.listen_and_serve()
152 srv.wait_till_running() or {
153 srv.close()
154 t.wait()
155 assert false, 'server failed to start: ${err}'
156 return
157 }
158 sw := time.new_stopwatch()
159 srv.close()
160 t.wait()
161 assert sw.elapsed() < time.second
162 assert srv.status() == .closed
163}
164
165fn test_server_tls_close_waits_for_active_request() {
166 $if use_openssl ? {
167 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
168 return
169 }
170 port := pick_port() or {
171 assert false, 'pick_port: ${err}'
172 return
173 }
174 started := chan bool{cap: 1}
175 release := chan bool{cap: 1}
176 done := chan string{cap: 1}
177 mut srv := &http.Server{
178 addr: '127.0.0.1:${port}'
179 cert: server_tls_cert
180 cert_key: server_tls_key
181 in_memory_verification: true
182 accept_timeout: time.second
183 handler: BlockingHandler{
184 started: started
185 release: release
186 }
187 show_startup_message: false
188 }
189 t := spawn srv.listen_and_serve()
190 srv.wait_till_running() or {
191 srv.close()
192 t.wait()
193 assert false, 'server failed to start: ${err}'
194 return
195 }
196 spawn fn [done, port] () {
197 resp := http.fetch(
198 url: 'https://127.0.0.1:${port}/blocked'
199 enable_http2: false
200 validate: false
201 ) or {
202 done <- 'error: ${err}'
203 return
204 }
205 done <- resp.body
206 }()
207 select {
208 _ := <-started {}
209 msg := <-done {
210 srv.close()
211 t.wait()
212 assert false, 'client finished before handler started: ${msg}'
213 return
214 }
215 2 * time.second {
216 srv.close()
217 t.wait()
218 assert false, 'timed out waiting for handler to start'
219 return
220 }
221 }
222 srv.close()
223 time.sleep(50 * time.millisecond)
224 release <- true
225 assert (<-done) == 'released'
226 t.wait()
227 assert srv.status() == .closed
228}
229
230fn test_server_tls_close_during_silent_handshake() {
231 $if use_openssl ? {
232 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
233 return
234 }
235 port := pick_port() or {
236 assert false, 'pick_port: ${err}'
237 return
238 }
239 mut srv := &http.Server{
240 addr: '127.0.0.1:${port}'
241 cert: server_tls_cert
242 cert_key: server_tls_key
243 in_memory_verification: true
244 accept_timeout: 100 * time.millisecond
245 handler: EchoHandler{}
246 show_startup_message: false
247 }
248 t := spawn srv.listen_and_serve()
249 srv.wait_till_running() or {
250 srv.close()
251 t.wait()
252 assert false, 'server failed to start: ${err}'
253 return
254 }
255 mut client := net.dial_tcp('127.0.0.1:${port}') or {
256 srv.close()
257 t.wait()
258 assert false, 'tcp dial failed: ${err}'
259 return
260 }
261 defer {
262 client.close() or {}
263 }
264 time.sleep(50 * time.millisecond)
265 sw := time.new_stopwatch()
266 srv.close()
267 t.wait()
268 assert sw.elapsed() < time.second
269 assert srv.status() == .closed
270}
271
272// test_server_tls_close_under_handshake_flood guards the case where every worker
273// is stuck in a slow TLS handshake AND the channel buffer is full of untracked
274// raw conns, so the accept thread parks on `ch <- conn`. If the accept thread
275// could not poll s.state during that send it would never reach close_idle(), and
276// close() would hang until a worker handshake timed out. Each client sends a
277// valid ClientHello then stalls, so the server's handshake blocks waiting for the
278// client's next flight (a bare connect would instead fail the handshake fast and
279// free the worker). With one worker and a one-slot buffer this fills: conn 0 ->
280// the worker, conn 1 -> the buffer, conn 2 -> the blocked send.
281fn test_server_tls_close_under_handshake_flood() {
282 $if use_openssl ? {
283 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
284 return
285 }
286 port := pick_port() or {
287 assert false, 'pick_port: ${err}'
288 return
289 }
290 // A real 165-byte TLS 1.2 ClientHello captured from V's own mbedtls client.
291 // The exact bytes (random/session id) are irrelevant; the server only needs a
292 // structurally valid first flight to start the handshake and then block
293 // waiting for the client's ClientKeyExchange, which never arrives.
294 client_hello := [u8(0x16), 0x03, 0x03, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x9c, 0x03, 0x03, 0x6a,
295 0x3d, 0x34, 0x4c, 0x8f, 0x46, 0x64, 0xd8, 0xe9, 0x2d, 0x46, 0x16, 0xd3, 0xf2, 0x87, 0xe4,
296 0xee, 0xc5, 0x57, 0x6c, 0x8e, 0xd7, 0x36, 0x19, 0x3b, 0x35, 0x7a, 0x01, 0x03, 0xde, 0x6e,
297 0x64, 0x00, 0x00, 0x24, 0xc0, 0x2c, 0xc0, 0x2b, 0xc0, 0x30, 0xc0, 0x2f, 0xc0, 0x24, 0xc0,
298 0x23, 0xc0, 0x28, 0xc0, 0x27, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x13, 0x00, 0x9d,
299 0x00, 0x9c, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x01, 0x00, 0x00, 0x4f, 0x00,
300 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02,
301 0x01, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x18, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04,
302 0x01, 0x05, 0x01, 0x02, 0x01, 0x04, 0x03, 0x05, 0x03, 0x02, 0x03, 0x02, 0x02, 0x06, 0x01,
303 0x06, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32,
304 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01,
305 0x00, 0x01, 0x00]
306 mut srv := &http.Server{
307 addr: '127.0.0.1:${port}'
308 cert: server_tls_cert
309 cert_key: server_tls_key
310 in_memory_verification: true
311 // A finite accept_timeout doubles as the handshake budget (see
312 // tls_accept_timeouts), so use a large one: each stalled handshake must
313 // stay stuck long enough to keep the worker busy and form the wedge,
314 // rather than time out in milliseconds. The accept loop still polls at the
315 // 100ms tls_accept_poll_timeout cap regardless.
316 accept_timeout: 8 * time.second
317 worker_num: 1
318 pool_channel_slots: 1
319 handler: EchoHandler{}
320 show_startup_message: false
321 }
322 t := spawn srv.listen_and_serve()
323 srv.wait_till_running() or {
324 srv.close()
325 t.wait()
326 assert false, 'server failed to start: ${err}'
327 return
328 }
329 mut clients := []&net.TcpConn{}
330 for _ in 0 .. 4 {
331 mut c := net.dial_tcp('127.0.0.1:${port}') or { continue }
332 c.write(client_hello) or {}
333 clients << c
334 }
335 defer {
336 for mut c in clients {
337 c.close() or {}
338 }
339 }
340 // Let the accept thread occupy the worker and buffer and park on the send.
341 time.sleep(400 * time.millisecond)
342 sw := time.new_stopwatch()
343 srv.close()
344 t.wait()
345 // close_idle() force-closes the stuck handshake fds, so close() returns well
346 // before the 8s handshake timeout — but only if the accept thread escaped the
347 // blocked send to reach it.
348 assert sw.elapsed() < 2 * time.second
349 assert srv.status() == .closed
350}
351
352fn test_server_tls_close_interrupts_idle_keep_alive() {
353 $if use_openssl ? {
354 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
355 return
356 }
357 port := pick_port() or {
358 assert false, 'pick_port: ${err}'
359 return
360 }
361 mut srv := &http.Server{
362 addr: '127.0.0.1:${port}'
363 cert: server_tls_cert
364 cert_key: server_tls_key
365 in_memory_verification: true
366 accept_timeout: time.second
367 read_timeout: 5 * time.second
368 handler: EchoHandler{}
369 show_startup_message: false
370 }
371 t := spawn srv.listen_and_serve()
372 srv.wait_till_running() or {
373 srv.close()
374 t.wait()
375 assert false, 'server failed to start: ${err}'
376 return
377 }
378 mut client := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{}) or {
379 srv.close()
380 t.wait()
381 assert false, 'ssl client init failed: ${err}'
382 return
383 }
384 defer {
385 client.shutdown() or {}
386 }
387 client.dial('127.0.0.1', port) or {
388 srv.close()
389 t.wait()
390 assert false, 'ssl dial failed: ${err}'
391 return
392 }
393 request := 'GET /idle HTTP/1.1\r\nHost: 127.0.0.1:${port}\r\nConnection: keep-alive\r\n\r\n'
394 client.write_string(request) or {
395 srv.close()
396 t.wait()
397 assert false, 'ssl write failed: ${err}'
398 return
399 }
400 mut buf := []u8{len: 4096}
401 n := client.read(mut buf) or {
402 srv.close()
403 t.wait()
404 assert false, 'ssl read failed: ${err}'
405 return
406 }
407 response := buf[..n].bytestr()
408 assert response.to_lower().contains('connection: keep-alive')
409 assert response.contains('tls hello /idle')
410
411 sw := time.new_stopwatch()
412 srv.close()
413 t.wait()
414 assert sw.elapsed() < 2 * time.second
415 assert srv.status() == .closed
416}
417
418fn test_server_tls_close_interrupts_idle_h2() {
419 $if use_openssl ? {
420 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
421 return
422 }
423 port := pick_port() or {
424 assert false, 'pick_port: ${err}'
425 return
426 }
427 mut srv := &http.Server{
428 addr: '127.0.0.1:${port}'
429 cert: server_tls_cert
430 cert_key: server_tls_key
431 in_memory_verification: true
432 accept_timeout: time.second
433 read_timeout: 5 * time.second
434 enable_http2: true
435 handler: EchoHandler{}
436 show_startup_message: false
437 }
438 t := spawn srv.listen_and_serve()
439 srv.wait_till_running() or {
440 srv.close()
441 t.wait()
442 assert false, 'server failed to start: ${err}'
443 return
444 }
445 mut client := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
446 alpn_protocols: ['h2']
447 }) or {
448 srv.close()
449 t.wait()
450 assert false, 'ssl client init failed: ${err}'
451 return
452 }
453 defer {
454 client.shutdown() or {}
455 }
456 client.dial('127.0.0.1', port) or {
457 srv.close()
458 t.wait()
459 assert false, 'ssl dial failed: ${err}'
460 return
461 }
462 assert client.negotiated_alpn() == 'h2'
463 mut h2 := http.new_h2_conn(client)
464 resp := h2.do(http.H2ClientRequest{
465 method: 'GET'
466 scheme: 'https'
467 authority: '127.0.0.1:${port}'
468 path: '/h2-idle'
469 }) or {
470 srv.close()
471 t.wait()
472 assert false, 'h2 request failed: ${err}'
473 return
474 }
475 assert resp.status == 200
476 assert resp.body.bytestr() == 'tls hello /h2-idle'
477
478 sw := time.new_stopwatch()
479 srv.close()
480 t.wait()
481 assert sw.elapsed() < 2 * time.second
482 assert srv.status() == .closed
483}
484
485fn test_server_tls_close_interrupts_incomplete_h2_request() {
486 $if use_openssl ? {
487 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
488 return
489 }
490 port := pick_port() or {
491 assert false, 'pick_port: ${err}'
492 return
493 }
494 mut srv := &http.Server{
495 addr: '127.0.0.1:${port}'
496 cert: server_tls_cert
497 cert_key: server_tls_key
498 in_memory_verification: true
499 accept_timeout: time.second
500 read_timeout: 2 * time.second
501 enable_http2: true
502 handler: EchoHandler{}
503 show_startup_message: false
504 }
505 t := spawn srv.listen_and_serve()
506 srv.wait_till_running() or {
507 srv.close()
508 t.wait()
509 assert false, 'server failed to start: ${err}'
510 return
511 }
512 mut client := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
513 alpn_protocols: ['h2']
514 }) or {
515 srv.close()
516 t.wait()
517 assert false, 'ssl client init failed: ${err}'
518 return
519 }
520 defer {
521 client.shutdown() or {}
522 }
523 client.dial('127.0.0.1', port) or {
524 srv.close()
525 t.wait()
526 assert false, 'ssl dial failed: ${err}'
527 return
528 }
529 assert client.negotiated_alpn() == 'h2'
530 mut enc := http.H2HpackEncoder{}
531 block := enc.encode([
532 http.H2HeaderField{':method', 'POST'},
533 http.H2HeaderField{':scheme', 'https'},
534 http.H2HeaderField{':authority', '127.0.0.1:${port}'},
535 http.H2HeaderField{':path', '/h2-incomplete'},
536 ])
537 mut out := []u8{}
538 out << http.h2_client_preface.bytes()
539 out << http.H2Frame(http.H2SettingsFrame{}).encode()
540 out << http.H2Frame(http.H2HeadersFrame{
541 stream_id: 1
542 fragment: block
543 end_headers: true
544 end_stream: false
545 }).encode()
546 written := client.write(out) or {
547 srv.close()
548 t.wait()
549 assert false, 'ssl write failed: ${err}'
550 return
551 }
552 assert written == out.len
553 time.sleep(100 * time.millisecond)
554
555 sw := time.new_stopwatch()
556 srv.close()
557 t.wait()
558 assert sw.elapsed() < time.second
559 assert srv.status() == .closed
560}
561
562fn test_server_tls_parallel_handshakes() {
563 $if use_openssl ? {
564 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
565 return
566 }
567 port := pick_port() or {
568 assert false, 'pick_port: ${err}'
569 return
570 }
571 mut srv := &http.Server{
572 addr: '127.0.0.1:${port}'
573 cert: server_tls_cert
574 cert_key: server_tls_key
575 in_memory_verification: true
576 accept_timeout: time.second
577 handler: EchoHandler{}
578 show_startup_message: false
579 }
580 t := spawn srv.listen_and_serve()
581 srv.wait_till_running() or {
582 srv.close()
583 t.wait()
584 assert false, 'server failed to start: ${err}'
585 return
586 }
587 defer {
588 srv.close()
589 t.wait()
590 }
591 time.sleep(50 * time.millisecond)
592
593 // Fire many clients at once. Handshakes now run on the worker pool, so they
594 // proceed concurrently against the listener's shared mbedtls config/RNG (safe
595 // because MBEDTLS_THREADING_C is enabled on all platforms). Assert every
596 // round-trip succeeds with the correct body — a thread-safety regression in
597 // the shared handshake state would surface as a failed/garbled response.
598 n := 16
599 results := chan string{cap: n}
600 for i in 0 .. n {
601 spawn fn [results, port, i] () {
602 resp := http.fetch(
603 url: 'https://127.0.0.1:${port}/p${i}'
604 enable_http2: false
605 validate: false
606 ) or {
607 results <- 'error: ${err}'
608 return
609 }
610 if resp.status_code != 200 {
611 results <- 'bad status: ${resp.status_code}'
612 return
613 }
614 results <- resp.body
615 }()
616 }
617 // Results arrive in nondeterministic order, so match on the common prefix
618 // rather than a specific path index.
619 mut ok := 0
620 for _ in 0 .. n {
621 body := <-results
622 assert body.starts_with('tls hello /p'), 'unexpected response: ${body}'
623 ok++
624 }
625 assert ok == n, 'expected ${n} successful concurrent handshakes, got ${ok}'
626}
627
628fn test_server_tls_h2_negotiation() {
629 $if use_openssl ? {
630 eprintln('skipping: TLS server not implemented for -d use_openssl yet')
631 return
632 }
633 port := pick_port() or {
634 assert false, 'pick_port: ${err}'
635 return
636 }
637 mut srv := &http.Server{
638 addr: '127.0.0.1:${port}'
639 cert: server_tls_cert
640 cert_key: server_tls_key
641 in_memory_verification: true
642 accept_timeout: time.second
643 enable_http2: true
644 handler: EchoHandler{}
645 show_startup_message: false
646 }
647 t := spawn srv.listen_and_serve()
648 srv.wait_till_running() or {
649 srv.close()
650 t.wait()
651 assert false, 'server failed to start: ${err}'
652 return
653 }
654 defer {
655 srv.close()
656 t.wait()
657 }
658 time.sleep(50 * time.millisecond)
659
660 // Client opts into HTTP/2; server must select `h2` via ALPN and serve the
661 // request through its HTTP/2 driver.
662 resp := http.fetch(
663 url: 'https://127.0.0.1:${port}/h2'
664 enable_http2: true
665 validate: false
666 ) or {
667 assert false, 'h2 fetch failed: ${err}'
668 return
669 }
670 assert resp.version() == .v2_0
671 assert resp.status_code == 200
672 assert resp.body == 'tls hello /h2'
673
674 // With HTTP/2 disabled on the client, the server must keep speaking
675 // HTTP/1.1 to the same listener. (enable_http2 defaults to true since
676 // vlang/v#27384, so it must be opted out of explicitly here.)
677 resp_h1 := http.fetch(
678 url: 'https://127.0.0.1:${port}/h1'
679 enable_http2: false
680 validate: false
681 ) or {
682 assert false, 'h1 fetch failed: ${err}'
683 return
684 }
685 assert resp_h1.version() == .v1_1
686 assert resp_h1.status_code == 200
687}
688