mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
127 lines
3.0 KiB
V
127 lines
3.0 KiB
V
import net
|
|
import net.http
|
|
import io
|
|
import os
|
|
import time
|
|
import veb
|
|
|
|
const exit_after = time.second * 10
|
|
const port = 13009
|
|
const localserver = 'localhost:${port}'
|
|
const tcp_r_timeout = 2 * time.second
|
|
const tcp_w_timeout = 2 * time.second
|
|
const max_retries = 4
|
|
|
|
const default_request = 'GET / HTTP/1.1
|
|
User-Agent: VTESTS
|
|
Accept: */*
|
|
\r\n'
|
|
|
|
const response_body = 'intact!'
|
|
|
|
pub struct Context {
|
|
veb.Context
|
|
}
|
|
|
|
pub struct App {
|
|
mut:
|
|
started chan bool
|
|
counter int
|
|
}
|
|
|
|
pub fn (mut app App) before_accept_loop() {
|
|
app.started <- true
|
|
}
|
|
|
|
pub fn (mut app App) index(mut ctx Context) veb.Result {
|
|
app.counter++
|
|
return ctx.text('${response_body}:${app.counter}')
|
|
}
|
|
|
|
pub fn (mut app App) reset(mut ctx Context) veb.Result {
|
|
app.counter = 0
|
|
return ctx.ok('')
|
|
}
|
|
|
|
fn testsuite_begin() {
|
|
os.chdir(os.dir(@FILE))!
|
|
mut app := &App{}
|
|
|
|
spawn veb.run_at[App, Context](mut app, port: port, timeout_in_seconds: 5)
|
|
_ := <-app.started
|
|
|
|
spawn fn () {
|
|
time.sleep(exit_after)
|
|
assert true == false, 'timeout reached!'
|
|
exit(1)
|
|
}()
|
|
}
|
|
|
|
fn test_conn_remains_intact() {
|
|
http.get('http://${localserver}/reset')!
|
|
|
|
mut conn := simple_tcp_client()!
|
|
conn.write_string(default_request)!
|
|
|
|
mut read := io.read_all(reader: conn)!
|
|
mut response := read.bytestr()
|
|
assert response.contains('Connection: close') == false, '`Connection` header should NOT be present!'
|
|
assert response.ends_with('${response_body}:1') == true, 'read response: ${response}'
|
|
|
|
// send request again over the same connection
|
|
conn.write_string(default_request)!
|
|
|
|
read = io.read_all(reader: conn)!
|
|
response = read.bytestr()
|
|
assert response.contains('Connection: close') == false, '`Connection` header should NOT be present!'
|
|
assert response.ends_with('${response_body}:2') == true, 'read response: ${response}'
|
|
|
|
conn.close() or {}
|
|
}
|
|
|
|
fn test_support_http_1() {
|
|
http.get('http://${localserver}/reset')!
|
|
// HTTP 1.0 always closes the connection after each request, so the client must
|
|
// send the Connection: close header. If that header is present the connection
|
|
// needs to be closed and a `Connection: close` header needs to be send back
|
|
mut x := http.fetch(http.FetchConfig{
|
|
url: 'http://${localserver}/'
|
|
header: http.new_header_from_map({
|
|
.connection: 'close'
|
|
})
|
|
})!
|
|
assert x.status() == .ok
|
|
if conn_header := x.header.get(.connection) {
|
|
assert conn_header == 'close'
|
|
} else {
|
|
assert false, '`Connection: close` header should be present!'
|
|
}
|
|
}
|
|
|
|
// utility code:
|
|
|
|
fn simple_tcp_client() !&net.TcpConn {
|
|
mut client := &net.TcpConn(unsafe { nil })
|
|
mut tries := 0
|
|
for tries < max_retries {
|
|
tries++
|
|
eprintln('> client retries: ${tries}')
|
|
client = net.dial_tcp(localserver) or {
|
|
eprintln('dial error: ${err.msg()}')
|
|
if tries > max_retries {
|
|
return err
|
|
}
|
|
time.sleep(100 * time.millisecond)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if client == unsafe { nil } {
|
|
eprintln('could not create a tcp client connection to http://${localserver} after ${max_retries} retries')
|
|
exit(1)
|
|
}
|
|
client.set_read_timeout(tcp_r_timeout)
|
|
client.set_write_timeout(tcp_w_timeout)
|
|
return client
|
|
}
|