Skip to content

Commit d88ca11

Browse files
authored
picoev, x.vweb: small fixes and backport changes from vweb (#20584)
1 parent 2874e7c commit d88ca11

18 files changed

+320
-130
lines changed

cmd/tools/vtest-self.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ const skip_on_ubuntu_musl = [
264264
'vlib/net/smtp/smtp_test.v',
265265
'vlib/v/tests/websocket_logger_interface_should_compile_test.v',
266266
'vlib/v/tests/fn_literal_type_test.v',
267-
'vlib/vweb/x/tests/vweb_test.v',
268-
'vlib/vweb/x/tests/vweb_app_test.v',
267+
'vlib/x/vweb/tests/vweb_test.v',
268+
'vlib/x/vweb/tests/vweb_app_test.v',
269269
]
270270
const skip_on_linux = [
271271
'do_not_remove',

examples/pico/pico.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ fn callback(data voidptr, req picohttpparser.Request, mut res picohttpparser.Res
5050

5151
fn main() {
5252
println('Starting webserver on http://localhost:${port}/ ...')
53-
mut server := picoev.new(port: port, cb: callback)
53+
mut server := picoev.new(port: port, cb: callback)!
5454
server.serve()
5555
}

examples/pico/raw_callback.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() {
1212
mut pico := picoev.new(
1313
port: port
1414
raw_cb: handle_conn
15-
)
15+
)!
1616
pico.serve()
1717
}
1818

vlib/picoev/picoev.v

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ pub:
4444
max_headers int = 100
4545
max_read int = 4096
4646
max_write int = 8192
47-
family net.AddrFamily = .ip
48-
host string = 'localhost'
47+
family net.AddrFamily = .ip6
48+
host string
4949
}
5050

5151
@[heap]
@@ -302,8 +302,8 @@ fn default_err_cb(data voidptr, req picohttpparser.Request, mut res picohttppars
302302
}
303303

304304
// new creates a `Picoev` struct and initializes the main loop
305-
pub fn new(config Config) &Picoev {
306-
listen_fd := listen(config)
305+
pub fn new(config Config) !&Picoev {
306+
listen_fd := listen(config)!
307307

308308
mut pv := &Picoev{
309309
num_loops: 1

vlib/picoev/socket_util.c.v

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ fn fatal_socket_error(fd int) bool {
9999
}
100100

101101
// listen creates a listening tcp socket and returns its file descriptor
102-
fn listen(config Config) int {
102+
fn listen(config Config) !int {
103103
// not using the `net` modules sockets, because not all socket options are defined
104104
fd := C.socket(config.family, net.SocketType.tcp, 0)
105105
assert fd != -1
@@ -110,16 +110,23 @@ fn listen(config Config) int {
110110

111111
// Setting flags for socket
112112
flag := 1
113-
assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0
113+
flag_zero := 0
114+
net.socket_error(C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)))!
115+
116+
if config.family == .ip6 {
117+
// set socket to dualstack so connections to both ipv4 and ipv6 addresses
118+
// can be accepted
119+
net.socket_error(C.setsockopt(fd, C.IPPROTO_IPV6, C.IPV6_V6ONLY, &flag_zero, sizeof(int)))!
120+
}
114121

115122
$if linux {
116123
// epoll socket options
117-
assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)) == 0
118-
assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)) == 0
119-
assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT, &config.timeout_secs,
120-
sizeof(int)) == 0
124+
net.socket_error(C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)))!
125+
net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)))!
126+
net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT, &config.timeout_secs,
127+
sizeof(int)))!
121128
queue_len := max_queue
122-
assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len, sizeof(int)) == 0
129+
net.socket_error(C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len, sizeof(int)))!
123130
}
124131

125132
// addr settings
@@ -128,12 +135,8 @@ fn listen(config Config) int {
128135
addr := addrs[0]
129136
alen := addr.len()
130137

131-
net.socket_error_message(C.bind(fd, voidptr(&addr), alen), 'binding to ${saddr} failed') or {
132-
panic(err)
133-
}
134-
net.socket_error_message(C.listen(fd, C.SOMAXCONN), 'listening on ${saddr} with maximum backlog pending queue of ${C.SOMAXCONN}, failed') or {
135-
panic(err)
136-
}
138+
net.socket_error_message(C.bind(fd, voidptr(&addr), alen), 'binding to ${saddr} failed')!
139+
net.socket_error_message(C.listen(fd, C.SOMAXCONN), 'listening on ${saddr} with maximum backlog pending queue of ${C.SOMAXCONN}, failed')!
137140

138141
setup_sock(fd) or {
139142
config.err_cb(config.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{},

vlib/x/vweb/context.v

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub enum RedirectType {
2020

2121
// The Context struct represents the Context which holds the HTTP request and response.
2222
// It has fields for the query, form, files and methods for handling the request and response
23+
@[heap]
2324
pub struct Context {
2425
mut:
2526
// vweb wil try to infer the content type base on file extension,
@@ -120,29 +121,28 @@ pub fn (mut ctx Context) send_response_to_client(mimetype string, response strin
120121
return Result{}
121122
}
122123

123-
// Response HTTP_OK with s as payload with content-type `text/html`
124+
// Response with payload and content-type `text/html`
124125
pub fn (mut ctx Context) html(s string) Result {
125126
return ctx.send_response_to_client('text/html', s)
126127
}
127128

128-
// Response HTTP_OK with s as payload with content-type `text/plain`
129+
// Response with `s` as payload and content-type `text/plain`
129130
pub fn (mut ctx Context) text(s string) Result {
130131
return ctx.send_response_to_client('text/plain', s)
131132
}
132133

133-
// Response HTTP_OK with j as payload with content-type `application/json`
134+
// Response with json_s as payload and content-type `application/json`
134135
pub fn (mut ctx Context) json[T](j T) Result {
135136
json_s := json.encode(j)
136137
return ctx.send_response_to_client('application/json', json_s)
137138
}
138139

139-
// Response HTTP_OK with a pretty-printed JSON result
140+
// Response with a pretty-printed JSON result
140141
pub fn (mut ctx Context) json_pretty[T](j T) Result {
141142
json_s := json.encode_pretty(j)
142143
return ctx.send_response_to_client('application/json', json_s)
143144
}
144145

145-
// TODO - test + turn read_file into streaming
146146
// Response HTTP_OK with file as payload
147147
pub fn (mut ctx Context) file(file_path string) Result {
148148
if !os.exists(file_path) {
@@ -187,10 +187,7 @@ fn (mut ctx Context) send_file(content_type string, file_path string) Result {
187187
}
188188
file.close()
189189

190-
// optimization: use max_read on purpose instead of max_write to take into account
191-
// the HTTP header size and the fact that it's not likely that the socket/OS
192-
// is able to write 8KB at once under load.
193-
if file_size < max_read || ctx.takeover {
190+
if ctx.takeover {
194191
// it's a small file so we can send the response directly
195192
data := os.read_file(file_path) or {
196193
eprintln('[vweb] error while trying to read file: ${err.msg()}')

vlib/x/vweb/controller.v

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module vweb
22

33
import net.urllib
44

5-
type ControllerHandler = fn (ctx Context, mut url urllib.URL, host string) Context
5+
type ControllerHandler = fn (ctx &Context, mut url urllib.URL, host string) &Context
66

77
pub struct ControllerPath {
88
pub:
@@ -35,7 +35,7 @@ pub fn controller[A, X](path string, mut global_app A) !&ControllerPath {
3535
// no need to type `ControllerHandler` as generic since it's not needed for closures
3636
return &ControllerPath{
3737
path: path
38-
handler: fn [mut global_app, path, routes, controllers_sorted] [A, X](ctx Context, mut url urllib.URL, host string) Context {
38+
handler: fn [mut global_app, path, routes, controllers_sorted] [A, X](ctx &Context, mut url urllib.URL, host string) &Context {
3939
// transform the url
4040
url.path = url.path.all_after_first(path)
4141

@@ -53,7 +53,8 @@ pub fn controller[A, X](path string, mut global_app A) !&ControllerPath {
5353
user_context.Context = ctx
5454

5555
handle_route[A, X](mut global_app, mut user_context, url, host, &routes)
56-
return user_context.Context
56+
// we need to explicitly tell the V compiler to return a reference
57+
return &user_context.Context
5758
}
5859
}
5960
}
@@ -95,7 +96,7 @@ fn check_duplicate_routes_in_controllers[T](global_app &T, routes map[string]Rou
9596
return controllers_sorted
9697
}
9798

98-
fn handle_controllers[X](controllers []&ControllerPath, ctx Context, mut url urllib.URL, host string) ?Context {
99+
fn handle_controllers[X](controllers []&ControllerPath, ctx &Context, mut url urllib.URL, host string) ?&Context {
99100
for controller in controllers {
100101
// skip controller if the hosts don't match
101102
if controller.host != '' && host != controller.host {

vlib/x/vweb/csrf/csrf_test.v

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ pub struct Context {
187187

188188
pub struct App {
189189
vweb.Middleware[Context]
190+
mut:
191+
started chan bool
192+
}
193+
194+
pub fn (mut app App) before_accept_loop() {
195+
app.started <- true
190196
}
191197

192198
fn (app &App) index(mut ctx Context) vweb.Result {
@@ -234,10 +240,9 @@ fn test_run_app_in_background() {
234240
mut app := &App{}
235241
app.route_use('/auth', csrf.middleware[Context](csrf_config))
236242

237-
spawn vweb.run_at[App, Context](mut app, port: sport, family: .ip)
238243
spawn exit_after_timeout(mut app, exit_after_time)
239-
240-
time.sleep(500 * time.millisecond)
244+
spawn vweb.run_at[App, Context](mut app, port: sport, family: .ip)
245+
_ := <-app.started
241246
}
242247

243248
fn test_token_input() {

vlib/x/vweb/sendfile_linux.c.v

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module vweb
2+
3+
fn C.sendfile(out_fd int, in_fd int, offset voidptr, count int) int
4+
5+
fn sendfile(out_fd int, in_fd int, nr_bytes int) int {
6+
// always pass nil as offset, so the file offset will be used and updated.
7+
return C.sendfile(out_fd, in_fd, 0, nr_bytes)
8+
}

vlib/x/vweb/sse/sse_test.v

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@ pub struct Context {
1212
vweb.Context
1313
}
1414

15-
pub struct App {}
15+
pub struct App {
16+
mut:
17+
started chan bool
18+
}
19+
20+
pub fn (mut app App) before_accept_loop() {
21+
app.started <- true
22+
}
1623

1724
fn (app &App) sse(mut ctx Context) vweb.Result {
1825
ctx.takeover_conn()
@@ -32,15 +39,15 @@ fn handle_sse_conn(mut ctx Context) {
3239

3340
fn testsuite_begin() {
3441
mut app := &App{}
35-
36-
spawn vweb.run_at[App, Context](mut app, port: port, family: .ip)
37-
// app startup time
38-
time.sleep(time.second * 2)
3942
spawn fn () {
4043
time.sleep(exit_after)
4144
assert true == false, 'timeout reached!'
4245
exit(1)
4346
}()
47+
48+
spawn vweb.run_at[App, Context](mut app, port: port, family: .ip)
49+
// app startup time
50+
_ := <-app.started
4451
}
4552

4653
fn test_sse() ! {

0 commit comments

Comments
 (0)