v / vlib / veb / tests / graceful_shutdown_test.v
155 lines · 134 sloc · 3.72 KB · 6768fdffe5da44d0356dd1135cda6ec130b1e1c8
Raw
1// vtest build: !windows && !tinyc // fasthttp.Server.run is not implemented on windows; the test hangs intermittently when built with tcc on linux CI
2import net
3import net.http
4import time
5import veb
6
7const graceful_shutdown_host = '127.0.0.1'
8const graceful_shutdown_wait_timeout = 10 * time.second
9
10struct GracefulShutdownContext {
11 veb.Context
12}
13
14struct GracefulShutdownApp {
15mut:
16 server &veb.Server = unsafe { nil }
17 slow_started chan bool = chan bool{cap: 1}
18}
19
20pub fn (mut app GracefulShutdownApp) init_server(server &veb.Server) {
21 app.server = server
22}
23
24pub fn (mut app GracefulShutdownApp) index(mut ctx GracefulShutdownContext) veb.Result {
25 return ctx.text('ready')
26}
27
28@['/slow']
29pub fn (mut app GracefulShutdownApp) slow(mut ctx GracefulShutdownContext) veb.Result {
30 app.slow_started <- true
31 time.sleep(300 * time.millisecond)
32 return ctx.text('slow done')
33}
34
35fn run_graceful_shutdown_app(mut app GracefulShutdownApp, port int) {
36 veb.run_at[GracefulShutdownApp, GracefulShutdownContext](mut app,
37 host: graceful_shutdown_host
38 port: port
39 family: .ip
40 show_startup_message: false
41 ) or { panic(err) }
42}
43
44fn send_slow_request(port int, responses chan http.Response) {
45 response := http.get('http://${graceful_shutdown_host}:${port}/slow') or { panic(err) }
46 responses <- response
47}
48
49fn shutdown_graceful_server(server &veb.Server, done chan string) {
50 server.shutdown(timeout: 5 * time.second) or {
51 done <- err.msg()
52 return
53 }
54 done <- ''
55}
56
57fn wait_for_slow_request(started chan bool) ! {
58 select {
59 _ := <-started {
60 return
61 }
62 graceful_shutdown_wait_timeout {
63 return error('slow request did not start')
64 }
65 }
66}
67
68fn wait_for_slow_response(responses chan http.Response) !http.Response {
69 select {
70 response := <-responses {
71 return response
72 }
73 graceful_shutdown_wait_timeout {
74 return error('slow request did not finish')
75 }
76 }
77 return error('slow request did not finish')
78}
79
80fn wait_for_shutdown(done chan string) ! {
81 select {
82 shutdown_error := <-done {
83 if shutdown_error != '' {
84 return error(shutdown_error)
85 }
86 return
87 }
88 graceful_shutdown_wait_timeout {
89 return error('server shutdown did not finish')
90 }
91 }
92}
93
94fn wait_for_server(port int) ! {
95 url := 'http://${graceful_shutdown_host}:${port}/'
96 for _ in 0 .. 100 {
97 response := http.get(url) or {
98 time.sleep(20 * time.millisecond)
99 continue
100 }
101 if response.status() == .ok && response.body == 'ready' {
102 return
103 }
104 time.sleep(20 * time.millisecond)
105 }
106 return error('server did not start listening in time')
107}
108
109fn test_veb_graceful_shutdown_waits_for_in_flight_requests() {
110 mut listener := net.listen_tcp(.ip, '${graceful_shutdown_host}:0') or { panic(err) }
111 port := listener.addr()!.port()!
112 listener.close() or {}
113
114 mut app := &GracefulShutdownApp{}
115 server_thread := spawn run_graceful_shutdown_app(mut app, int(port))
116
117 wait_for_server(int(port)) or {
118 assert false, err.msg()
119 return
120 }
121
122 slow_responses := chan http.Response{cap: 1}
123 spawn send_slow_request(int(port), slow_responses)
124 wait_for_slow_request(app.slow_started) or {
125 assert false, err.msg()
126 return
127 }
128
129 mut server := app.server
130 if server == unsafe { nil } {
131 assert false, 'veb server was not initialized'
132 return
133 }
134 shutdown_done := chan string{cap: 1}
135 spawn shutdown_graceful_server(server, shutdown_done)
136
137 slow_response := wait_for_slow_response(slow_responses) or {
138 assert false, err.msg()
139 return
140 }
141 assert slow_response.status() == .ok
142 assert slow_response.body == 'slow done'
143
144 wait_for_shutdown(shutdown_done) or {
145 assert false, err.msg()
146 return
147 }
148 server_thread.wait()
149
150 http.get('http://${graceful_shutdown_host}:${port}/') or {
151 assert true
152 return
153 }
154 assert false, 'server still accepts new requests after shutdown'
155}
156