forked from vlang/v
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x.vweb: support HTTP 1.1 persistent connections (vlang#20658)
- Loading branch information
Showing
4 changed files
with
171 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import net | ||
import net.http | ||
import io | ||
import os | ||
import time | ||
import x.vweb | ||
|
||
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 { | ||
vweb.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) vweb.Result { | ||
app.counter++ | ||
return ctx.text('${response_body}:${app.counter}') | ||
} | ||
|
||
pub fn (mut app App) reset(mut ctx Context) vweb.Result { | ||
app.counter = 0 | ||
return ctx.ok('') | ||
} | ||
|
||
fn testsuite_begin() { | ||
os.chdir(os.dir(@FILE))! | ||
mut app := &App{} | ||
|
||
spawn vweb.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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters