mirror of
https://github.com/vlang/v.git
synced 2025-09-08 14:51:53 -04:00
vweb: add an optional Context.before_accept_loop/0 method, to make testing easier and more robust (#20538)
This commit is contained in:
parent
9268241f96
commit
6cfca66e73
@ -0,0 +1,120 @@
|
||||
import os
|
||||
import log
|
||||
import time
|
||||
import vweb
|
||||
import net.http
|
||||
|
||||
const vexe = os.getenv('VEXE')
|
||||
const vroot = os.dir(vexe)
|
||||
const port = 28871
|
||||
const welcome_text = 'Welcome to our simple vweb server'
|
||||
|
||||
// Use a known good http client like `curl` (if it exists):
|
||||
const curl_executable = os.find_abs_path_of_executable('curl') or { '' }
|
||||
const curl_ok = curl_supports_ipv6()
|
||||
|
||||
fn curl_supports_ipv6() bool {
|
||||
if curl_executable == '' {
|
||||
return false
|
||||
}
|
||||
curl_res := os.execute('${curl_executable} --version')
|
||||
if curl_res.exit_code != 0 {
|
||||
return false
|
||||
}
|
||||
if !curl_res.output.match_glob('curl*Features: * IPv6 *') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn testsuite_begin() {
|
||||
log.set_level(.debug)
|
||||
log.debug(@FN)
|
||||
os.chdir(vroot) or {}
|
||||
if curl_ok {
|
||||
log.info('working curl_executable found at: ${curl_executable}')
|
||||
} else {
|
||||
log.warn('no working working curl_executable found')
|
||||
}
|
||||
start_services()
|
||||
}
|
||||
|
||||
fn testsuite_end() {
|
||||
log.debug(@FN)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
fn ensure_curl_works(tname string) ? {
|
||||
if !curl_ok {
|
||||
log.warn('skipping test ${tname}, since it needs a working curl')
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
fn test_curl_connecting_through_ipv4_works() {
|
||||
ensure_curl_works(@FN) or { return }
|
||||
res := os.execute('${curl_executable} --connect-timeout 0.5 --silent http://127.0.0.1:${port}/')
|
||||
assert res.exit_code == 0, res.output
|
||||
assert res.output == welcome_text
|
||||
log.info('> ${@FN}')
|
||||
}
|
||||
|
||||
fn test_net_http_connecting_through_ipv4_works() {
|
||||
res := http.get('http://127.0.0.1:${port}/')!
|
||||
assert res.status_code == 200, res.str()
|
||||
assert res.status_msg == 'OK', res.str()
|
||||
assert res.body == welcome_text, res.str()
|
||||
log.info('> ${@FN}')
|
||||
}
|
||||
|
||||
fn test_curl_connecting_through_ipv6_works() {
|
||||
ensure_curl_works(@FN) or { return }
|
||||
res := os.execute('${curl_executable} --silent --connect-timeout 0.5 http://[::1]:${port}/')
|
||||
assert res.exit_code == 0, res.output
|
||||
assert res.output == welcome_text
|
||||
log.info('> ${@FN}')
|
||||
}
|
||||
|
||||
fn test_net_http_connecting_through_ipv6_works() {
|
||||
$if windows {
|
||||
log.warn('skipping test ${@FN} on windows for now')
|
||||
return
|
||||
}
|
||||
res := http.get('http://[::1]:${port}/')!
|
||||
assert res.status_code == 200, res.str()
|
||||
assert res.status_msg == 'OK', res.str()
|
||||
assert res.body == welcome_text, res.str()
|
||||
log.info('> ${@FN}')
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
struct App {
|
||||
vweb.Context
|
||||
mut:
|
||||
started chan bool
|
||||
}
|
||||
|
||||
pub fn (mut app App) before_accept_loop() {
|
||||
app.started <- true
|
||||
}
|
||||
|
||||
pub fn (mut app App) index() vweb.Result {
|
||||
return app.text(welcome_text)
|
||||
}
|
||||
|
||||
fn start_services() {
|
||||
log.debug('starting watchdog thread to ensure the test will always exit one way or another...')
|
||||
spawn fn (timeout_in_ms int) {
|
||||
time.sleep(timeout_in_ms * time.millisecond)
|
||||
log.error('Timeout of ${timeout_in_ms} ms reached, for webserver: pid: ${os.getpid()}. Exiting ...')
|
||||
exit(1)
|
||||
}(10_000)
|
||||
|
||||
log.debug('starting webserver...')
|
||||
mut app := &App{}
|
||||
spawn vweb.run(app, port)
|
||||
_ := <-app.started
|
||||
log.debug('webserver started')
|
||||
}
|
@ -204,9 +204,19 @@ pub fn (ctx Context) init_server() {
|
||||
eprintln('init_server() has been deprecated, please init your web app in `fn main()`')
|
||||
}
|
||||
|
||||
// before_accept_loop is called once the vweb app is started, and listening, but before the loop that accepts
|
||||
// incomming request connections.
|
||||
// It will be called in the main thread, that runs vweb.run/2 or vweb.run_at/2.
|
||||
// It allows you to be notified about the successfull start of your app, and to synchronise your other threads
|
||||
// with the webserver start, without error prone and slow pooling or time.sleep waiting.
|
||||
// Defining this method is optional.
|
||||
pub fn (ctx &Context) before_accept_loop() {
|
||||
}
|
||||
|
||||
// before_request is called once before each request is routed.
|
||||
// It will be called in one of multiple threads in a pool, serving requests,
|
||||
// the same one, in which the matching route method will be executed right after it.
|
||||
// Defining this method is optional.
|
||||
// This method is called before every request (aka middleware).
|
||||
// You can use it for checking user session cookies or to add headers.
|
||||
pub fn (ctx Context) before_request() {}
|
||||
|
||||
// TODO - test
|
||||
@ -567,6 +577,10 @@ pub fn run_at[T](global_app &T, params RunParams) ! {
|
||||
}
|
||||
flush_stdout()
|
||||
|
||||
unsafe {
|
||||
global_app.before_accept_loop()
|
||||
}
|
||||
|
||||
// Forever accept every connection that comes, and
|
||||
// pass it through the channel, to the thread pool:
|
||||
for {
|
||||
|
Loading…
x
Reference in New Issue
Block a user