diff --git a/examples/vweb/vweb_example.v b/examples/vweb/vweb_example.v index cfb46d6700..0808c80755 100644 --- a/examples/vweb/vweb_example.v +++ b/examples/vweb/vweb_example.v @@ -31,6 +31,7 @@ pub fn (mut app App) index() vweb.Result { // app.text('Hello world from vweb') hello := 'Hello world from vweb' numbers := [1, 2, 3] + app.enable_chunked_transfer(40) return $vweb.html() } diff --git a/vlib/vweb/tests/vweb_test.v b/vlib/vweb/tests/vweb_test.v index 4ab7558b90..b863b96f5f 100644 --- a/vlib/vweb/tests/vweb_test.v +++ b/vlib/vweb/tests/vweb_test.v @@ -106,6 +106,13 @@ fn test_http_client_index() { assert x.text == 'Welcome to VWeb' } +fn test_http_client_chunk_transfer() { + x := http.get('http://127.0.0.1:$sport/chunk') or { panic(err) } + assert_common_http_headers(x) + assert x.headers['Transfer-Encoding'] == 'chunked' + assert x.text == 'Lorem ipsum dolor sit amet, consetetur sadipscing' +} + fn test_http_client_404() { url_404_list := [ 'http://127.0.0.1:$sport/zxcnbnm', diff --git a/vlib/vweb/tests/vweb_test_server.v b/vlib/vweb/tests/vweb_test_server.v index c587b153cf..08d2a907b0 100644 --- a/vlib/vweb/tests/vweb_test_server.v +++ b/vlib/vweb/tests/vweb_test_server.v @@ -56,6 +56,11 @@ pub fn (mut app App) html_page() vweb.Result { return app.html('

ok

') } +pub fn (mut app App) chunk() vweb.Result { + app.enable_chunked_transfer(20) + return app.html('Lorem ipsum dolor sit amet, consetetur sadipscing') +} + // the following serve custom routes ['/:user/settings'] pub fn (mut app App) settings(username string) vweb.Result { diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 5d0b8cf71b..2f8eff6716 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -56,6 +56,8 @@ pub mut: done bool page_gen_start i64 form_error string + chunked_transfer bool + max_chunk_len int = 20 } // declaring init_once in your App struct is optional @@ -88,10 +90,35 @@ pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bo sb.write('HTTP/1.1 $ctx.status') sb.write('\r\nContent-Type: $mimetype') sb.write('\r\nContent-Length: $res.len') + if ctx.chunked_transfer { + sb.write('\r\nTransfer-Encoding: chunked') + } sb.write(ctx.headers) sb.write('\r\n') sb.write(headers_close) - sb.write(res) + if ctx.chunked_transfer { + mut i := 0 + mut len := res.len + for { + if len <= 0 { + break + } + mut chunk := '' + if len > ctx.max_chunk_len { + chunk = res[i..i + ctx.max_chunk_len] + i += ctx.max_chunk_len + len -= ctx.max_chunk_len + } else { + chunk = res[i..] + len = 0 + } + sb.write(chunk.len.hex()) + sb.write('\r\n$chunk\r\n') + } + sb.write('0\r\n\r\n') // End of chunks + } else { + sb.write(res) + } s := sb.str() defer { s.free() @@ -140,6 +167,11 @@ pub fn (mut ctx Context) not_found() Result { return Result{} } +pub fn (mut ctx Context) enable_chunked_transfer(max_chunk_len int) { + ctx.chunked_transfer = true + ctx.max_chunk_len = max_chunk_len +} + pub fn (mut ctx Context) set_cookie(cookie Cookie) { mut cookie_data := []string{} mut secure := if cookie.secure { 'Secure;' } else { '' }