From bae41912ce9b9802bc977b05a89be18a80da5f8d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 14 Apr 2026 12:45:29 +0300 Subject: [PATCH] veb: Compile error `unknown type veb.Server` (fixes #26811) --- vlib/veb/README.md | 25 +++++++++++++++--- vlib/veb/server.v | 30 ++++++++++++++++++++++ vlib/veb/server_d_new_veb.v | 44 ++++++++++++++++++++++++++++++++ vlib/veb/server_notd_new_veb.v | 20 +++++++++++++++ vlib/veb/tests/veb_test_server.v | 8 ++++-- vlib/veb/veb_d_new_veb.v | 2 ++ vlib/veb/veb_picoev.v | 1 + 7 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 vlib/veb/server.v create mode 100644 vlib/veb/server_d_new_veb.v create mode 100644 vlib/veb/server_notd_new_veb.v diff --git a/vlib/veb/README.md b/vlib/veb/README.md index 16e9fba1e..83ed23874 100644 --- a/vlib/veb/README.md +++ b/vlib/veb/README.md @@ -130,10 +130,27 @@ shutdown. Once shutdown begins, it stops accepting new requests, waits for in-flight requests to finish, and only then exits. This is useful for deploys, tests, and management endpoints that trigger a stop after sending a response. -The lifecycle controls come from the underlying `fasthttp.ServerHandle` -(`wait_till_running()` and `shutdown(timeout: ...)`). If you need explicit -server lifecycle management, use those lower-level hooks in the process that -starts your app. +You can store the server handle in your app by adding an optional +`init_server(server &veb.Server)` method: + +```v +module main + +import veb + +pub struct App { +pub mut: + server &veb.Server = unsafe { nil } +} + +pub fn (mut app App) init_server(server &veb.Server) { + app.server = server +} +``` + +`veb.Server` forwards `wait_till_running()` and `shutdown(timeout: ...)` +to the `new_veb` backend. These lifecycle controls are available only when +running with `-d new_veb` without SSL. ## Defining endpoints diff --git a/vlib/veb/server.v b/vlib/veb/server.v new file mode 100644 index 000000000..a6632aa6c --- /dev/null +++ b/vlib/veb/server.v @@ -0,0 +1,30 @@ +module veb + +import time + +// ShutdownParams configures how graceful shutdown waits for in-flight requests. +@[params] +pub struct ShutdownParams { +pub: + timeout time.Duration = time.infinite + retry_period_ms int = 10 +} + +// WaitTillRunningParams configures how long the server waits to report that it is serving. +@[params] +pub struct WaitTillRunningParams { +pub: + max_retries int = 100 + retry_period_ms int = 10 +} + +interface HasInitServer { +mut: + init_server(server &Server) +} + +fn maybe_init_server[A](mut global_app A, server &Server) { + $if A is HasInitServer { + global_app.init_server(server) + } +} diff --git a/vlib/veb/server_d_new_veb.v b/vlib/veb/server_d_new_veb.v new file mode 100644 index 000000000..2267b920a --- /dev/null +++ b/vlib/veb/server_d_new_veb.v @@ -0,0 +1,44 @@ +module veb + +import fasthttp + +@[heap] +pub struct Server { + handle fasthttp.ServerHandle + lifecycle_control bool +} + +fn new_server_with_lifecycle(handle fasthttp.ServerHandle) &Server { + return &Server{ + handle: handle + lifecycle_control: true + } +} + +fn new_server_without_lifecycle() &Server { + return &Server{} +} + +fn (s &Server) ensure_lifecycle_control() ! { + if !s.lifecycle_control { + return error('veb server lifecycle control requires `-d new_veb` without SSL') + } +} + +// wait_till_running waits until the server starts accepting requests. +pub fn (s &Server) wait_till_running(params WaitTillRunningParams) !int { + s.ensure_lifecycle_control()! + return s.handle.wait_till_running(fasthttp.WaitTillRunningParams{ + max_retries: params.max_retries + retry_period_ms: params.retry_period_ms + })! +} + +// shutdown gracefully stops accepting new requests and waits for in-flight requests to finish. +pub fn (s &Server) shutdown(params ShutdownParams) ! { + s.ensure_lifecycle_control()! + s.handle.shutdown(fasthttp.ShutdownParams{ + timeout: params.timeout + retry_period_ms: params.retry_period_ms + })! +} diff --git a/vlib/veb/server_notd_new_veb.v b/vlib/veb/server_notd_new_veb.v new file mode 100644 index 000000000..0ef9837c9 --- /dev/null +++ b/vlib/veb/server_notd_new_veb.v @@ -0,0 +1,20 @@ +module veb + +@[heap] +pub struct Server {} + +fn new_server_without_lifecycle() &Server { + return &Server{} +} + +// wait_till_running waits until the server starts accepting requests. +pub fn (s &Server) wait_till_running(params WaitTillRunningParams) !int { + _ = params + return error('veb server lifecycle control requires `-d new_veb` without SSL') +} + +// shutdown gracefully stops accepting new requests and waits for in-flight requests to finish. +pub fn (s &Server) shutdown(params ShutdownParams) ! { + _ = params + return error('veb server lifecycle control requires `-d new_veb` without SSL') +} diff --git a/vlib/veb/tests/veb_test_server.v b/vlib/veb/tests/veb_test_server.v index c421ba404..68f02cc9b 100644 --- a/vlib/veb/tests/veb_test_server.v +++ b/vlib/veb/tests/veb_test_server.v @@ -17,6 +17,8 @@ pub fn (mut ctx ServerContext) not_found() veb.Result { } pub struct ServerApp { +mut: + server &veb.Server = unsafe { nil } port int timeout int global_config Config @@ -58,8 +60,10 @@ fn main() { )! } -// pub fn (mut app ServerApp) init_server() { -//} +// init_server stores the veb server handle when lifecycle control is available. +pub fn (mut app ServerApp) init_server(server &veb.Server) { + app.server = server +} pub fn (mut app ServerApp) index(mut ctx ServerContext) veb.Result { assert app.global_config.max_ping == 50 diff --git a/vlib/veb/veb_d_new_veb.v b/vlib/veb/veb_d_new_veb.v index ae65de13f..a4ea5e479 100644 --- a/vlib/veb/veb_d_new_veb.v +++ b/vlib/veb/veb_d_new_veb.v @@ -27,6 +27,7 @@ pub fn run_new[A, X](mut global_app A, params RunParams) ! { return error('invalid port number `${params.port}`, it should be between 1 and 65535') } if ssl_enabled(params) { + maybe_init_server[A](mut global_app, new_server_without_lifecycle()) run_at_with_ssl[A, X](mut global_app, params)! return } @@ -54,6 +55,7 @@ pub fn run_new[A, X](mut global_app A, params RunParams) ! { eprintln('Failed to create server: ${err}') return } + maybe_init_server[A](mut global_app, new_server_with_lifecycle(server.handle())) println('[veb] Running multi-threaded app on ${server_protocol(params)}://${startup_host(params)}:${params.port}/') flush_stdout() $if A is BeforeAcceptApp { diff --git a/vlib/veb/veb_picoev.v b/vlib/veb/veb_picoev.v index 65f763e93..0c3f085fa 100644 --- a/vlib/veb/veb_picoev.v +++ b/vlib/veb/veb_picoev.v @@ -20,6 +20,7 @@ $if !new_veb ? { if params.port <= 0 || params.port > 65535 { return error('invalid port number `${params.port}`, it should be between 1 and 65535') } + maybe_init_server[A](mut global_app, new_server_without_lifecycle()) if ssl_enabled(params) { run_at_with_ssl[A, X](mut global_app, params)! return -- 2.39.5