From cd2a2cef253dd054a9d601acaaac46495b9c4cc5 Mon Sep 17 00:00:00 2001 From: Emily Hudson Date: Sun, 15 Nov 2020 20:54:47 +0000 Subject: [PATCH] vlib/net: add buffered IO, x.net -> net (#6754) --- .gitignore | 4 + cmd/tools/vbin2v.v | 8 +- cmd/tools/vcreate.v | 6 +- cmd/tools/vdoc.v | 20 +- examples/buf_reader.v | 21 ++ examples/net_peer_ip.v | 5 + examples/net_raw_http.v | 12 +- examples/net_t.v | 23 ++ examples/path_tracing.v | 2 +- examples/tcp_echo_server.v | 45 --- vlib/builtin/array.v | 4 + vlib/io/buffered_reader.v | 95 ++++++ vlib/io/net_stream_test.v | 181 ------------ vlib/io/netinstream.v | 188 ------------ vlib/io/netoutstream.v | 184 ------------ vlib/io/reader.v | 45 +++ vlib/io/readerwriter.v | 45 +++ vlib/io/writer.v | 12 + vlib/{x => }/net/aasocket.c.v | 5 +- vlib/net/address.v | 81 +++++ vlib/{x => }/net/common.v | 0 vlib/{x => }/net/errors.v | 15 + vlib/net/ftp/ftp.v | 113 ++++--- vlib/net/ftp/ftp_test.v | 17 +- vlib/net/http/http.v | 31 +- vlib/net/init_nix.c.v | 17 -- vlib/net/init_windows.c.v | 37 --- vlib/net/net.v | 16 - vlib/{x => }/net/net_nix.c.v | 0 vlib/{x => }/net/net_windows.c.v | 0 vlib/net/smtp/smtp.v | 36 +-- vlib/net/socket.v | 392 ------------------------- vlib/{x => }/net/socket_options.c.v | 0 vlib/net/socket_test.v | 106 ------- vlib/net/socket_udp_test.v | 18 -- vlib/{x => }/net/tcp.v | 119 ++++---- vlib/{x => }/net/tcp_test.v | 10 +- vlib/{x => }/net/udp.v | 26 +- vlib/{x => }/net/udp_test.v | 8 +- vlib/{x => }/net/util.v | 0 vlib/os/file.v | 118 ++++---- vlib/os/os.v | 2 +- vlib/os/os_test.v | 44 +-- vlib/v/gen/fn.v | 6 - vlib/vweb/assets/assets.v | 2 +- vlib/vweb/tests/vweb_test.v | 50 ++-- vlib/vweb/vweb.v | 116 ++++---- vlib/x/net/address.v | 74 ----- vlib/x/openssl/openssl.v | 2 +- vlib/x/websocket/handshake.v | 2 +- vlib/x/websocket/io.v | 14 +- vlib/x/websocket/message.v | 4 +- vlib/x/websocket/websocket_client.v | 4 +- vlib/x/websocket/websocket_server.v | 2 +- vlib/x/websocket/websocket_windows.c.v | 2 +- 55 files changed, 741 insertions(+), 1648 deletions(-) create mode 100644 examples/buf_reader.v create mode 100644 examples/net_peer_ip.v create mode 100644 examples/net_t.v delete mode 100644 examples/tcp_echo_server.v create mode 100644 vlib/io/buffered_reader.v delete mode 100644 vlib/io/net_stream_test.v delete mode 100644 vlib/io/netinstream.v delete mode 100644 vlib/io/netoutstream.v create mode 100644 vlib/io/reader.v create mode 100644 vlib/io/readerwriter.v create mode 100644 vlib/io/writer.v rename vlib/{x => }/net/aasocket.c.v (94%) create mode 100644 vlib/net/address.v rename vlib/{x => }/net/common.v (100%) rename vlib/{x => }/net/errors.v (81%) delete mode 100644 vlib/net/init_nix.c.v delete mode 100644 vlib/net/init_windows.c.v delete mode 100644 vlib/net/net.v rename vlib/{x => }/net/net_nix.c.v (100%) rename vlib/{x => }/net/net_windows.c.v (100%) delete mode 100644 vlib/net/socket.v rename vlib/{x => }/net/socket_options.c.v (100%) delete mode 100644 vlib/net/socket_test.v delete mode 100644 vlib/net/socket_udp_test.v rename vlib/{x => }/net/tcp.v (74%) rename vlib/{x => }/net/tcp_test.v (89%) rename vlib/{x => }/net/udp.v (86%) rename vlib/{x => }/net/udp_test.v (91%) rename vlib/{x => }/net/util.v (100%) delete mode 100644 vlib/x/net/address.v diff --git a/.gitignore b/.gitignore index 2a26fcfd2b..96e03f860b 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,7 @@ thumbs.db /.bin _docs + +# ignore vs databases +*.suo +*.VC.db diff --git a/cmd/tools/vbin2v.v b/cmd/tools/vbin2v.v index 2dffd70aa9..50f7ee0f72 100644 --- a/cmd/tools/vbin2v.v +++ b/cmd/tools/vbin2v.v @@ -118,7 +118,7 @@ fn main() { exit(0) } context.files = real_files - if !context.write_file.ends_with('.v') { + if context.write_file != '' && !context.write_file.ends_with('.v') { context.write_file += '.v' } mut file_byte_map := map[string][]byte{} @@ -134,11 +134,11 @@ fn main() { mut out_file := os.create(context.write_file) or { panic(err) } - out_file.write(context.header()) + out_file.write_str(context.header()) for bname, fbytes in file_byte_map { - out_file.write(context.file2v(bname, fbytes, max_bname)) + out_file.write_str(context.file2v(bname, fbytes, max_bname)) } - out_file.write(context.footer()) + out_file.write_str(context.footer()) } else { println(context.header()) for bname, fbytes in file_byte_map { diff --git a/cmd/tools/vcreate.v b/cmd/tools/vcreate.v index 85eacadac1..047bd71abe 100644 --- a/cmd/tools/vcreate.v +++ b/cmd/tools/vcreate.v @@ -58,7 +58,7 @@ fn (c &Create) write_vmod(new bool) { cerror(err) exit(1) } - vmod.write(vmod_content(c.name, c.description)) + vmod.write_str(vmod_content(c.name, c.description)) vmod.close() } @@ -71,7 +71,7 @@ fn (c &Create) write_main(new bool) { cerror(err) exit(2) } - main.write(main_content()) + main.write_str(main_content()) main.close() } @@ -87,7 +87,7 @@ fn (c &Create) create_git_repo(dir string) { // We don't really need a .gitignore, it's just a nice-to-have return } - fl.write(gen_gitignore(c.name)) + fl.write_str(gen_gitignore(c.name)) fl.close() } } diff --git a/cmd/tools/vdoc.v b/cmd/tools/vdoc.v index ea3fd0ae59..bbd0692dde 100644 --- a/cmd/tools/vdoc.v +++ b/cmd/tools/vdoc.v @@ -16,6 +16,7 @@ import v.token import v.vmod import v.pref import json +import io enum HighlightTokenTyp { unone @@ -154,7 +155,7 @@ fn (mut cfg DocConfig) serve_html() { } def_name := docs.keys()[0] server_url := 'http://localhost:' + cfg.server_port.str() - server := net.listen(cfg.server_port) or { + server := net.listen_tcp(cfg.server_port) or { panic(err) } println('Serving docs on: $server_url') @@ -173,12 +174,12 @@ fn (mut cfg DocConfig) serve_html() { default_filename: def_name } for { - mut con := server.accept() or { + mut conn := server.accept() or { server.close() or { } panic(err) } - handle_http_connection(mut con, server_context) - con.close() or { + handle_http_connection(mut conn, server_context) + conn.close() or { eprintln('error closing the connection: $err') } } @@ -190,10 +191,9 @@ struct VdocHttpServerContext { default_filename string } -fn handle_http_connection(mut con net.Socket, ctx &VdocHttpServerContext) { - s := con.read_line() - first_line := s.all_before('\r\n') - if first_line.len == 0 { +fn handle_http_connection(mut con net.TcpConn, ctx &VdocHttpServerContext) { + mut reader := io.new_buffered_reader(reader: io.make_reader(con)) + first_line := reader.read_line() or { send_http_response(mut con, 501, ctx.content_type, 'bad request') return } @@ -211,7 +211,7 @@ fn handle_http_connection(mut con net.Socket, ctx &VdocHttpServerContext) { send_http_response(mut con, 200, ctx.content_type, ctx.docs[filename]) } -fn send_http_response(mut con net.Socket, http_code int, content_type string, html string) { +fn send_http_response(mut con net.TcpConn, http_code int, content_type string, html string) { content_length := html.len.str() shttp_code := http_code.str() mut http_response := strings.new_builder(20000) @@ -229,7 +229,7 @@ fn send_http_response(mut con net.Socket, http_code int, content_type string, ht http_response.write('\r\n') http_response.write(html) sresponse := http_response.str() - con.send_string(sresponse) or { + con.write_str(sresponse) or { eprintln('error sending http response: $err') } } diff --git a/examples/buf_reader.v b/examples/buf_reader.v new file mode 100644 index 0000000000..16c39004d8 --- /dev/null +++ b/examples/buf_reader.v @@ -0,0 +1,21 @@ +// Simple raw HTTP head request +import net +import time +import io + +// Make a new connection +mut conn := net.dial_tcp('google.com:80')? +// Simple http HEAD request for a file +conn.write_str('GET /index.html HTTP/1.0\r\n\r\n')? +// Make sure to set a timeout so we can wait for a response! +conn.set_read_timeout(10 * time.second) +// Wrap in a buffered reader +mut r := io.new_buffered_reader(reader: io.make_reader(conn)) +for { + l := r.read_line() or { + break + } + println('$l') + // Make it nice and obvious that we are doing this line by line + time.sleep_ms(10) +} diff --git a/examples/net_peer_ip.v b/examples/net_peer_ip.v new file mode 100644 index 0000000000..9c45c08b94 --- /dev/null +++ b/examples/net_peer_ip.v @@ -0,0 +1,5 @@ +import net + +conn := net.dial_tcp('google.com:80')? +peer_addr := conn.peer_addr()? +println('$peer_addr') diff --git a/examples/net_raw_http.v b/examples/net_raw_http.v index c03ad818bd..7ae5608b8a 100644 --- a/examples/net_raw_http.v +++ b/examples/net_raw_http.v @@ -1,14 +1,16 @@ // Simple raw HTTP head request -import x.net +import net import time +import io // Make a new connection mut conn := net.dial_tcp('google.com:80')? +defer { conn.close() } // Simple http HEAD request for a file -conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n')? +conn.write_str('HEAD /index.html HTTP/1.0\r\n\r\n')? // Make sure to set a timeout so we can wait for a response! -conn.set_read_timeout(10 * time.second) +conn.set_read_timeout(net.infinite_timeout) // Read all the data that is waiting -result := conn.read()? +result := io.read_all(conn)? // Cast to string and print result -println(result.bytestr()) \ No newline at end of file +println(result.bytestr()) diff --git a/examples/net_t.v b/examples/net_t.v new file mode 100644 index 0000000000..6ae4b7520c --- /dev/null +++ b/examples/net_t.v @@ -0,0 +1,23 @@ +import net.http +import sync +import time + +fn send_request(mut wg sync.WaitGroup) ?string { + start := time.ticks() + data := http.get('https://google.com')? + finish := time.ticks() + println('Finish getting time ${finish - start} ms') + wg.done() + return data.text +} + + + +fn main() { + mut wg := sync.new_waitgroup() + for i := 0; i < 50; i++ { + wg.add(1) + go send_request(mut wg) + } + wg.wait() +} diff --git a/examples/path_tracing.v b/examples/path_tracing.v index ad4c9221be..351bfccd3f 100644 --- a/examples/path_tracing.v +++ b/examples/path_tracing.v @@ -110,7 +110,7 @@ fn (image Image) save_as_ppm(file_name string) { c_r := to_int(unsafe{image.data[i]}.x) c_g := to_int(unsafe{image.data[i]}.y) c_b := to_int(unsafe{image.data[i]}.z) - f_out.write('$c_r $c_g $c_b ') + f_out.write_str('$c_r $c_g $c_b ') } f_out.close() } diff --git a/examples/tcp_echo_server.v b/examples/tcp_echo_server.v deleted file mode 100644 index c3a29364e8..0000000000 --- a/examples/tcp_echo_server.v +++ /dev/null @@ -1,45 +0,0 @@ -import net - -// This file shows how a basic TCP echo server can be implemented using -// the `net` module. You can connect to the server by using netcat -// or telnet, in separate shells, for example: -// `nc 127.0.0.1 12345` -// `telnet 127.0.0.1 12345` -fn handle_connection(con net.Socket) { - peer_ip := con.peer_ip() or { - '0.0.0.0' - } - eprintln('${peer_ip:16} | new client connected') - defer { - eprintln('${peer_ip:16} | closing connection...') - con.close() or { } - } - con.send_string("Welcome to V's TCP Echo server.\n") or { - return - } - for { - line := con.read_line() - if line.len == 0 { - return - } - eprintln('${peer_ip:16} | received line: ' + line.trim_space()) - con.send_string(line) or { - return - } - } -} - -fn main() { - server_port := 12345 - eprintln('Starting an echo server, listening on port: $server_port') - server := net.listen(server_port) or { - panic(err) - } - for { - con := server.accept() or { - server.close() or { } - panic(err) - } - go handle_connection(con) - } -} diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index e00620eaf4..a8966d6e55 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -620,6 +620,10 @@ pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int { return accum_ } +pub fn (mut a array) grow(amount int) { + a.ensure_cap(a.len + amount) +} + // array_eq checks if two arrays contain all the same elements in the same order. // []int == []int (also for: i64, f32, f64, byte, string) /* diff --git a/vlib/io/buffered_reader.v b/vlib/io/buffered_reader.v new file mode 100644 index 0000000000..c32993d6d3 --- /dev/null +++ b/vlib/io/buffered_reader.v @@ -0,0 +1,95 @@ +module io + +// BufferedReader provides a buffered interface for a reader +struct BufferedReader { +mut: + reader Reader + buf []byte + // current offset in the buffer + offset int + len int +} + +// BufferedReaderConfig are options that can be given to a reader +pub struct BufferedReaderConfig { + reader Reader + buf_cap int = 128 * 1024 // large for fast reading of big(ish) files +} + +// new_buffered_reader creates new BufferedReader +pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader { + assert o.buf_cap >= 2 + + // create + r := &BufferedReader{ + reader: o.reader + buf: []byte{len: o.buf_cap, cap: o.buf_cap} + offset: 0 + } + return r +} + +// read fufills the Reader interface +pub fn (mut r BufferedReader) read(mut buf []byte) ?int { + // read data out of the buffer if we dont have any + if r.offset >= r.len-1 { + r.fill_buffer()? + } + + read := copy(buf, r.buf[r.offset..r.len]) + r.offset += read + + return read +} + +// fill buffer attempts to refill the internal buffer +fn (mut r BufferedReader) fill_buffer() ? { + // TODO we should keep track of when we get an end of stream + // from the upstream reader so that we dont have to keep + // trying to call this + r.offset = 0 + r.len = r.reader.read(mut r.buf) or { + 0 + } +} + +// read_line reads a line from the buffered reader +pub fn (mut r BufferedReader) read_line() ?string { + mut line := []byte{} + for { + if r.offset >= (r.len-1) { + // go fetch some new data + r.fill_buffer()? + } + + if r.len == 0 { + // if we have no data then return nothing + return none + } + + // try and find a newline character + mut i := r.offset + for ; i < r.len; i++ { + c := r.buf[i] + if c == `\n` { + // great, we hit something + // do some checking for whether we hit \r\n or just \n + + if i != 0 && r.buf[i-1] == `\r` { + x := i-1 + line << r.buf[r.offset..x] + } else { + line << r.buf[r.offset..i] + } + + r.offset = i + 1 + + return line.bytestr() + } + } + + line << r.buf[r.offset..i] + + r.offset = i + } +} diff --git a/vlib/io/net_stream_test.v b/vlib/io/net_stream_test.v deleted file mode 100644 index a0ff86e457..0000000000 --- a/vlib/io/net_stream_test.v +++ /dev/null @@ -1,181 +0,0 @@ -import net -import io - -struct Test { - a int - b f32 - c string -} - -fn setup() (net.Socket, net.Socket, net.Socket) { - server := net.listen(0) or { panic(err) } - server_port := server.get_port() - client := net.dial('127.0.0.1', server_port) or { panic(err) } - socket := server.accept() or { panic(err) } - return server, client, socket -} - -fn test_arrays() { - _, c, s := setup() - - mut nos := io.new_net_output_stream(s) - mut nis := io.new_net_input_stream(c) - - //bytes - a := [byte(76), 7, 43, 5] - nos.write_bytes(a) or { assert false } - c_a := nis.read_bytes(4) - assert a == c_a - - //u16s - b := [u16(546), 3434, 33] - nos.write_u16s(b) or { assert false } - c_b := nis.read_u16s(3) - assert b == c_b - - //u32s - d := [u32(10), 34324, 454, 34, 352] - nos.write_u32s(d) or { assert false } - c_d := nis.read_u32s(5) - assert d == c_d - - //u64s - e := [u64(32542), 23213, 23244353, 324534534] - nos.write_u64s(e) or { assert false } - c_e := nis.read_u64s(4) - assert e == c_e - - //i8s - f := [i8(20), 40, 5, 10] - nos.write_i8s(f) or { assert false } - c_f := nis.read_i8s(4) - assert f == c_f - - //i16s - g := [i16(3434), 3242, 655, 445, 23] - nos.write_i16s(g) or { assert false } - c_g := nis.read_i16s(5) - assert g == c_g - - //i32s - h := [342, 32424, 565, 343, 7676, 34] - nos.write_ints(h) or { assert false } - c_h := nis.read_ints(6) - assert h == c_h - //i64s - i := [i64(354345), 45435564, 54645654, 3242342] - nos.write_i64s(i) or { assert false } - c_i := nis.read_i64s(4) - assert i == c_i -} - -fn test_primitives() { - _, c, s := setup() - - mut nos := io.new_net_output_stream(s) - mut nis := io.new_net_input_stream(c) - - // bytes - a := byte(45) - nos.write_byte(a) or { assert false } - c_a := nis.read_byte() - assert a == c_a - - // u16 - b := u16(345) - nos.write_u16(b) or { assert false } - c_b := nis.read_u16() - assert b == c_b - - // u32 - d := u32(353453) - nos.write_u32(d) or { assert false } - c_d := nis.read_u32() - assert d == c_d - - // u64 - e := u64(43645645654) - nos.write_u64(e) or { assert false } - c_e := nis.read_u64() - assert e == c_e - - // i8 - f := i8(43) - nos.write_i8(f) or { assert false } - c_f := nis.read_i8() - assert f == c_f - - // i16 - g := i16(676) - nos.write_i16(g) or { assert false } - c_g := nis.read_i16() - assert g == c_g - - // int - h := 4543565 - nos.write_int(h) or { assert false } - c_h := nis.read_int() - assert h == c_h - - // i64 - i := i64(4343654654654) - nos.write_i64(i) or { assert false } - c_i := nis.read_i64() - assert i == c_i -} - -fn test_floats() { - _, c, s := setup() - - mut nos := io.new_net_output_stream(s) - mut nis := io.new_net_input_stream(c) - - a := f32(7.5) - nos.write_f32(a) or { assert false } - c_a := nis.read_f32() - assert a == c_a - - b := f64(43587349857834579834.45435435) - nos.write_f64(b) or { assert false } - c_b := nis.read_f64() - assert b == c_b - - d := [f32(7.3), 3.45, 546.3, 4545.3] - nos.write_f32s(d) or { assert false } - c_d := nis.read_f32s(4) - assert d == c_d - - e := [f64(345324324.3242342), 3243242.342, 344564.343242423, 43543.43534, 34234.34324] - nos.write_f64s(e) or { assert false } - c_e := nis.read_f64s(5) - assert e == c_e -} - -fn test_string() { - _, c, s := setup() - - mut nos := io.new_net_output_stream(s) - mut nis := io.new_net_input_stream(c) - - a := 'hello' - nos.write_string(a) or { assert false } - c_a := nis.read_string(5) - assert a == c_a -} - -fn test_struct() { - _, c, s := setup() - - mut nos := io.new_net_output_stream(s) - mut nis := io.new_net_input_stream(c) - - a := Test{ - a: 1 - b: 2.0 - c: 'test' - } - nos.write_struct(a, sizeof(Test)) or { assert false } - got := &Test(nis.read_struct(sizeof(Test))) - de_ref := *got - assert a.a == de_ref.a && a.b == de_ref.b && a.c == de_ref.c -} diff --git a/vlib/io/netinstream.v b/vlib/io/netinstream.v deleted file mode 100644 index adeb8e7d81..0000000000 --- a/vlib/io/netinstream.v +++ /dev/null @@ -1,188 +0,0 @@ -module io - -import net -import encoding.binary - -struct NetInputStream { -mut: - sock &net.Socket -} - -pub fn new_net_input_stream(sock &net.Socket) &NetInputStream { - return &NetInputStream{ - sock: sock - } -} - -pub fn (mut nis NetInputStream) read_int() int { - return int(binary.big_endian_u32(nis.read_bytes(sizeof(int)))) -} - -pub fn (mut nis NetInputStream) read_ints(l u32) []int { - bytes := nis.read_bytes(sizeof(int) * l) - mut ints := []int{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(int)) - b := bytes[offs..int(u32(offs) + sizeof(int))] - ints << int(binary.big_endian_u32(b)) - } - return ints -} - -pub fn (mut nis NetInputStream) read_i8() i8 { - return i8(nis.read_byte()) -} - -pub fn (mut nis NetInputStream) read_i8s(l u32) []i8 { - bytes := nis.read_bytes(sizeof(i8) * l) - mut i8s := []i8{} - for i in 0 .. l { - i8s << i8(bytes[i]) - } - return i8s -} - -pub fn (mut nis NetInputStream) read_i16() i16 { - return i16(binary.big_endian_u16(nis.read_bytes(sizeof(i16)))) -} - -pub fn (mut nis NetInputStream) read_i16s(l u32) []i16 { - bytes := nis.read_bytes(sizeof(i16) * l) - mut i16s := []i16{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(i16)) - b := bytes[offs..int(u32(offs) + sizeof(i16))] - i16s << i16(binary.big_endian_u16(b)) - } - return i16s -} - -pub fn (mut nis NetInputStream) read_i64() i64 { - return i64(binary.big_endian_u64(nis.read_bytes(sizeof(i64)))) - -} - -pub fn (mut nis NetInputStream) read_i64s(l u32) []i64 { - bytes := nis.read_bytes(sizeof(i64) * l) - mut i64s := []i64{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(i64)) - b := bytes[offs..int(u32(offs) + sizeof(i64))] - i64s << i64(binary.big_endian_u64(b)) - } - return i64s -} - -pub fn (mut nis NetInputStream) read_byte() byte { - ptr, _ := nis.sock.recv(int(sizeof(byte))) - unsafe { - return ptr[0] - } -} - -pub fn (mut nis NetInputStream) read_bytes(l u32) []byte { - mut bytes := []byte{len: int(l), cap: int(l)} - for i in 0..l { - bytes[i] = nis.read_byte() - } - return bytes -} - -pub fn (mut nis NetInputStream) read_u16() u16 { - return binary.big_endian_u16(nis.read_bytes(sizeof(u16))) -} - -pub fn (mut nis NetInputStream) read_u16s(l u32) []u16 { - bytes := nis.read_bytes(sizeof(u16) * l) - mut u16s := []u16{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(u16)) - b := bytes[offs..int(u32(offs) + sizeof(u16))] - u16s << binary.big_endian_u16(b) - - } - return u16s -} - -pub fn (mut nis NetInputStream) read_u32() u32 { - return binary.big_endian_u32(nis.read_bytes(sizeof(u32))) -} - -pub fn (mut nis NetInputStream) read_u32s(l u32) []u32 { - bytes := nis.read_bytes(sizeof(u32) * l) - mut u32s := []u32{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(u32)) - b := bytes[offs..int(u32(offs) + sizeof(u32))] - u32s << binary.big_endian_u32(b) - } - return u32s -} - -pub fn (mut nis NetInputStream) read_u64() u64 { - return binary.big_endian_u64(nis.read_bytes(sizeof(u64))) -} - -pub fn (mut nis NetInputStream) read_u64s(l u32) []u64 { - bytes := nis.read_bytes(sizeof(u64) * l) - mut u64s := []u64{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(u64)) - b := bytes[offs..int(u32(offs) + sizeof(u64))] - u64s << binary.big_endian_u64(b) - } - return u64s -} -pub fn (mut nis NetInputStream) read_f32() f32 { - bytes := nis.read_bytes(sizeof(f32)) - f := &f32(bytes.data) - return *f -} - -pub fn (mut nis NetInputStream) read_f32s(l u32) []f32 { - bytes := nis.read_bytes(sizeof(f32) * l) - mut f32s := []f32{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(f32)) - b := bytes[offs..int(u32(offs) + sizeof(f32))] - f := &f32(b.data) - unsafe { - f32s << *f - } - } - return f32s -} - -pub fn (mut nis NetInputStream) read_f64() f64 { - bytes := nis.read_bytes(sizeof(f64)) - f := &f64(bytes.data) - return *f -} - -pub fn (mut nis NetInputStream) read_f64s(l u32) []f64 { - bytes := nis.read_bytes(sizeof(f64) * l) - mut f64s := []f64{} - for i in 0 .. l { - offs := int(u32(i) * sizeof(f64)) - b := bytes[offs..int(u32(offs) + sizeof(f64))] - f := &f64(b.data) - unsafe { - f64s << *f - } - } - return f64s -} - -pub fn (mut nis NetInputStream) read_string(l u32) string { - bytes := nis.read_bytes(l) - return tos(bytes.data, bytes.len) -} - -pub fn (mut nis NetInputStream) skip(l u32) { - nis.read_bytes(l) -} - -// TODO make it generic -pub fn (mut nis NetInputStream) read_struct(l u32) voidptr { - return voidptr(nis.read_bytes(l).data) -} diff --git a/vlib/io/netoutstream.v b/vlib/io/netoutstream.v deleted file mode 100644 index 6275375a78..0000000000 --- a/vlib/io/netoutstream.v +++ /dev/null @@ -1,184 +0,0 @@ -module io - -import net -import encoding.binary - -struct NetOutputStream { -mut: - sock &net.Socket -} - -pub fn new_net_output_stream(sock &net.Socket) &NetOutputStream { - return &NetOutputStream{ - sock: sock - } -} - -pub fn (mut nos NetOutputStream) write_int(d int) ? { - mut bytes := []byte{len: int(sizeof(int))} - binary.big_endian_put_u32(mut bytes, u32(d)) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_ints(d []int) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(int))} - binary.big_endian_put_u32(mut tmp, u32(u)) - bytes << tmp - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_i8(d i8) ? { - nos.sock.send([byte(d)].data, int(sizeof(i8)))? -} - -pub fn (mut nos NetOutputStream) write_i8s(d []i8) ? { - nos.sock.send(d.data, int(sizeof(i8) * u32(d.len)))? -} - -pub fn (mut nos NetOutputStream) write_i16(d i16) ? { - mut bytes := []byte{len: int(sizeof(i16))} - binary.big_endian_put_u16(mut bytes, u16(d)) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_i16s(d []i16) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(i16))} - binary.big_endian_put_u16(mut tmp, u16(u)) - bytes << tmp - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_i64(d i64) ? { - mut bytes := []byte{len: int(sizeof(i64))} - binary.big_endian_put_u64(mut bytes, u64(d)) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_i64s(d []i64) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(i64))} - binary.big_endian_put_u64(mut tmp, u64(u)) - bytes << tmp - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_byte(d byte) ? { - nos.sock.send([d].data, int(sizeof(byte)))? -} - -pub fn (mut nos NetOutputStream) write_bytes(d []byte) ? { - nos.sock.send(d.data, int(sizeof(byte) * u32(d.len)))? -} - -pub fn (mut nos NetOutputStream) write_u16(d u16) ? { - mut bytes := []byte{len: int(sizeof(u16))} - binary.big_endian_put_u16(mut bytes, d) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_u16s(d []u16) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(u16))} - binary.big_endian_put_u16(mut tmp, u) - bytes << tmp - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_u32(d u32) ? { - mut bytes := []byte{len: int(sizeof(u32))} - binary.big_endian_put_u32(mut bytes, d) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_u32s(d []u32) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(u32))} - binary.big_endian_put_u32(mut tmp, u) - bytes << tmp - } - nos.sock.send(bytes.data, int(sizeof(u32) * u32(d.len)))? -} - -pub fn (mut nos NetOutputStream) write_u64(d u64) ? { - mut bytes := []byte{len: int(sizeof(u64))} - binary.big_endian_put_u64(mut bytes, d) - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_u64s(d []u64) ? { - mut bytes := []byte{} - for u in d { - mut tmp := []byte{len: int(sizeof(u64))} - binary.big_endian_put_u64(mut tmp, u) - bytes << tmp - } - nos.sock.send(bytes.data, int(sizeof(u64) * u32(d.len)))? -} - -pub fn (mut nos NetOutputStream) write_f32(d f32) ? { - pb := &byte(&d) - mut bytes := []byte{len: int(sizeof(f32))} - unsafe { - for i in 0..bytes.len { - bytes[i] = pb[i] - } - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_f32s(d []f32) ? { - mut bytes := []byte{} - for f in d { - pb := &byte(&f) - unsafe { - for i in 0..int(sizeof(f32)) { - bytes << pb[i] - } - } - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_f64(d f64) ? { - pb := &byte(&d) - mut bytes := []byte{len: int(sizeof(f64))} - unsafe { - for i in 0..bytes.len { - bytes[i] = pb[i] - } - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_f64s(d []f64) ? { - mut bytes := []byte{} - for f in d { - pb := &byte(&f) - unsafe { - for i in 0..int(sizeof(f64)) { - bytes << pb[i] - } - } - } - nos.sock.send(bytes.data, bytes.len)? -} - -pub fn (mut nos NetOutputStream) write_string(d string) ? { - nos.write_bytes(d.bytes())? -} - -// TODO make it generic -pub fn (mut nos NetOutputStream) write_struct(d voidptr, l u32) ? { - nos.sock.send(byteptr(d), int(l))? -} diff --git a/vlib/io/reader.v b/vlib/io/reader.v new file mode 100644 index 0000000000..5ca59b17f0 --- /dev/null +++ b/vlib/io/reader.v @@ -0,0 +1,45 @@ +module io + +// Reader represents a stream of data that can be read +pub interface Reader { + // read reads up to buf.len bytes and places + // them into buf. + // A struct that implements this should return + // `none` on end of stream (EOF) instead of just returning 0 + read(mut buf []byte) ?int +} + +// make_reader is a temp that converts a struct to a reader +// (e.g. for use in struct initialisation) +pub fn make_reader(r Reader) Reader { + return r +} + +const ( + read_all_len = 10 * 1024 + read_all_grow_len = 1024 +) + +// read_all reads all available bytes from a reader +pub fn read_all(r Reader) ?[]byte { + mut b := []byte{len:read_all_len} + mut read := 0 + for { + new_read := r.read(mut b[read..]) or { + break + } + read += new_read + if new_read == 0 { + break + } + if b.len == read { + b.grow(read_all_grow_len) + } + } + return b[..read] +} + +// RandomReader represents a stream of data that can be read from at a random location +interface RandomReader { + read_from(pos int, mut buf []byte) ?int +} diff --git a/vlib/io/readerwriter.v b/vlib/io/readerwriter.v new file mode 100644 index 0000000000..31965eb85a --- /dev/null +++ b/vlib/io/readerwriter.v @@ -0,0 +1,45 @@ +module io + +// ReaderWriter represents a stream that can be read from and wrote to +pub interface ReaderWriter { + // from Reader + read(mut buf []byte) ?int + + // from Writer + write(buf []byte) ?int +} + +// ReaderWriterImpl is a ReaderWriter that can be made from +// a seperate reader and writer (see fn make_readerwriter) +struct ReaderWriterImpl { + r Reader + w Writer +} + +pub fn (mut r ReaderWriterImpl) read(mut buf []byte) ?int { + return r.r.read(mut buf) +} + +pub fn (mut r ReaderWriterImpl) write(buf []byte) ?int { + return r.w.write(buf) +} + +// make_readerwriter takes a rstream and a wstream and makes +// an rwstream with them +pub fn make_readerwriter(r Reader, w Writer) ReaderWriterImpl { + return {r: r, w: w} +} + +struct Zzz_CoerceInterfaceTableGeneration { +} + +fn (_ Zzz_CoerceInterfaceTableGeneration) write(buf []byte) ?int { +} + +fn (_ Zzz_CoerceInterfaceTableGeneration) read(mut buf []byte) ?int { +} + +fn zzz_reader_writer_coerce_compiler() { + x := Zzz_CoerceInterfaceTableGeneration{} + _ := make_readerwriter(x, x) +} diff --git a/vlib/io/writer.v b/vlib/io/writer.v new file mode 100644 index 0000000000..e647e189a3 --- /dev/null +++ b/vlib/io/writer.v @@ -0,0 +1,12 @@ +module io + +// Writer represents a stream of data that can be wrote to +pub interface Writer { + write(buf []byte) ?int +} + +// RandomWriter represents a stream of data that can be wrote to +// at a random pos +pub interface RandomWriter { + write_to(pos int, buf []byte) ?int +} diff --git a/vlib/x/net/aasocket.c.v b/vlib/net/aasocket.c.v similarity index 94% rename from vlib/x/net/aasocket.c.v rename to vlib/net/aasocket.c.v index a89035faaa..e02f9c3867 100644 --- a/vlib/x/net/aasocket.c.v +++ b/vlib/net/aasocket.c.v @@ -22,6 +22,7 @@ mut: } struct C.sockaddr { + sa_family u16 } struct C.sockaddr_in { @@ -75,9 +76,11 @@ fn C.shutdown() int fn C.ntohs() int +fn C.getpeername() int + fn C.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr -fn C.WSAStringToAddress() int +fn C.WSAAddressToStringA() int fn C.getsockname() int diff --git a/vlib/net/address.v b/vlib/net/address.v new file mode 100644 index 0000000000..38eef6c92f --- /dev/null +++ b/vlib/net/address.v @@ -0,0 +1,81 @@ +module net + +// Addr represents an ip address +pub struct Addr { + addr C.sockaddr + len int +pub: + saddr string + port int +} + +pub fn (a Addr) str() string { + return '${a.saddr}:${a.port}' +} + +const ( + max_ipv4_addr_len = 24 + ipv4_addr_size = sizeof(C.sockaddr_in) +) + +fn new_addr(addr C.sockaddr) ?Addr { + addr_len := if addr.sa_family == SocketFamily.inet { + sizeof(C.sockaddr) + } else { + // TODO NOOOOOOOOOOOO + 0 + } + // Convert to string representation + buf := []byte{ len: max_ipv4_addr_len, init: 0 } + $if windows { + res := C.WSAAddressToStringA(&addr, addr_len, C.NULL, buf.data, &buf.len) + if res != 0 { + socket_error(-1)? + } + } $else { + res := C.inet_ntop(SocketFamily.inet, &addr, buf.data, buf.len) + if res == 0 { + socket_error(-1)? + } + } + mut saddr := buf.bytestr() + + hport := (&C.sockaddr_in(&addr)).sin_port + port := C.ntohs(hport) + + $if windows { + // strip the port from the address string + saddr = saddr.split(':')[0] + } + + return Addr { + addr int(addr_len) saddr port + } +} + +pub fn resolve_addr(addr string, family SocketFamily, typ SocketType) ?Addr { + address, port := split_address(addr)? + + mut hints := C.addrinfo{} + hints.ai_family = family + hints.ai_socktype = typ + hints.ai_flags = C.AI_PASSIVE + hints.ai_protocol = 0 + hints.ai_addrlen = 0 + hints.ai_canonname = C.NULL + hints.ai_addr = C.NULL + hints.ai_next = C.NULL + info := &C.addrinfo(0) + + sport := '$port' + + // This might look silly but is recommended by MSDN + $if windows { + socket_error(0-C.getaddrinfo(address.str, sport.str, &hints, &info))? + } $else { + x := C.getaddrinfo(address.str, sport.str, &hints, &info) + wrap_error(x)? + } + + return new_addr(*info.ai_addr) +} diff --git a/vlib/x/net/common.v b/vlib/net/common.v similarity index 100% rename from vlib/x/net/common.v rename to vlib/net/common.v diff --git a/vlib/x/net/errors.v b/vlib/net/errors.v similarity index 81% rename from vlib/x/net/errors.v rename to vlib/net/errors.v index f69b7edeab..903e05e86e 100644 --- a/vlib/x/net/errors.v +++ b/vlib/net/errors.v @@ -39,6 +39,21 @@ pub fn wrap_error(error_code int) ? { return error_with_code('socket error: $enum_error', error_code) } $else { + if error_code == 0 { + return + } return error_with_code('net: socket error: $error_code', error_code) } } + +// wrap_read_result takes a read result and sees if it is 0 for graceful +// connection termination and returns none +// e.g. res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0))? +[inline] +fn wrap_read_result(result int) ?int { + if result > 0 || result < 0 { + return result + } + + return none +} diff --git a/vlib/net/ftp/ftp.v b/vlib/net/ftp/ftp.v index 97eee0c7d4..fd92b27df4 100644 --- a/vlib/net/ftp/ftp.v +++ b/vlib/net/ftp/ftp.v @@ -1,3 +1,5 @@ +module ftp + /* basic ftp module RFC-959 @@ -15,9 +17,9 @@ basic ftp module dtp.close() ftp.close() */ -module ftp import net +import io const ( connected = 220 @@ -35,33 +37,26 @@ const ( struct DTP { mut: - sock net.Socket + conn net.TcpConn + reader io.BufferedReader ip string port int } -fn (dtp DTP) read() []byte { - mut data := []byte{} - for { - buf, len := dtp.sock.recv(1024) - if len == 0 { - break - } - for i in 0 .. len { - data << unsafe {buf[i]} - } - unsafe {free(buf)} - } +fn (mut dtp DTP) read() ?[]byte { + mut data := []byte{len: 1024} + dtp.reader.read(mut data)? return data } fn (dtp DTP) close() { - dtp.sock.close() or { } + dtp.conn.close() } struct FTP { mut: - sock net.Socket + conn net.TcpConn + reader io.BufferedReader buffer_size int } @@ -71,18 +66,15 @@ pub fn new() FTP { return f } -fn (ftp FTP) write(data string) ?int { +fn (mut ftp FTP) write(data string) ? { $if debug { println('FTP.v >>> $data') } - n := ftp.sock.send_string('$data\r\n') or { - return error('Cannot send data') - } - return n + ftp.conn.write('$data\r\n'.bytes())? } -fn (ftp FTP) read() (int, string) { - mut data := ftp.sock.read_line() +fn (mut ftp FTP) read() ?(int, string) { + mut data := ftp.reader.read_line()? $if debug { println('FTP.v <<< $data') } @@ -92,7 +84,7 @@ fn (ftp FTP) read() (int, string) { code := data[..3].int() if data[3] == `-` { for { - data = ftp.sock.read_line() + data = ftp.reader.read_line()? if data[..3].int() == code && data[3] != `-` { break } @@ -101,26 +93,25 @@ fn (ftp FTP) read() (int, string) { return code, data } -pub fn (mut ftp FTP) connect(ip string) bool { - sock := net.dial(ip, 21) or { - return false - } - ftp.sock = sock - code, _ := ftp.read() +pub fn (mut ftp FTP) connect(ip string) ?bool { + conn := net.dial_tcp('$ip:21')? + ftp.conn = conn + code, _ := ftp.read()? if code == connected { return true } + ftp.reader = io.new_buffered_reader(reader: io.make_reader(ftp.conn)) return false } -pub fn (ftp FTP) login(user string, passwd string) bool { +pub fn (mut ftp FTP) login(user string, passwd string) ?bool { ftp.write('USER $user') or { $if debug { println('ERROR sending user') } return false } - mut code, _ := ftp.read() + mut code, _ := ftp.read()? if code == logged_in { return true } @@ -133,24 +124,21 @@ pub fn (ftp FTP) login(user string, passwd string) bool { } return false } - code, _ = ftp.read() + code, _ = ftp.read()? if code == logged_in { return true } return false } -pub fn (ftp FTP) close() { - send_quit := 'QUIT\r\n' - ftp.sock.send_string(send_quit) or { } - ftp.sock.close() or { } +pub fn ( mut ftp FTP) close() ? { + ftp.write('QUIT')? + ftp.conn.close() } -pub fn (ftp FTP) pwd() string { - ftp.write('PWD') or { - return '' - } - _, data := ftp.read() +pub fn ( mut ftp FTP) pwd() ?string { + ftp.write('PWD')? + _, data := ftp.read()? spl := data.split('"') // " if spl.len >= 2 { return spl[1] @@ -158,11 +146,11 @@ pub fn (ftp FTP) pwd() string { return data } -pub fn (ftp FTP) cd(dir string) { +pub fn ( mut ftp FTP) cd(dir string) ? { ftp.write('CWD $dir') or { return } - mut code, mut data := ftp.read() + mut code, mut data := ftp.read()? match int(code) { denied { $if debug { @@ -170,7 +158,7 @@ pub fn (ftp FTP) cd(dir string) { } } complete { - code, data = ftp.read() + code, data = ftp.read()? } else {} } @@ -184,20 +172,21 @@ fn new_dtp(msg string) ?DTP { return error('Bad message') } ip, port := get_host_ip_from_dtp_message(msg) - sock := net.dial(ip, port) or { + conn := net.dial_tcp('$ip:$port') or { return error('Cannot connect to the data channel') } dtp := DTP{ - sock: sock + conn: conn ip: ip port: port } return dtp } -fn (ftp FTP) pasv() ?DTP { - ftp.write('PASV') or { } - code, data := ftp.read() +fn ( mut ftp FTP) pasv() ?DTP { + ftp.write('PASV') or { + } + code, data := ftp.read()? $if debug { println('pass: $data') } @@ -208,20 +197,21 @@ fn (ftp FTP) pasv() ?DTP { return dtp } -pub fn (ftp FTP) dir() ?[]string { - dtp := ftp.pasv() or { +pub fn ( mut ftp FTP) dir() ?[]string { + mut dtp := ftp.pasv() or { return error('cannot establish data connection') } - ftp.write('LIST') or { } - code, _ := ftp.read() + ftp.write('LIST') or { + } + code, _ := ftp.read()? if code == denied { return error('LIST denied') } if code != open_data_connection { return error('data channel empty') } - list_dir := dtp.read() - result, _ := ftp.read() + list_dir := dtp.read()? + result, _ := ftp.read()? if result != close_data_connection { println('LIST not ok') } @@ -237,19 +227,20 @@ pub fn (ftp FTP) dir() ?[]string { return dir } -pub fn (ftp FTP) get(file string) ?[]byte { - dtp := ftp.pasv() or { +pub fn (mut ftp FTP) get(file string) ?[]byte { + mut dtp := ftp.pasv() or { return error('Cannot stablish data connection') } - ftp.write('RETR $file') or { } - code, _ := ftp.read() + ftp.write('RETR $file') or { + } + code, _ := ftp.read()? if code == denied { return error('Permission denied') } if code != open_data_connection { return error('Data connection not ready') } - blob := dtp.read() + blob := dtp.read()? dtp.close() return blob } diff --git a/vlib/net/ftp/ftp_test.v b/vlib/net/ftp/ftp_test.v index a60bef9370..22ade360ee 100644 --- a/vlib/net/ftp/ftp_test.v +++ b/vlib/net/ftp/ftp_test.v @@ -3,15 +3,17 @@ import net.ftp // NB: this function makes network calls to external servers, // that is why it is not a very good idea to run it in CI. // If you want to run it manually, use `v -d network vlib/net/ftp/ftp_test.v` -fn test_ftp_client() { +fn ftp_client_test_inside() ? { $if !network ? { return } mut ftp := ftp.new() defer { ftp.close() } - assert ftp.connect('ftp.redhat.com') - assert ftp.login('ftp', 'ftp') - pwd := ftp.pwd() + connect_result := ftp.connect('ftp.redhat.com')? + assert connect_result + login_result := ftp.login('ftp', 'ftp')? + assert login_result + pwd := ftp.pwd()? assert pwd.len > 0 ftp.cd('/') dir_list1 := ftp.dir() or { @@ -31,3 +33,10 @@ fn test_ftp_client() { } assert blob.len > 0 } + + +fn test_ftp_cleint() { + ftp_client_test_inside() or { + panic(err) + } +} diff --git a/vlib/net/http/http.v b/vlib/net/http/http.v index 8c7a5b2f1f..dc84d97cbd 100644 --- a/vlib/net/http/http.v +++ b/vlib/net/http/http.v @@ -5,8 +5,9 @@ module http import net.urllib import net.http.chunked -import strings import net +import time +import io const ( max_redirects = 4 @@ -276,7 +277,7 @@ fn (req &Request) method_and_url_to_response(method Method, url urllib.URL) ?Res return res } else if scheme == 'http' { // println('http_do( $nport, $method, $host_name, $path )') - res := req.http_do(nport, method, host_name, path) ? + res := req.http_do('$host_name:$nport', method, path)? return res } return error('http.request.method_and_url_to_response: unsupported scheme: "$scheme"') @@ -392,24 +393,16 @@ pub fn escape(s string) string { panic('http.escape() was replaced with http.escape_url()') } -fn (req &Request) http_do(port int, method Method, host_name string, path string) ?Response { - rbuffer := [bufsize]byte{} - mut sb := strings.new_builder(100) +fn (req &Request) http_do(host string, method Method, path string) ?Response { + host_name, _ := net.split_address(host)? s := req.build_request_headers(method, host_name, path) - client := net.dial(host_name, port) ? - client.send(s.str, s.len) or { } - for { - readbytes := client.crecv(rbuffer, bufsize) - if readbytes < 0 { - return error('http.request.http_do: error reading response. readbytes=$readbytes') - } - if readbytes == 0 { - break - } - sb.write(tos(rbuffer, readbytes)) - } - client.close() or { } - return parse_response(sb.str()) + mut client := net.dial_tcp(host)? + // TODO this really needs to be exposed somehow + client.set_read_timeout(time.second * 30) + client.write(s.bytes())? + mut bytes := io.read_all(client)? + client.close() + return parse_response(bytes.bytestr()) } pub fn (req &Request) referer() string { diff --git a/vlib/net/init_nix.c.v b/vlib/net/init_nix.c.v deleted file mode 100644 index c31100f22f..0000000000 --- a/vlib/net/init_nix.c.v +++ /dev/null @@ -1,17 +0,0 @@ -module net - -#include -#include -#include -#include -#include -#include -fn error_code() int { - return C.errno -} - -pub const ( - msg_nosignal = 0x4000 -) - -#flag solaris -lsocket diff --git a/vlib/net/init_windows.c.v b/vlib/net/init_windows.c.v deleted file mode 100644 index 442ffe91c5..0000000000 --- a/vlib/net/init_windows.c.v +++ /dev/null @@ -1,37 +0,0 @@ -module net - -#flag -lws2_32 -#include -#include - -struct C.WSAData { -mut: - wVersion u16 - wHighVersion u16 - szDescription [257]byte - szSystemStatus [129]byte - iMaxSockets u16 - iMaxUdpDg u16 - lpVendorInfo byteptr -} - - -const ( - wsa_v22 = 0x202 // C.MAKEWORD(2, 2) -) - -fn init() { - mut wsadata := C.WSAData{} - res := C.WSAStartup(wsa_v22, &wsadata) - if res != 0 { - panic('socket: WSAStartup failed') - } -} - -fn error_code() int { - return C.WSAGetLastError() -} - -pub const ( - msg_nosignal = 0 -) diff --git a/vlib/net/net.v b/vlib/net/net.v deleted file mode 100644 index 1123a1dd63..0000000000 --- a/vlib/net/net.v +++ /dev/null @@ -1,16 +0,0 @@ -module net - -fn C.gethostname() int -// hostname returns the host name reported by the kernel. -pub fn hostname() ?string { - mut name := [256]byte{} - // https://www.ietf.org/rfc/rfc1035.txt - // The host name is returned as a null-terminated string. - namebp := byteptr(name) - res := C.gethostname(namebp, 256) - if res != 0 { - return error('net.hostname: failed with $res') - } - return tos_clone(namebp) -} - diff --git a/vlib/x/net/net_nix.c.v b/vlib/net/net_nix.c.v similarity index 100% rename from vlib/x/net/net_nix.c.v rename to vlib/net/net_nix.c.v diff --git a/vlib/x/net/net_windows.c.v b/vlib/net/net_windows.c.v similarity index 100% rename from vlib/x/net/net_windows.c.v rename to vlib/net/net_windows.c.v diff --git a/vlib/net/smtp/smtp.v b/vlib/net/smtp/smtp.v index b391adfa84..9953039220 100644 --- a/vlib/net/smtp/smtp.v +++ b/vlib/net/smtp/smtp.v @@ -9,6 +9,7 @@ import net import encoding.base64 import strings import time +import io const ( recv_size = 128 @@ -29,7 +30,8 @@ pub enum BodyType { pub struct Client { mut: - socket net.Socket + conn net.TcpConn + reader io.BufferedReader pub: server string port int = 25 @@ -62,8 +64,10 @@ pub fn new_client(config Client) ?Client { pub fn (mut c Client) reconnect() ? { if c.is_open { return error('Already connected to server') } - socket := net.dial(c.server, c.port) or { return error('Connecting to server failed') } - c.socket = socket + conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') } + c.conn = conn + + c.reader = io.new_buffered_reader(reader: io.make_reader(c.conn)) c.expect_reply(.ready) or { return error('Received invalid response from server') } c.send_ehlo() or { return error('Sending EHLO packet failed') } @@ -85,35 +89,21 @@ pub fn (c Client) send(config Mail) ? { pub fn (mut c Client) quit() ? { c.send_str('QUIT\r\n') c.expect_reply(.close) - c.socket.close()? + c.conn.close()? c.is_open = false } // expect_reply checks if the SMTP server replied with the expected reply code fn (c Client) expect_reply(expected ReplyCode) ? { - bytes, len := c.socket.recv(recv_size) + bytes := io.read_all(c.conn)? - str := tos(bytes, len).trim_space() + str := bytes.bytestr().trim_space() $if smtp_debug? { - eprintln('\n\n[RECV START]') + eprintln('\n\n[RECV]') eprint(str) } - // Read remaining data in the socket - if len >= recv_size { - for { - tbytes, tlen := c.socket.recv(recv_size) - str2 := tos(tbytes, tlen) - $if smtp_debug? { eprint(str2) } - if tlen < recv_size { break } - } - } - - $if smtp_debug? { - eprintln('\n[RECV END]') - } - - if len >= 3 { + if str.len >= 3 { status := str[..3].int() if status != expected { return error('Received unexpected status code $status, expecting $expected') @@ -128,7 +118,7 @@ fn (c Client) send_str(s string) ? { eprint(s.trim_space()) eprintln('\n[SEND END]') } - c.socket.send_string(s)? + c.conn.write(s.bytes())? } [inline] diff --git a/vlib/net/socket.v b/vlib/net/socket.v deleted file mode 100644 index 12e7ad151a..0000000000 --- a/vlib/net/socket.v +++ /dev/null @@ -1,392 +0,0 @@ -module net - -import os - -pub struct Socket { -pub: - sockfd int - family int - typ int - proto int -pub mut: - max_single_send_size int = 64000 -} - -struct C.in_addr { -mut: - s_addr int -} - -struct C.sockaddr { -} - -struct C.sockaddr_in { -mut: - sin_family int - sin_port int - sin_addr C.in_addr -} - -struct C.addrinfo { -mut: - ai_family int - ai_socktype int - ai_flags int - ai_protocol int - ai_addrlen int - ai_addr voidptr - ai_canonname voidptr - ai_next voidptr -} - -struct C.sockaddr_storage { -} - -fn C.socket() int - -fn C.setsockopt() int - -fn C.htonl() int - -fn C.htons() int - -fn C.bind() int - -fn C.listen() int - -fn C.accept() int - -fn C.getaddrinfo() int - -fn C.connect() int - -fn C.send() int - -fn C.recv() int - -fn C.shutdown() int - -fn C.ntohs() int - -fn C.getsockname() int - -fn C.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr - -fn C.getpeername(sockfd int, addr &C.sockaddr_in, addrsize &int) int - -// create socket -pub fn new_socket(family int, typ int, proto int) ?Socket { - sockfd := C.socket(family, typ, proto) - one := 1 - // This is needed so that there are no problems with reusing the - // same port after the application exits. - C.setsockopt(sockfd, C.SOL_SOCKET, C.SO_REUSEADDR, &one, sizeof(voidptr)) - if sockfd == -1 { - return error('net.socket: failed') - } - s := Socket{ - sockfd: sockfd - family: family - typ: typ - proto: proto - } - return s -} - -pub fn socket_udp() ?Socket { - return new_socket(C.AF_INET, C.SOCK_DGRAM, C.IPPROTO_UDP) -} - -// set socket options -pub fn (s Socket) setsockopt(level int, optname int, optvalue &int) ?int { - res := C.setsockopt(s.sockfd, level, optname, optvalue, sizeof(int)) - if res < 0 { - return error('net.setsocketopt: failed with $res') - } - return res -} - -// bind socket to port -pub fn (s Socket) bind(port int) ?int { - mut addr := C.sockaddr_in{} - addr.sin_family = s.family - addr.sin_port = C.htons(port) - addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY) - size := 16 // sizeof(C.sockaddr_in) - tmp := voidptr(&addr) - skaddr := &C.sockaddr(tmp) - res := C.bind(s.sockfd, skaddr, size) - if res < 0 { - return error('net.bind: failed with $res') - } - return res -} - -// put socket into passive mode and wait to receive -pub fn (s Socket) listen() ?int { - backlog := 128 - res := C.listen(s.sockfd, backlog) - if res < 0 { - return error('net.listen: failed with $res') - } - $if debug { - println('listen res = $res') - } - return res -} - -// put socket into passive mode with user specified backlog and wait to receive -pub fn (s Socket) listen_backlog(backlog int) ?int { - mut n := 0 - if backlog > 0 { - n = backlog - } - res := C.listen(s.sockfd, n) - if res < 0 { - return error('net.listen_backlog: failed with $res') - } - return res -} - -// helper method to create, bind, and listen given port number -pub fn listen(port int) ?Socket { - $if debug { - println('net.listen($port)') - } - s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) ? - s.bind(port) ? - s.listen() ? - return s -} - -// accept first connection request from socket queue -pub fn (s Socket) accept() ?Socket { - $if debug { - println('accept()') - } - addr := C.sockaddr{} - size := sizeof(addr) - sockfd := C.accept(s.sockfd, &addr, &size) - if sockfd < 0 { - return error('net.accept: failed with $sockfd') - } - c := Socket{ - sockfd: sockfd - family: s.family - typ: s.typ - proto: s.proto - } - return c -} - -pub fn (s Socket) peer_ip() ?string { - buf := [44]byte{} - peeraddr := C.sockaddr_in{} - speeraddr := sizeof(peeraddr) - ok := C.getpeername(s.sockfd, &C.sockaddr(&peeraddr), &speeraddr) - if ok == -1 { - return error('net.peer_ip: getpeername failed') - } - cstr := C.inet_ntop(C.AF_INET, &peeraddr.sin_addr, buf, sizeof(buf)) - if cstr == 0 { - return error('net.peer_ip: inet_ntop failed') - } - res := cstring_to_vstring(cstr) - return res -} - -// connect to given addrress and port -pub fn (s Socket) connect(address string, port int) ?int { - mut hints := C.addrinfo{} - hints.ai_family = s.family - hints.ai_socktype = s.typ - hints.ai_flags = C.AI_PASSIVE - hints.ai_protocol = s.proto - hints.ai_addrlen = 0 - hints.ai_canonname = C.NULL - hints.ai_addr = C.NULL - hints.ai_next = C.NULL - info := &C.addrinfo(0) - sport := '$port' - info_res := C.getaddrinfo(address.str, sport.str, &hints, &info) - if info_res != 0 { - error_message := os.get_error_msg(error_code()) - return error('net.connect: getaddrinfo failed "$error_message"') - } - res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen) - if res < 0 { - error_message := os.get_error_msg(error_code()) - return error('net.connect: connect failed "$error_message"') - } - return res -} - -// helper method to create socket and connect -pub fn dial(address string, port int) ?Socket { - s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) ? - s.connect(address, port) ? - return s -} - -// send data to socket (when you have a memory buffer) -pub fn (s Socket) send(buf byteptr, len int) ?int { - mut dptr := buf - mut dlen := len - for { - send_size := if dlen > s.max_single_send_size { s.max_single_send_size } else { dlen } - sbytes := C.send(s.sockfd, dptr, send_size, msg_nosignal) - if sbytes < 0 { - return error('net.send: failed with $sbytes') - } - dlen -= sbytes - if dlen <= 0 { - break - } - unsafe { - dptr += sbytes - } - } - return len -} - -// send string data to socket (when you have a v string) -pub fn (s Socket) send_string(sdata string) ?int { - return s.send(sdata.str, sdata.len) -} - -// receive string data from socket. NB: you are responsible for freeing the returned byteptr -pub fn (s Socket) recv(bufsize int) (byteptr, int) { - mut buf := byteptr(0) - unsafe { - buf = malloc(bufsize) - } - res := C.recv(s.sockfd, buf, bufsize, 0) - return buf, res -} - -// TODO: remove cread/2 and crecv/2 when the Go net interface is done -pub fn (s Socket) cread(buffer byteptr, buffersize int) int { - res := C.read(s.sockfd, buffer, buffersize) - return res -} - -// Receive a message from the socket, and place it in a preallocated buffer buf, -// with maximum message size bufsize. Returns the length of the received message. -pub fn (s Socket) crecv(buffer voidptr, buffersize int) int { - res := C.recv(s.sockfd, byteptr(buffer), buffersize, 0) - return res -} - -// shutdown and close socket -pub fn (s Socket) close() ?int { - mut shutdown_res := 0 - $if windows { - shutdown_res = C.shutdown(s.sockfd, C.SD_BOTH) - } $else { - shutdown_res = C.shutdown(s.sockfd, C.SHUT_RDWR) - } - // TODO: should shutdown throw an error? close will - // continue even if shutdown failed - // if shutdown_res < 0 { - // return error('net.close: shutdown failed with $shutdown_res') - // } - mut res := 0 - $if windows { - res = C.closesocket(s.sockfd) - } $else { - res = C.close(s.sockfd) - } - if res < 0 { - return error('net.close: failed with $res') - } - return 0 -} - -pub const ( - crlf = '\r\n' - max_read = 400 - msg_peek = 0x02 -) - -// write - write a string with CRLF after it over the socket s -pub fn (s Socket) write(str string) ?int { - line := '$str$crlf' - res := C.send(s.sockfd, line.str, line.len, msg_nosignal) - if res < 0 { - return error('net.write: failed with $res') - } - return res -} - -// read_line - retrieves a line from the socket s (i.e. a string ended with \n) -pub fn (s Socket) read_line() string { - mut buf := [max_read]byte{} // where C.recv will store the network data - mut res := '' // The final result, including the ending \n. - for { - mut line := '' // The current line. Can be a partial without \n in it. - n := C.recv(s.sockfd, buf, max_read - 1, msg_peek) - if n == -1 { - return res - } - if n == 0 { - return res - } - buf[n] = `\0` - mut eol_idx := -1 - for i in 0 .. n { - if int(buf[i]) == `\n` { - eol_idx = i - // Ensure that tos_clone(buf) later, - // will return *only* the first line (including \n), - // and ignore the rest - buf[i + 1] = `\0` - break - } - } - bufbp := byteptr(buf) - line = tos_clone(bufbp) - if eol_idx > 0 { - // At this point, we are sure that recv returned valid data, - // that contains *at least* one line. - // Ensure that the block till the first \n (including it) - // is removed from the socket's receive queue, so that it does - // not get read again. - C.recv(s.sockfd, buf, eol_idx + 1, 0) - res += line - break - } - // recv returned a buffer without \n in it . - C.recv(s.sockfd, buf, n, 0) - res += line - res += crlf - break - } - return res -} - -// TODO -pub fn (s Socket) read_all() string { - mut buf := [max_read]byte{} // where C.recv will store the network data - mut res := '' // The final result, including the ending \n. - for { - n := C.recv(s.sockfd, buf, max_read - 1, 0) - if n == -1 { - return res - } - if n == 0 { - return res - } - bufbp := byteptr(buf) - res += tos_clone(bufbp) - } - return res -} - -pub fn (s Socket) get_port() int { - mut addr := C.sockaddr_in{} - size := 16 // sizeof(sockaddr_in) - tmp := voidptr(&addr) - skaddr := &C.sockaddr(tmp) - C.getsockname(s.sockfd, skaddr, &size) - return C.ntohs(addr.sin_port) -} diff --git a/vlib/x/net/socket_options.c.v b/vlib/net/socket_options.c.v similarity index 100% rename from vlib/x/net/socket_options.c.v rename to vlib/net/socket_options.c.v diff --git a/vlib/net/socket_test.v b/vlib/net/socket_test.v deleted file mode 100644 index f8d6ed2ec7..0000000000 --- a/vlib/net/socket_test.v +++ /dev/null @@ -1,106 +0,0 @@ -import net - -fn setup() (net.Socket, net.Socket, net.Socket) { - server := net.listen(0) or { - panic(err) - } - server_port := server.get_port() - client := net.dial('127.0.0.1', server_port) or { - panic(err) - } - socket := server.accept() or { - panic(err) - } - $if debug_peer_ip ? { - ip := socket.peer_ip() or { - '$err' - } - eprintln('socket peer_ip: $ip') - } - return server, client, socket -} - -fn cleanup(server &net.Socket, client &net.Socket, socket &net.Socket) { - server.close() or { } - client.close() or { } - socket.close() or { } -} - -fn test_socket() { - server, client, socket := setup() - defer { - cleanup(server, client, socket) - } - message := 'Hello World' - socket.send(message.str, message.len) or { - assert false - } - $if debug { - println('message send: $message') - } - $if debug { - println('send socket: $socket.sockfd') - } - bytes, blen := client.recv(1024) - received := tos(bytes, blen) - $if debug { - println('message received: $received') - } - $if debug { - println('client: $client.sockfd') - } - assert message == received -} - -fn test_socket_read_line() { - server, client, socket := setup() - defer { - cleanup(server, client, socket) - } - message1, message2 := 'message1', 'message2' - message := '$message1\n$message2' - socket.write(message) or { - assert false - } - line1, line2 := client.read_line(), client.read_line() - assert line1 != message1 - assert line1.trim_space() == message1 - assert line2 != message2 - assert line2.trim_space() == message2 -} - -fn test_socket_write() { - server, client, socket := setup() - defer { - cleanup(server, client, socket) - } - message1 := 'a message 1' - socket.write(message1) or { - assert false - } - line1 := client.read_line() - assert line1 != message1 - assert line1.trim_space() == message1 -} - -fn test_socket_write_fail_without_panic() { - server, client, socket := setup() - defer { - cleanup(server, client, socket) - } - message2 := 'a message 2' - // ensure that socket.write (i.e. done on the server side) - // continues to work, even when the client side has been disconnected - // this test is important for a stable long standing server - client.close() or { } - $if solaris { - return - } - // TODO: fix segfaulting on Solaris - for i := 0; i < 3; i++ { - socket.write(message2) or { - println('write to a socket without a recipient should produce an option fail: $err | $message2') - assert true - } - } -} diff --git a/vlib/net/socket_udp_test.v b/vlib/net/socket_udp_test.v deleted file mode 100644 index 5bd40ea6d1..0000000000 --- a/vlib/net/socket_udp_test.v +++ /dev/null @@ -1,18 +0,0 @@ -import net - -fn start_socket_udp_server() { - bufsize := 1024 - bytes := [1024]byte{} - s := net.socket_udp() or { panic(err) } - s.bind( 9876 ) or { panic(err) } - println('Waiting for udp packets:') - for { - res := s.crecv(bytes, bufsize) - if res < 0 { break } - print('Received $res bytes: ' + tos(bytes, res)) - } -} - -fn test_udp_server() { - // start_socket_udp_server() -} diff --git a/vlib/x/net/tcp.v b/vlib/net/tcp.v similarity index 74% rename from vlib/x/net/tcp.v rename to vlib/net/tcp.v index 069c0d69dc..19c6e55c49 100644 --- a/vlib/x/net/tcp.v +++ b/vlib/net/tcp.v @@ -20,9 +20,9 @@ pub fn dial_tcp(address string) ?TcpConn { return TcpConn { sock: s - - read_timeout: -1 - write_timeout: -1 + + read_timeout: 30 * time.second + write_timeout: 30 * time.second } } @@ -65,15 +65,15 @@ pub fn (c TcpConn) write(bytes []byte) ? { return c.write_ptr(bytes.data, bytes.len) } -// write_string blocks and attempts to write all data -pub fn (c TcpConn) write_string(s string) ? { +// write_str blocks and attempts to write all data +pub fn (c TcpConn) write_str(s string) ? { return c.write_ptr(s.str, s.len) } -pub fn (c TcpConn) read_into_ptr(buf_ptr byteptr, len int) ?int { - res := C.recv(c.sock.handle, buf_ptr, len, 0) +pub fn (c TcpConn) read_ptr(buf_ptr byteptr, len int) ?int { + mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0))? - if res >= 0 { + if res > 0 { return res } @@ -81,7 +81,8 @@ pub fn (c TcpConn) read_into_ptr(buf_ptr byteptr, len int) ?int { match code { error_ewouldblock { c.wait_for_read()? - return socket_error(C.recv(c.sock.handle, buf_ptr, len, 0)) + res = wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0))? + return socket_error(res) } else { wrap_error(code)? @@ -89,29 +90,8 @@ pub fn (c TcpConn) read_into_ptr(buf_ptr byteptr, len int) ?int { } } -pub fn (c TcpConn) read_into(mut buf []byte) ?int { - res := C.recv(c.sock.handle, buf.data, buf.len, 0) - - if res >= 0 { - return res - } - - code := error_code() - match code { - error_ewouldblock { - c.wait_for_read()? - return socket_error(C.recv(c.sock.handle, buf.data, buf.len, 0)) - } - else { - wrap_error(code)? - } - } -} - -pub fn (c TcpConn) read() ?[]byte { - mut buf := []byte { len: 1024 } - read := c.read_into(mut buf)? - return buf[..read] +pub fn (c TcpConn) read(mut buf []byte) ?int { + return c.read_ptr(buf.data, buf.len) } pub fn (c TcpConn) read_deadline() ?time.Time { @@ -162,6 +142,28 @@ pub fn (c TcpConn) wait_for_write() ? { return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) } +pub fn (c TcpConn) peer_addr() ?Addr { + mut addr := C.sockaddr{} + len := sizeof(C.sockaddr) + + socket_error(C.getpeername(c.sock.handle, &addr, &len))? + + return new_addr(addr) +} + +pub fn (c TcpConn) peer_ip() ?string { + buf := [44]byte{} + peeraddr := C.sockaddr_in{} + speeraddr := sizeof(peeraddr) + socket_error(C.getpeername(c.sock.handle, &C.sockaddr(&peeraddr), &speeraddr))? + cstr := C.inet_ntop(C.AF_INET, &peeraddr.sin_addr, buf, sizeof(buf)) + if cstr == 0 { + return error('net.peer_ip: inet_ntop failed') + } + res := cstring_to_vstring(cstr) + return res +} + pub fn (c TcpConn) str() string { // TODO return 'TcpConn' @@ -194,8 +196,8 @@ pub fn listen_tcp(port int) ?TcpListener { return TcpListener { sock: s - accept_timeout: -1 accept_deadline: no_deadline + accept_timeout: infinite_timeout } } @@ -220,14 +222,10 @@ pub fn (l TcpListener) accept() ?TcpConn { } } - new_sock := TcpSocket { - handle: new_handle - } + new_sock := tcp_socket_from_handle(new_handle)? return TcpConn{ sock: new_sock - read_timeout: -1 - write_timeout: -1 } } @@ -279,7 +277,22 @@ fn new_tcp_socket() ?TcpSocket { t := true socket_error(C.ioctlsocket(sockfd, fionbio, &t))? } $else { - socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK)) + socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) + } + return s +} + +fn tcp_socket_from_handle(sockfd int) ?TcpSocket { + s := TcpSocket { + handle: sockfd + } + //s.set_option_bool(.reuse_addr, true)? + s.set_option_int(.reuse_addr, 1)? + $if windows { + t := true + socket_error(C.ioctlsocket(sockfd, fionbio, &t))? + } $else { + socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) } return s } @@ -326,23 +339,19 @@ fn (s TcpSocket) connect(a string) ? { return none } - errcode := error_code() + _ := error_code() - if errcode == error_ewouldblock { - write_result := s.@select(.write, connect_timeout)? - if write_result { - // succeeded - return none - } - except_result := s.@select(.except, connect_timeout)? - if except_result { - return err_connect_failed - } - // otherwise we timed out - return err_connect_timed_out + write_result := s.@select(.write, connect_timeout)? + if write_result { + // succeeded + return none } - - return wrap_error(errcode) + except_result := s.@select(.except, connect_timeout)? + if except_result { + return err_connect_failed + } + // otherwise we timed out + return err_connect_timed_out } // address gets the address of a socket @@ -352,5 +361,5 @@ pub fn (s TcpSocket) address() ?Addr { // cast to the correct type sockaddr := &C.sockaddr(&addr) C.getsockname(s.handle, sockaddr, &size) - return new_addr(sockaddr, '', 0) + return new_addr(sockaddr) } diff --git a/vlib/x/net/tcp_test.v b/vlib/net/tcp_test.v similarity index 89% rename from vlib/x/net/tcp_test.v rename to vlib/net/tcp_test.v index 9a08cfc691..59d56a1183 100644 --- a/vlib/x/net/tcp_test.v +++ b/vlib/net/tcp_test.v @@ -1,4 +1,4 @@ -import x.net +import net import time const ( @@ -13,7 +13,7 @@ fn handle_conn(_c net.TcpConn) { c.set_write_timeout(10 * time.second) for { mut buf := []byte{len: 100, init: 0} - read := c.read_into(mut buf) or { + read := c.read(mut buf) or { println('Server: connection dropped') return } @@ -44,9 +44,9 @@ fn echo() ? { c.set_read_timeout(10 * time.second) c.set_write_timeout(10 * time.second) data := 'Hello from vlib/net!' - c.write_string(data)? - mut buf := []byte{len: 100, init: 0} - read := c.read_into(mut buf)? + c.write_str(data)? + mut buf := []byte{len: 4096} + read := c.read(mut buf)? assert read == data.len for i := 0; i < read; i++ { assert buf[i] == data[i] diff --git a/vlib/x/net/udp.v b/vlib/net/udp.v similarity index 86% rename from vlib/x/net/udp.v rename to vlib/net/udp.v index 68a00b77a2..8c4c155336 100644 --- a/vlib/x/net/udp.v +++ b/vlib/net/udp.v @@ -48,7 +48,7 @@ pub fn (c UdpConn) write(buf []byte) ? { return c.write_ptr(buf.data, buf.len) } -pub fn (c UdpConn) write_string(s string) ? { +pub fn (c UdpConn) write_str(s string) ? { return c.write_ptr(s.str, s.len) } @@ -83,16 +83,15 @@ pub fn (c UdpConn) write_to_string(addr Addr, s string) ? { return c.write_to_ptr(addr, s.str, s.len) } -// read_into reads from the socket into buf up to buf.len returning the number of bytes read -pub fn (c UdpConn) read_into(mut buf []byte) ?(int, Addr) { +// read reads from the socket into buf up to buf.len returning the number of bytes read +pub fn (c UdpConn) read(mut buf []byte) ?(int, Addr) { mut addr_from := C.sockaddr{} len := sizeof(C.sockaddr) - res := C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, &len) + mut res := wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, &len))? - if res >= 0 { - port_from := (&C.sockaddr_in(&addr_from)).sin_port - addr := new_addr(addr_from, '', port_from)? + if res > 0 { + addr := new_addr(addr_from)? return res, addr } @@ -100,10 +99,11 @@ pub fn (c UdpConn) read_into(mut buf []byte) ?(int, Addr) { match code { error_ewouldblock { c.wait_for_read()? - res2 := socket_error(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, &len))? + // same setup as in tcp + res = wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, &len))? + res2 := socket_error(res)? - port_from := (&C.sockaddr_in(&addr_from)).sin_port - addr := new_addr(addr_from, '', port_from)? + addr := new_addr(addr_from)? return res2, addr } else { @@ -114,12 +114,6 @@ pub fn (c UdpConn) read_into(mut buf []byte) ?(int, Addr) { return none } -pub fn (c UdpConn) read() ?([]byte, Addr) { - mut buf := []byte { len: 1024 } - read, addr := c.read_into(mut buf)? - return buf[..read], addr -} - pub fn (c UdpConn) read_deadline() ?time.Time { if c.read_deadline.unix == 0 { return c.read_deadline diff --git a/vlib/x/net/udp_test.v b/vlib/net/udp_test.v similarity index 91% rename from vlib/x/net/udp_test.v rename to vlib/net/udp_test.v index d6f417ce23..2bfccfc83c 100644 --- a/vlib/x/net/udp_test.v +++ b/vlib/net/udp_test.v @@ -1,4 +1,4 @@ -import x.net +import net import time fn echo_server(_c net.UdpConn) { @@ -9,7 +9,7 @@ fn echo_server(_c net.UdpConn) { c.set_write_timeout(10 * time.second) for { mut buf := []byte{ len: 100, init: 0 } - read, addr := c.read_into(mut buf) or { + read, addr := c.read(mut buf) or { continue } @@ -31,10 +31,10 @@ fn echo() ? { data := 'Hello from vlib/net!' - c.write_string(data)? + c.write_str(data)? mut buf := []byte{ len: 100, init: 0 } - read, addr := c.read_into(mut buf)? + read, addr := c.read(mut buf)? assert read == data.len println('Got address $addr') diff --git a/vlib/x/net/util.v b/vlib/net/util.v similarity index 100% rename from vlib/x/net/util.v rename to vlib/net/util.v diff --git a/vlib/os/file.v b/vlib/os/file.v index 08ac6a4970..069ffb234a 100644 --- a/vlib/os/file.v +++ b/vlib/os/file.v @@ -1,7 +1,5 @@ module os -import strings - pub struct File { cfile voidptr // Using void* instead of FILE* pub: @@ -17,12 +15,13 @@ struct FileInfo { [deprecated] pub fn (f File) is_opened() bool { - eprintln('warning: `file.is_opened()` has been deprecated, use `file.is_opened` instead') + eprintln('warning: `File.is_opened()` has been deprecated, use `File.is_opened` instead') return f.is_opened } // **************************** Write ops *************************** -pub fn (mut f File) write(s string) ?int { +// write implements the Writer interface +pub fn (mut f File) write(buf []byte) ?int { if !f.is_opened { return error('file is not opened') } @@ -34,8 +33,8 @@ pub fn (mut f File) write(s string) ?int { } } */ - written := C.fwrite(s.str, s.len, 1, f.cfile) - if written == 0 && s.len != 0 { + written := C.fwrite(buf.data, buf.len, 1, f.cfile) + if written == 0 && buf.len != 0 { return error('0 bytes written') } return written @@ -66,11 +65,23 @@ pub fn (mut f File) writeln(s string) ?int { return (written + 1) } +// write_to implements the RandomWriter interface +pub fn (mut f File) write_to(pos int, buf []byte) ?int { + C.fseek(f.cfile, pos, C.SEEK_SET) + res := C.fwrite(buf.data, 1, buf.len, f.cfile) + C.fseek(f.cfile, 0, C.SEEK_END) + return res +} + +[deprecated] pub fn (mut f File) write_bytes(data voidptr, size int) int { + eprintln('warning `File.write_bytes()` has been deprecated, use `File.write` instead') return C.fwrite(data, 1, size, f.cfile) } +[deprecated] pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int { + eprintln('warning `File.write_bytes_at()` has been deprecated, use `File.write_at` instead') C.fseek(f.cfile, pos, C.SEEK_SET) res := C.fwrite(data, 1, size, f.cfile) C.fseek(f.cfile, 0, C.SEEK_END) @@ -79,12 +90,16 @@ pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int { // **************************** Read ops *************************** // read_bytes reads bytes from the beginning of the file +[deprecated] pub fn (f &File) read_bytes(size int) []byte { + eprintln('warning `File.read_bytes()` has been deprecated, use `File.read` instead') return f.read_bytes_at(size, 0) } // read_bytes_at reads bytes at the given position in the file +[deprecated] pub fn (f &File) read_bytes_at(size int, pos int) []byte { + eprintln('warning `File.read_bytes_at()` has been deprecated, use `File.read_at` instead') mut arr := []byte{len: size} nreadbytes := f.read_bytes_into(pos, mut arr) or { // return err @@ -96,7 +111,9 @@ pub fn (f &File) read_bytes_at(size int, pos int) []byte { // read_bytes_from fills `buf` with bytes at the given position in the file. // `buf` must have length greater than zero. // Returns number of bytes read or an error. +[deprecated] pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int { + eprintln('warning `File.read_bytes_into()` has been deprecated, use `File.read_from_into` instead') if buf.len == 0 { panic(@FN + ': `buf.len` == 0') } @@ -114,8 +131,35 @@ pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int { return nbytes } +// read implements the Reader interface +pub fn (f &File) read(mut buf []byte) ?int { + if buf.len == 0 { + return 0 + } + C.errno = 0 + nbytes := C.fread(buf.data, 1, buf.len, f.cfile) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + return nbytes +} + +// read_at reads buf.len bytes from pos in the file +pub fn (f &File) read_at(pos int, mut buf []byte) ?int { + if buf.len == 0 { + return 0 + } + C.fseek(f.cfile, pos, C.SEEK_SET) + C.errno = 0 + nbytes := C.fread(buf.data, 1, buf.len, f.cfile) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + return nbytes +} + // **************************** Utility ops *********************** -// write any unwritten data in stream's buffer +// flush writes any unwritten data in stream's buffer pub fn (mut f File) flush() { if !f.is_opened { return @@ -133,52 +177,24 @@ pub fn open_stdin() File { } // File.get_line - get a single line from the file. NB: the ending newline is *included*. +[deprecated] pub fn (mut f File) get_line() ?string { + eprintln('File.get_line() is deprecated... Use a BufferedReader instead') if !f.is_opened { return error('file is closed') } - $if !windows { - mut zbuf := byteptr(0) - mut zblen := size_t(0) - mut zx := 0 - unsafe { - zx = C.getline(&charptr(&zbuf), &zblen, f.cfile) - if zx == -1 { - C.free(zbuf) - if C.errno == 0 { - return error('end of file') - } - return error(posix_get_error_msg(C.errno)) - } - return zbuf.vstring_with_len(zx) - } - } - // - // using C.fgets is less efficient than f.get_line_getline, - // but is available everywhere, while C.getline does not work - // on windows - // - buf := [4096]byte{} - mut res := strings.new_builder(1024) - mut x := charptr(0) - for { - unsafe { - x = C.fgets(charptr(buf), 4096, f.cfile) - } - if x == 0 { - if res.len > 0 { - break - } - return error('end of file') - } - bufbp := byteptr(buf) - mut blen := vstrlen(bufbp) - res.write_bytes(bufbp, blen) - unsafe { - if blen == 0 || bufbp[blen - 1] == `\n` || bufbp[blen - 1] == `\r` { - break - } - } - } - return res.str() + return error('use io.new_buffered_reader') + /* + mut reader := io.new_buffered_reader({ + reader: io.make_reader(f) + }) + return reader.read_line() + */ +} + +pub fn (mut f File) write_str(s string) ? { + if !f.is_opened { + return error('file is closed') + } + f.write(s.bytes()) ? } diff --git a/vlib/os/os.v b/vlib/os/os.v index 724f347db0..639e32ca01 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -860,7 +860,7 @@ pub fn home_dir() string { // write_file writes `text` data to a file in `path`. pub fn write_file(path string, text string) ? { mut f := os.create(path)? - f.write(text) + f.write(text.bytes()) f.close() } diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 6a81e5bc37..9bd8e37af2 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -33,7 +33,7 @@ fn test_open_file() { mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } - file.write(hello) + file.write_str(hello) file.close() assert hello.len == os.file_size(filename) read_hello := os.read_file(filename) or { @@ -64,26 +64,26 @@ fn test_open_file_binary() { os.rm(filename) } -fn test_file_get_line() { - filename := './fgetline.txt' - os.write_file(filename, 'line 1\nline 2') - mut f := os.open_file(filename, 'r', 0) or { - assert false - return - } - line1 := f.get_line() or { - '' - } - line2 := f.get_line() or { - '' - } - f.close() - // - // eprintln('line1: $line1') - // eprintln('line2: $line2') - assert line1 == 'line 1\n' - assert line2 == 'line 2' -} +// fn test_file_get_line() { +// filename := './fgetline.txt' +// os.write_file(filename, 'line 1\nline 2') +// mut f := os.open_file(filename, 'r', 0) or { +// assert false +// return +// } +// line1 := f.get_line() or { +// '' +// } +// line2 := f.get_line() or { +// '' +// } +// f.close() +// // +// eprintln('line1: $line1 $line1.bytes()') +// eprintln('line2: $line2 $line2.bytes()') +// assert line1 == 'line 1\n' +// assert line2 == 'line 2' +// } fn test_create_file() { filename := './test1.txt' @@ -91,7 +91,7 @@ fn test_create_file() { mut f := os.create(filename) or { panic(err) } - f.write(hello) + f.write_str(hello) f.close() assert hello.len == os.file_size(filename) os.rm(filename) diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index c1d343393d..564ccd8814 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -217,12 +217,6 @@ fn (mut g Gen) fn_args(args []table.Param, is_variadic bool) ([]string, []string typ := g.unwrap_generic(arg.typ) arg_type_sym := g.table.get_type_symbol(typ) mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name) - // if arg.name == 'xxx' { - // println('xxx arg type= ' + arg_type_name) - // } - if g.cur_generic_type != 0 { - // foo() => foo_int(), foo_string() etc - } is_varg := i == args.len - 1 && is_variadic if is_varg { varg_type_str := int(arg.typ).str() diff --git a/vlib/vweb/assets/assets.v b/vlib/vweb/assets/assets.v index 23508a255a..8e340fb54c 100644 --- a/vlib/vweb/assets/assets.v +++ b/vlib/vweb/assets/assets.v @@ -107,7 +107,7 @@ fn (am AssetManager) combine(asset_type string, to_file bool) string { mut file := os.create(out_file) or { panic(err) } - file.write(out) + file.write(out.bytes()) file.close() return out_file } diff --git a/vlib/vweb/tests/vweb_test.v b/vlib/vweb/tests/vweb_test.v index 9ef8fd1d75..d88107f8ba 100644 --- a/vlib/vweb/tests/vweb_test.v +++ b/vlib/vweb/tests/vweb_test.v @@ -3,6 +3,7 @@ import time import json import net import net.http +import io const ( sport = 12380 @@ -10,6 +11,8 @@ const ( vexe = os.getenv('VEXE') vroot = os.dir(vexe) serverexe = os.join_path(os.cache_dir(), 'vweb_test_server.exe') + tcp_r_timeout = 30 * time.second + tcp_w_timeout = 30 * time.second ) // setup of vweb webserver @@ -18,33 +21,26 @@ fn testsuite_begin() { if os.exists(serverexe) { os.rm(serverexe) } - // prevent failing tests when vweb_test.v is rerun quickly - // and the previous webserver has not yet timed out. - for i := 0; i < 10; i++ { - if client := net.dial('127.0.0.1', sport) { - client.close() or { } - eprintln('previous webserver has not yet stopped ($i); waiting...') - time.sleep_ms(exit_after_time / 10) - continue - } else { - return - } - } } fn test_a_simple_vweb_app_can_be_compiled() { - did_server_compile := os.system('$vexe -o $serverexe vlib/vweb/tests/vweb_test_server.v') + did_server_compile := os.system('$vexe -g -o $serverexe vlib/vweb/tests/vweb_test_server.v') assert did_server_compile == 0 assert os.exists(serverexe) } fn test_a_simple_vweb_app_runs_in_the_background() { - server_exec_cmd := '$serverexe $sport $exit_after_time > /dev/null &' + suffix := $if windows { '' } $else { ' > /dev/null &' } + server_exec_cmd := '$serverexe $sport $exit_after_time $suffix' $if debug_net_socket_client ? { eprintln('running:\n$server_exec_cmd') } - res := os.system(server_exec_cmd) - assert res == 0 + $if windows { + go os.system(server_exec_cmd) + } $else { + res := os.system(server_exec_cmd) + assert res == 0 + } time.sleep_ms(100) } @@ -114,7 +110,7 @@ fn test_http_client_404() { 'http://127.0.0.1:$sport/zxcnbnm', 'http://127.0.0.1:$sport/JHKAJA', 'http://127.0.0.1:$sport/unknown', - ] + ] for url in url_404_list { res := http.get(url) or { panic(err) @@ -226,7 +222,7 @@ fn testsuite_end() { // utility code: struct SimpleTcpClientConfig { - retries int = 20 + retries int = 20 host string = 'static.dev' path string = '/' agent string = 'v/net.tcp.v' @@ -235,11 +231,11 @@ struct SimpleTcpClientConfig { } fn simple_tcp_client(config SimpleTcpClientConfig) ?string { - mut client := net.Socket{} + mut client := net.TcpConn{} mut tries := 0 for tries < config.retries { tries++ - client = net.dial('127.0.0.1', sport) or { + client = net.dial_tcp('127.0.0.1:$sport') or { if tries > config.retries { return error(err) } @@ -248,10 +244,11 @@ fn simple_tcp_client(config SimpleTcpClientConfig) ?string { } break } + client.set_read_timeout(tcp_r_timeout) + client.set_write_timeout(tcp_w_timeout) defer { - client.close() or { } + client.close() } - // message := 'GET $config.path HTTP/1.1 Host: $config.host User-Agent: $config.agent @@ -261,11 +258,10 @@ $config.content' $if debug_net_socket_client ? { eprintln('sending:\n$message') } - client.send(message.str, message.len)? - bytes, blen := client.recv(4096) - received := unsafe {bytes.vstring_with_len(blen)} + client.write(message.bytes()) ? + read := io.read_all(client) ? $if debug_net_socket_client ? { - eprintln('received:\n$received') + eprintln('received:\n$read') } - return received + return read.bytestr() } diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index fbae29d214..a4ed3eccab 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -4,6 +4,7 @@ module vweb import os +import io import net import net.http import net.urllib @@ -44,8 +45,8 @@ mut: content_type string = 'text/plain' status string = '200 OK' pub: - req http.Request - conn net.Socket + req http.Request + conn net.TcpConn // TODO Response pub mut: form map[string]string @@ -87,9 +88,7 @@ fn (mut ctx Context) send_response_to_client(mimetype string, res string) bool { defer { s.free() } - ctx.conn.send_string(s) or { - return false - } + send_string(ctx.conn, s) or { return false } return true } @@ -117,9 +116,7 @@ pub fn (mut ctx Context) redirect(url string) Result { return Result{} } ctx.done = true - ctx.conn.send_string('HTTP/1.1 302 Found\r\nLocation: $url$ctx.headers\r\n$headers_close') or { - return Result{} - } + send_string(ctx.conn, 'HTTP/1.1 302 Found\r\nLocation: ${url}${ctx.headers}\r\n${headers_close}') or { return Result{} } return Result{} } @@ -128,8 +125,8 @@ pub fn (mut ctx Context) not_found() Result { return Result{} } ctx.done = true - ctx.conn.send_string(http_404) or { } - return Result{} + send_string(ctx.conn, http_404) or {} + return vweb.Result{} } pub fn (mut ctx Context) set_cookie(cookie Cookie) { @@ -202,9 +199,7 @@ pub fn run(port int) { pub fn run_app(mut app T, port int) { println('Running a Vweb app on http://localhost:$port') - l := net.listen(port) or { - panic('failed to listen') - } + l := net.listen_tcp(port) or { panic('failed to listen') } app.vweb = Context{} app.init_once() $for method in T.methods { @@ -214,16 +209,13 @@ pub fn run_app(mut app T, port int) { } // app.reset() for { - conn := l.accept() or { - panic('accept() failed') - } - // handle_conn(conn, mut app) - handle_conn(conn, mut app) - // app.vweb.page_gen_time = time.ticks() - t - // eprintln('handle conn() took ${time.ticks()-t}ms') - // message := readall(conn) - // println(message) - /* + mut conn := l.accept() or { panic('accept() failed') } + handle_conn(mut conn, mut app) + //app.vweb.page_gen_time = time.ticks() - t + //eprintln('handle conn() took ${time.ticks()-t}ms') + //message := readall(conn) + //println(message) +/* if message.len > max_http_post_size { println('message.len = $message.len > max_http_post_size') conn.send_string(http_500) or {} @@ -243,15 +235,20 @@ pub fn run_app(mut app T, port int) { } } -fn handle_conn(conn net.Socket, mut app T) { - defer { - conn.close() or { } - } - // fn handle_conn(conn net.Socket, app_ T) T { - // mut app := app_ - // first_line := strip(lines[0]) +fn handle_conn(mut conn net.TcpConn, mut app T) { + conn.set_read_timeout(1 * time.second) + defer { conn.close() or {} } + //fn handle_conn(conn net.Socket, app_ T) T { + //mut app := app_ + //first_line := strip(lines[0]) + + mut reader := io.new_buffered_reader(reader: io.make_reader(conn)) + page_gen_start := time.ticks() - first_line := conn.read_line() + first_line := reader.read_line() or { + println('Failed first_line') + return + } $if debug { println('firstline="$first_line"') } @@ -261,29 +258,37 @@ fn handle_conn(conn net.Socket, mut app T) { vals := first_line.split(' ') if vals.len < 2 { println('no vals for http') - conn.send_string(http_500) or { } + send_string(conn, http_500) or {} return } mut headers := []string{} mut body := '' mut in_headers := true mut len := 0 - // for line in lines[1..] { - for _ in 0 .. 100 { - // println(j) - line := conn.read_line() + //for line in lines[1..] { + for _ in 0..100 { + //println(j) + line := reader.read_line() or { + println('Failed read_line') + break + } sline := strip(line) if sline == '' { - // if in_headers { - // End of headers, no body => exit - if len == 0 { - break - } - // } //else { - // End of body - // break - // } - in_headers = false + //if in_headers { + // End of headers, no body => exit + if len == 0 { + break + } + //} //else { + // End of body + //break + //} + + // read body + read_body := io.read_all(reader) or { []byte{} } + body += read_body.bytestr() + + break } if in_headers { // println(sline) @@ -292,12 +297,6 @@ fn handle_conn(conn net.Socket, mut app T) { len = sline.all_after(': ').int() // println('GOT CL=$len') } - } else { - body += line.trim_left('\r\n') - if body.len >= len { - break - } - // println('body:$body') } } req := http.Request{ @@ -343,7 +342,7 @@ fn handle_conn(conn net.Socket, mut app T) { mime_type := app.vweb.static_mime_types[static_file_name] if static_file != '' && mime_type != '' { data := os.read_file(static_file) or { - conn.send_string(http_404) or { } + send_string(conn, http_404) or {} return } app.vweb.send_response_to_client(mime_type, data) @@ -480,7 +479,7 @@ fn handle_conn(conn net.Socket, mut app T) { } if action == '' { // site not found - conn.send_string(http_404) or { } + send_string(conn, http_404) or {} return } $for method in T.methods { @@ -580,9 +579,7 @@ pub fn (ctx &Context) ip() string { ip = ip.all_before(',') } if ip == '' { - ip = ctx.conn.peer_ip() or { - '' - } + ip = ctx.conn.peer_ip() or { '' } } return ip } @@ -628,3 +625,8 @@ fn filter(s string) string { } pub type RawHtml = string + + +fn send_string(conn net.TcpConn, s string) ? { + conn.write(s.bytes())? +} diff --git a/vlib/x/net/address.v b/vlib/x/net/address.v deleted file mode 100644 index 752b51b93b..0000000000 --- a/vlib/x/net/address.v +++ /dev/null @@ -1,74 +0,0 @@ -module net - -// Addr represents an ip address -pub struct Addr { - addr C.sockaddr - len int -pub: - saddr string - port int -} - -pub fn (a Addr) str() string { - return '${a.saddr}:${a.port}' -} - -const ( - max_ipv4_addr_len = 16 -) - -fn new_addr(addr C.sockaddr, _saddr string, _port int) ?Addr { - mut saddr := _saddr - if saddr == '' { - // Convert to string representation - buf := []byte{ len: max_ipv4_addr_len, init: 0 } - $if windows { - res := C.WSAStringToAddress(&addr, SocketFamily.inet, C.NULL, &buf.data, &buf.len) - if res == 0 { - socket_error(-1)? - } - } $else { - res := C.inet_ntop(SocketFamily.inet, &addr, buf.data, buf.len) - if res == 0 { - socket_error(-1)? - } - } - saddr = buf.bytestr() - } - - mut port := _port - if port == 0 { - hport := (&C.sockaddr_in(&addr)).sin_port - port = C.ntohs(hport) - } - - return Addr { - addr int(sizeof(C.sockaddr)) saddr port - } -} - -pub fn resolve_addr(addr string, family SocketFamily, typ SocketType) ?Addr { - address, port := split_address(addr)? - - mut hints := C.addrinfo{} - hints.ai_family = family - hints.ai_socktype = typ - hints.ai_flags = C.AI_PASSIVE - hints.ai_protocol = 0 - hints.ai_addrlen = 0 - hints.ai_canonname = C.NULL - hints.ai_addr = C.NULL - hints.ai_next = C.NULL - info := &C.addrinfo(0) - - sport := '$port' - - // This might look silly but is recommended by MSDN - $if windows { - socket_error(0-C.getaddrinfo(address.str, sport.str, &hints, &info))? - } $else { - wrap_error(C.getaddrinfo(address.str, sport.str, &hints, &info)) - } - - return new_addr(*info.ai_addr, address, port) -} diff --git a/vlib/x/openssl/openssl.v b/vlib/x/openssl/openssl.v index 18e1cd757f..4391638d5d 100644 --- a/vlib/x/openssl/openssl.v +++ b/vlib/x/openssl/openssl.v @@ -1,7 +1,7 @@ module openssl import net.openssl -import x.net +import net import time // const ( diff --git a/vlib/x/websocket/handshake.v b/vlib/x/websocket/handshake.v index f3b4767e76..f3a7fe7077 100644 --- a/vlib/x/websocket/handshake.v +++ b/vlib/x/websocket/handshake.v @@ -111,7 +111,7 @@ fn (mut ws Client) read_handshake_str() ?string { mut msg := [1024]byte{} mut buffer := [1]byte{} for total_bytes_read < 1024 { - bytes_read := ws.socket_read_into_ptr(byteptr(&buffer), 1) ? + bytes_read := ws.socket_read_ptr(byteptr(&buffer), 1) ? if bytes_read == 0 { return error_with_code('unexpected no response from handshake', 5) } diff --git a/vlib/x/websocket/io.v b/vlib/x/websocket/io.v index 14066e6cb6..1bc71856c8 100644 --- a/vlib/x/websocket/io.v +++ b/vlib/x/websocket/io.v @@ -1,22 +1,22 @@ module websocket -import x.net +import net import time interface WebsocketIO { - socket_read_into(mut buffer []byte) ?int + socket_read(mut buffer []byte) ?int socket_write(bytes []byte) ? } -// socket_read_into reads into the provided buffer with its length -fn (mut ws Client) socket_read_into(mut buffer []byte) ?int { +// socket_read reads into the provided buffer with its length +fn (mut ws Client) socket_read(mut buffer []byte) ?int { lock { if ws.is_ssl { r := ws.ssl_conn.read_into(mut buffer)? return r } else { for { - r := ws.conn.read_into(mut buffer) or { + r := ws.conn.read(mut buffer) or { if errcode == net.err_timed_out_code { continue } @@ -28,14 +28,14 @@ fn (mut ws Client) socket_read_into(mut buffer []byte) ?int { } } -fn (mut ws Client) socket_read_into_ptr(buf_ptr byteptr, len int) ?int { +fn (mut ws Client) socket_read_ptr(buf_ptr byteptr, len int) ?int { lock { if ws.is_ssl { r := ws.ssl_conn.socket_read_into_ptr(buf_ptr, len)? return r } else { for { - r := ws.conn.read_into_ptr(buf_ptr, len) or { + r := ws.conn.read_ptr(buf_ptr, len) or { if errcode == net.err_timed_out_code { continue } diff --git a/vlib/x/websocket/message.v b/vlib/x/websocket/message.v index d9d7437092..e91bc0cc42 100644 --- a/vlib/x/websocket/message.v +++ b/vlib/x/websocket/message.v @@ -86,7 +86,7 @@ fn (mut ws Client) read_payload(frame &Frame) ?[]byte { mut read_buf := [1]byte{} mut bytes_read := 0 for bytes_read < frame.payload_len { - len := ws.socket_read_into_ptr(byteptr(&read_buf), 1)? + len := ws.socket_read_ptr(byteptr(&read_buf), 1)? if len != 1 { return error('expected read all message, got zero') } @@ -223,7 +223,7 @@ pub fn (mut ws Client) parse_frame_header() ?Frame { for ws.state == .open { // Todo: different error scenarios to make sure we close correctly on error // reader.read_into(mut rbuff) ? - read_bytes := ws.socket_read_into_ptr(byteptr(rbuff), 1)? + read_bytes := ws.socket_read_ptr(byteptr(rbuff), 1)? if read_bytes == 0 { // This is probably a timeout or close continue diff --git a/vlib/x/websocket/websocket_client.v b/vlib/x/websocket/websocket_client.v index 6eb923a790..fa2132d081 100644 --- a/vlib/x/websocket/websocket_client.v +++ b/vlib/x/websocket/websocket_client.v @@ -7,7 +7,7 @@ // check with valgrind if you do any changes in the free calls module websocket -import x.net +import net import x.openssl import net.urllib import time @@ -98,6 +98,8 @@ pub fn (mut ws Client) connect() ? { ws.set_state(.connecting) ws.logger.info('connecting to host $ws.uri') ws.conn = ws.dial_socket()? + ws.conn.set_read_timeout(net.infinite_timeout) + ws.conn.set_write_timeout(net.infinite_timeout) ws.handshake()? ws.set_state(.open) ws.logger.info('successfully connected to host $ws.uri') diff --git a/vlib/x/websocket/websocket_server.v b/vlib/x/websocket/websocket_server.v index 783f67e1d9..70f147dc11 100644 --- a/vlib/x/websocket/websocket_server.v +++ b/vlib/x/websocket/websocket_server.v @@ -1,7 +1,7 @@ // The module websocket implements the websocket server capabilities module websocket -import x.net +import net import x.openssl import log import sync diff --git a/vlib/x/websocket/websocket_windows.c.v b/vlib/x/websocket/websocket_windows.c.v index 4f93fe9910..e630ac25c7 100644 --- a/vlib/x/websocket/websocket_windows.c.v +++ b/vlib/x/websocket/websocket_windows.c.v @@ -1,6 +1,6 @@ module websocket -import x.net +import net fn error_code() int { return C.WSAGetLastError()