vlib/net: add buffered IO, x.net -> net (#6754)

pull/6842/head
Emily Hudson 2020-11-15 20:54:47 +00:00 committed by GitHub
parent 20bec81678
commit cd2a2cef25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 741 additions and 1648 deletions

4
.gitignore vendored
View File

@ -71,3 +71,7 @@ thumbs.db
/.bin /.bin
_docs _docs
# ignore vs databases
*.suo
*.VC.db

View File

@ -118,7 +118,7 @@ fn main() {
exit(0) exit(0)
} }
context.files = real_files 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' context.write_file += '.v'
} }
mut file_byte_map := map[string][]byte{} mut file_byte_map := map[string][]byte{}
@ -134,11 +134,11 @@ fn main() {
mut out_file := os.create(context.write_file) or { mut out_file := os.create(context.write_file) or {
panic(err) panic(err)
} }
out_file.write(context.header()) out_file.write_str(context.header())
for bname, fbytes in file_byte_map { 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 { } else {
println(context.header()) println(context.header())
for bname, fbytes in file_byte_map { for bname, fbytes in file_byte_map {

View File

@ -58,7 +58,7 @@ fn (c &Create) write_vmod(new bool) {
cerror(err) cerror(err)
exit(1) exit(1)
} }
vmod.write(vmod_content(c.name, c.description)) vmod.write_str(vmod_content(c.name, c.description))
vmod.close() vmod.close()
} }
@ -71,7 +71,7 @@ fn (c &Create) write_main(new bool) {
cerror(err) cerror(err)
exit(2) exit(2)
} }
main.write(main_content()) main.write_str(main_content())
main.close() 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 // We don't really need a .gitignore, it's just a nice-to-have
return return
} }
fl.write(gen_gitignore(c.name)) fl.write_str(gen_gitignore(c.name))
fl.close() fl.close()
} }
} }

View File

@ -16,6 +16,7 @@ import v.token
import v.vmod import v.vmod
import v.pref import v.pref
import json import json
import io
enum HighlightTokenTyp { enum HighlightTokenTyp {
unone unone
@ -154,7 +155,7 @@ fn (mut cfg DocConfig) serve_html() {
} }
def_name := docs.keys()[0] def_name := docs.keys()[0]
server_url := 'http://localhost:' + cfg.server_port.str() 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) panic(err)
} }
println('Serving docs on: $server_url') println('Serving docs on: $server_url')
@ -173,12 +174,12 @@ fn (mut cfg DocConfig) serve_html() {
default_filename: def_name default_filename: def_name
} }
for { for {
mut con := server.accept() or { mut conn := server.accept() or {
server.close() or { } server.close() or { }
panic(err) panic(err)
} }
handle_http_connection(mut con, server_context) handle_http_connection(mut conn, server_context)
con.close() or { conn.close() or {
eprintln('error closing the connection: $err') eprintln('error closing the connection: $err')
} }
} }
@ -190,10 +191,9 @@ struct VdocHttpServerContext {
default_filename string default_filename string
} }
fn handle_http_connection(mut con net.Socket, ctx &VdocHttpServerContext) { fn handle_http_connection(mut con net.TcpConn, ctx &VdocHttpServerContext) {
s := con.read_line() mut reader := io.new_buffered_reader(reader: io.make_reader(con))
first_line := s.all_before('\r\n') first_line := reader.read_line() or {
if first_line.len == 0 {
send_http_response(mut con, 501, ctx.content_type, 'bad request') send_http_response(mut con, 501, ctx.content_type, 'bad request')
return 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]) 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() content_length := html.len.str()
shttp_code := http_code.str() shttp_code := http_code.str()
mut http_response := strings.new_builder(20000) 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('\r\n')
http_response.write(html) http_response.write(html)
sresponse := http_response.str() sresponse := http_response.str()
con.send_string(sresponse) or { con.write_str(sresponse) or {
eprintln('error sending http response: $err') eprintln('error sending http response: $err')
} }
} }

View File

@ -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)
}

View File

@ -0,0 +1,5 @@
import net
conn := net.dial_tcp('google.com:80')?
peer_addr := conn.peer_addr()?
println('$peer_addr')

View File

@ -1,14 +1,16 @@
// Simple raw HTTP head request // Simple raw HTTP head request
import x.net import net
import time import time
import io
// Make a new connection // Make a new connection
mut conn := net.dial_tcp('google.com:80')? mut conn := net.dial_tcp('google.com:80')?
defer { conn.close() }
// Simple http HEAD request for a file // 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! // 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 // Read all the data that is waiting
result := conn.read()? result := io.read_all(conn)?
// Cast to string and print result // Cast to string and print result
println(result.bytestr()) println(result.bytestr())

23
examples/net_t.v 100644
View File

@ -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()
}

View File

@ -110,7 +110,7 @@ fn (image Image) save_as_ppm(file_name string) {
c_r := to_int(unsafe{image.data[i]}.x) c_r := to_int(unsafe{image.data[i]}.x)
c_g := to_int(unsafe{image.data[i]}.y) c_g := to_int(unsafe{image.data[i]}.y)
c_b := to_int(unsafe{image.data[i]}.z) 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() f_out.close()
} }

View File

@ -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)
}
}

View File

@ -620,6 +620,10 @@ pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int {
return accum_ return accum_
} }
pub fn (mut a array) grow(amount int) {
a.ensure_cap(a.len + amount)
}
// array_eq<T> checks if two arrays contain all the same elements in the same order. // array_eq<T> checks if two arrays contain all the same elements in the same order.
// []int == []int (also for: i64, f32, f64, byte, string) // []int == []int (also for: i64, f32, f64, byte, string)
/* /*

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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))?
}

45
vlib/io/reader.v 100644
View File

@ -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
}

View File

@ -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)
}

12
vlib/io/writer.v 100644
View File

@ -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
}

View File

@ -22,6 +22,7 @@ mut:
} }
struct C.sockaddr { struct C.sockaddr {
sa_family u16
} }
struct C.sockaddr_in { struct C.sockaddr_in {
@ -75,9 +76,11 @@ fn C.shutdown() int
fn C.ntohs() 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.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr
fn C.WSAStringToAddress() int fn C.WSAAddressToStringA() int
fn C.getsockname() int fn C.getsockname() int

81
vlib/net/address.v 100644
View File

@ -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)
}

View File

@ -39,6 +39,21 @@ pub fn wrap_error(error_code int) ? {
return error_with_code('socket error: $enum_error', error_code) return error_with_code('socket error: $enum_error', error_code)
} }
$else { $else {
if error_code == 0 {
return
}
return error_with_code('net: socket error: $error_code', error_code) 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
}

View File

@ -1,3 +1,5 @@
module ftp
/* /*
basic ftp module basic ftp module
RFC-959 RFC-959
@ -15,9 +17,9 @@ basic ftp module
dtp.close() dtp.close()
ftp.close() ftp.close()
*/ */
module ftp
import net import net
import io
const ( const (
connected = 220 connected = 220
@ -35,33 +37,26 @@ const (
struct DTP { struct DTP {
mut: mut:
sock net.Socket conn net.TcpConn
reader io.BufferedReader
ip string ip string
port int port int
} }
fn (dtp DTP) read() []byte { fn (mut dtp DTP) read() ?[]byte {
mut data := []byte{} mut data := []byte{len: 1024}
for { dtp.reader.read(mut data)?
buf, len := dtp.sock.recv(1024)
if len == 0 {
break
}
for i in 0 .. len {
data << unsafe {buf[i]}
}
unsafe {free(buf)}
}
return data return data
} }
fn (dtp DTP) close() { fn (dtp DTP) close() {
dtp.sock.close() or { } dtp.conn.close()
} }
struct FTP { struct FTP {
mut: mut:
sock net.Socket conn net.TcpConn
reader io.BufferedReader
buffer_size int buffer_size int
} }
@ -71,18 +66,15 @@ pub fn new() FTP {
return f return f
} }
fn (ftp FTP) write(data string) ?int { fn (mut ftp FTP) write(data string) ? {
$if debug { $if debug {
println('FTP.v >>> $data') println('FTP.v >>> $data')
} }
n := ftp.sock.send_string('$data\r\n') or { ftp.conn.write('$data\r\n'.bytes())?
return error('Cannot send data')
}
return n
} }
fn (ftp FTP) read() (int, string) { fn (mut ftp FTP) read() ?(int, string) {
mut data := ftp.sock.read_line() mut data := ftp.reader.read_line()?
$if debug { $if debug {
println('FTP.v <<< $data') println('FTP.v <<< $data')
} }
@ -92,7 +84,7 @@ fn (ftp FTP) read() (int, string) {
code := data[..3].int() code := data[..3].int()
if data[3] == `-` { if data[3] == `-` {
for { for {
data = ftp.sock.read_line() data = ftp.reader.read_line()?
if data[..3].int() == code && data[3] != `-` { if data[..3].int() == code && data[3] != `-` {
break break
} }
@ -101,26 +93,25 @@ fn (ftp FTP) read() (int, string) {
return code, data return code, data
} }
pub fn (mut ftp FTP) connect(ip string) bool { pub fn (mut ftp FTP) connect(ip string) ?bool {
sock := net.dial(ip, 21) or { conn := net.dial_tcp('$ip:21')?
return false ftp.conn = conn
} code, _ := ftp.read()?
ftp.sock = sock
code, _ := ftp.read()
if code == connected { if code == connected {
return true return true
} }
ftp.reader = io.new_buffered_reader(reader: io.make_reader(ftp.conn))
return false 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 { ftp.write('USER $user') or {
$if debug { $if debug {
println('ERROR sending user') println('ERROR sending user')
} }
return false return false
} }
mut code, _ := ftp.read() mut code, _ := ftp.read()?
if code == logged_in { if code == logged_in {
return true return true
} }
@ -133,24 +124,21 @@ pub fn (ftp FTP) login(user string, passwd string) bool {
} }
return false return false
} }
code, _ = ftp.read() code, _ = ftp.read()?
if code == logged_in { if code == logged_in {
return true return true
} }
return false return false
} }
pub fn (ftp FTP) close() { pub fn ( mut ftp FTP) close() ? {
send_quit := 'QUIT\r\n' ftp.write('QUIT')?
ftp.sock.send_string(send_quit) or { } ftp.conn.close()
ftp.sock.close() or { }
} }
pub fn (ftp FTP) pwd() string { pub fn ( mut ftp FTP) pwd() ?string {
ftp.write('PWD') or { ftp.write('PWD')?
return '' _, data := ftp.read()?
}
_, data := ftp.read()
spl := data.split('"') // " spl := data.split('"') // "
if spl.len >= 2 { if spl.len >= 2 {
return spl[1] return spl[1]
@ -158,11 +146,11 @@ pub fn (ftp FTP) pwd() string {
return data return data
} }
pub fn (ftp FTP) cd(dir string) { pub fn ( mut ftp FTP) cd(dir string) ? {
ftp.write('CWD $dir') or { ftp.write('CWD $dir') or {
return return
} }
mut code, mut data := ftp.read() mut code, mut data := ftp.read()?
match int(code) { match int(code) {
denied { denied {
$if debug { $if debug {
@ -170,7 +158,7 @@ pub fn (ftp FTP) cd(dir string) {
} }
} }
complete { complete {
code, data = ftp.read() code, data = ftp.read()?
} }
else {} else {}
} }
@ -184,20 +172,21 @@ fn new_dtp(msg string) ?DTP {
return error('Bad message') return error('Bad message')
} }
ip, port := get_host_ip_from_dtp_message(msg) 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') return error('Cannot connect to the data channel')
} }
dtp := DTP{ dtp := DTP{
sock: sock conn: conn
ip: ip ip: ip
port: port port: port
} }
return dtp return dtp
} }
fn (ftp FTP) pasv() ?DTP { fn ( mut ftp FTP) pasv() ?DTP {
ftp.write('PASV') or { } ftp.write('PASV') or {
code, data := ftp.read() }
code, data := ftp.read()?
$if debug { $if debug {
println('pass: $data') println('pass: $data')
} }
@ -208,20 +197,21 @@ fn (ftp FTP) pasv() ?DTP {
return dtp return dtp
} }
pub fn (ftp FTP) dir() ?[]string { pub fn ( mut ftp FTP) dir() ?[]string {
dtp := ftp.pasv() or { mut dtp := ftp.pasv() or {
return error('cannot establish data connection') return error('cannot establish data connection')
} }
ftp.write('LIST') or { } ftp.write('LIST') or {
code, _ := ftp.read() }
code, _ := ftp.read()?
if code == denied { if code == denied {
return error('LIST denied') return error('LIST denied')
} }
if code != open_data_connection { if code != open_data_connection {
return error('data channel empty') return error('data channel empty')
} }
list_dir := dtp.read() list_dir := dtp.read()?
result, _ := ftp.read() result, _ := ftp.read()?
if result != close_data_connection { if result != close_data_connection {
println('LIST not ok') println('LIST not ok')
} }
@ -237,19 +227,20 @@ pub fn (ftp FTP) dir() ?[]string {
return dir return dir
} }
pub fn (ftp FTP) get(file string) ?[]byte { pub fn (mut ftp FTP) get(file string) ?[]byte {
dtp := ftp.pasv() or { mut dtp := ftp.pasv() or {
return error('Cannot stablish data connection') return error('Cannot stablish data connection')
} }
ftp.write('RETR $file') or { } ftp.write('RETR $file') or {
code, _ := ftp.read() }
code, _ := ftp.read()?
if code == denied { if code == denied {
return error('Permission denied') return error('Permission denied')
} }
if code != open_data_connection { if code != open_data_connection {
return error('Data connection not ready') return error('Data connection not ready')
} }
blob := dtp.read() blob := dtp.read()?
dtp.close() dtp.close()
return blob return blob
} }

View File

@ -3,15 +3,17 @@ import net.ftp
// NB: this function makes network calls to external servers, // NB: this function makes network calls to external servers,
// that is why it is not a very good idea to run it in CI. // 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` // 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 } $if !network ? { return }
mut ftp := ftp.new() mut ftp := ftp.new()
defer { defer {
ftp.close() ftp.close()
} }
assert ftp.connect('ftp.redhat.com') connect_result := ftp.connect('ftp.redhat.com')?
assert ftp.login('ftp', 'ftp') assert connect_result
pwd := ftp.pwd() login_result := ftp.login('ftp', 'ftp')?
assert login_result
pwd := ftp.pwd()?
assert pwd.len > 0 assert pwd.len > 0
ftp.cd('/') ftp.cd('/')
dir_list1 := ftp.dir() or { dir_list1 := ftp.dir() or {
@ -31,3 +33,10 @@ fn test_ftp_client() {
} }
assert blob.len > 0 assert blob.len > 0
} }
fn test_ftp_cleint() {
ftp_client_test_inside() or {
panic(err)
}
}

View File

@ -5,8 +5,9 @@ module http
import net.urllib import net.urllib
import net.http.chunked import net.http.chunked
import strings
import net import net
import time
import io
const ( const (
max_redirects = 4 max_redirects = 4
@ -276,7 +277,7 @@ fn (req &Request) method_and_url_to_response(method Method, url urllib.URL) ?Res
return res return res
} else if scheme == 'http' { } else if scheme == 'http' {
// println('http_do( $nport, $method, $host_name, $path )') // 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 res
} }
return error('http.request.method_and_url_to_response: unsupported scheme: "$scheme"') 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()') panic('http.escape() was replaced with http.escape_url()')
} }
fn (req &Request) http_do(port int, method Method, host_name string, path string) ?Response { fn (req &Request) http_do(host string, method Method, path string) ?Response {
rbuffer := [bufsize]byte{} host_name, _ := net.split_address(host)?
mut sb := strings.new_builder(100)
s := req.build_request_headers(method, host_name, path) s := req.build_request_headers(method, host_name, path)
client := net.dial(host_name, port) ? mut client := net.dial_tcp(host)?
client.send(s.str, s.len) or { } // TODO this really needs to be exposed somehow
for { client.set_read_timeout(time.second * 30)
readbytes := client.crecv(rbuffer, bufsize) client.write(s.bytes())?
if readbytes < 0 { mut bytes := io.read_all(client)?
return error('http.request.http_do: error reading response. readbytes=$readbytes') client.close()
} return parse_response(bytes.bytestr())
if readbytes == 0 {
break
}
sb.write(tos(rbuffer, readbytes))
}
client.close() or { }
return parse_response(sb.str())
} }
pub fn (req &Request) referer() string { pub fn (req &Request) referer() string {

View File

@ -1,17 +0,0 @@
module net
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
fn error_code() int {
return C.errno
}
pub const (
msg_nosignal = 0x4000
)
#flag solaris -lsocket

View File

@ -1,37 +0,0 @@
module net
#flag -lws2_32
#include <winsock2.h>
#include <ws2tcpip.h>
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
)

View File

@ -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)
}

View File

@ -9,6 +9,7 @@ import net
import encoding.base64 import encoding.base64
import strings import strings
import time import time
import io
const ( const (
recv_size = 128 recv_size = 128
@ -29,7 +30,8 @@ pub enum BodyType {
pub struct Client { pub struct Client {
mut: mut:
socket net.Socket conn net.TcpConn
reader io.BufferedReader
pub: pub:
server string server string
port int = 25 port int = 25
@ -62,8 +64,10 @@ pub fn new_client(config Client) ?Client {
pub fn (mut c Client) reconnect() ? { pub fn (mut c Client) reconnect() ? {
if c.is_open { return error('Already connected to server') } if c.is_open { return error('Already connected to server') }
socket := net.dial(c.server, c.port) or { return error('Connecting to server failed') } conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') }
c.socket = socket 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.expect_reply(.ready) or { return error('Received invalid response from server') }
c.send_ehlo() or { return error('Sending EHLO packet failed') } 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() ? { pub fn (mut c Client) quit() ? {
c.send_str('QUIT\r\n') c.send_str('QUIT\r\n')
c.expect_reply(.close) c.expect_reply(.close)
c.socket.close()? c.conn.close()?
c.is_open = false c.is_open = false
} }
// expect_reply checks if the SMTP server replied with the expected reply code // expect_reply checks if the SMTP server replied with the expected reply code
fn (c Client) expect_reply(expected ReplyCode) ? { 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? { $if smtp_debug? {
eprintln('\n\n[RECV START]') eprintln('\n\n[RECV]')
eprint(str) eprint(str)
} }
// Read remaining data in the socket if str.len >= 3 {
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 {
status := str[..3].int() status := str[..3].int()
if status != expected { if status != expected {
return error('Received unexpected status code $status, expecting $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()) eprint(s.trim_space())
eprintln('\n[SEND END]') eprintln('\n[SEND END]')
} }
c.socket.send_string(s)? c.conn.write(s.bytes())?
} }
[inline] [inline]

View File

@ -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)
}

View File

@ -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
}
}
}

View File

@ -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()
}

View File

@ -20,9 +20,9 @@ pub fn dial_tcp(address string) ?TcpConn {
return TcpConn { return TcpConn {
sock: s sock: s
read_timeout: -1 read_timeout: 30 * time.second
write_timeout: -1 write_timeout: 30 * time.second
} }
} }
@ -65,15 +65,15 @@ pub fn (c TcpConn) write(bytes []byte) ? {
return c.write_ptr(bytes.data, bytes.len) return c.write_ptr(bytes.data, bytes.len)
} }
// write_string blocks and attempts to write all data // write_str blocks and attempts to write all data
pub fn (c TcpConn) write_string(s string) ? { pub fn (c TcpConn) write_str(s string) ? {
return c.write_ptr(s.str, s.len) return c.write_ptr(s.str, s.len)
} }
pub fn (c TcpConn) read_into_ptr(buf_ptr byteptr, len int) ?int { pub fn (c TcpConn) read_ptr(buf_ptr byteptr, len int) ?int {
res := C.recv(c.sock.handle, buf_ptr, len, 0) mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0))?
if res >= 0 { if res > 0 {
return res return res
} }
@ -81,7 +81,8 @@ pub fn (c TcpConn) read_into_ptr(buf_ptr byteptr, len int) ?int {
match code { match code {
error_ewouldblock { error_ewouldblock {
c.wait_for_read()? 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 { else {
wrap_error(code)? 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 { pub fn (c TcpConn) read(mut buf []byte) ?int {
res := C.recv(c.sock.handle, buf.data, buf.len, 0) return c.read_ptr(buf.data, buf.len)
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_deadline() ?time.Time { 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) 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 { pub fn (c TcpConn) str() string {
// TODO // TODO
return 'TcpConn' return 'TcpConn'
@ -194,8 +196,8 @@ pub fn listen_tcp(port int) ?TcpListener {
return TcpListener { return TcpListener {
sock: s sock: s
accept_timeout: -1
accept_deadline: no_deadline accept_deadline: no_deadline
accept_timeout: infinite_timeout
} }
} }
@ -220,14 +222,10 @@ pub fn (l TcpListener) accept() ?TcpConn {
} }
} }
new_sock := TcpSocket { new_sock := tcp_socket_from_handle(new_handle)?
handle: new_handle
}
return TcpConn{ return TcpConn{
sock: new_sock sock: new_sock
read_timeout: -1
write_timeout: -1
} }
} }
@ -279,7 +277,22 @@ fn new_tcp_socket() ?TcpSocket {
t := true t := true
socket_error(C.ioctlsocket(sockfd, fionbio, &t))? socket_error(C.ioctlsocket(sockfd, fionbio, &t))?
} $else { } $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 return s
} }
@ -326,23 +339,19 @@ fn (s TcpSocket) connect(a string) ? {
return none return none
} }
errcode := error_code() _ := error_code()
if errcode == error_ewouldblock { write_result := s.@select(.write, connect_timeout)?
write_result := s.@select(.write, connect_timeout)? if write_result {
if write_result { // succeeded
// succeeded return none
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
} }
except_result := s.@select(.except, connect_timeout)?
return wrap_error(errcode) if except_result {
return err_connect_failed
}
// otherwise we timed out
return err_connect_timed_out
} }
// address gets the address of a socket // address gets the address of a socket
@ -352,5 +361,5 @@ pub fn (s TcpSocket) address() ?Addr {
// cast to the correct type // cast to the correct type
sockaddr := &C.sockaddr(&addr) sockaddr := &C.sockaddr(&addr)
C.getsockname(s.handle, sockaddr, &size) C.getsockname(s.handle, sockaddr, &size)
return new_addr(sockaddr, '', 0) return new_addr(sockaddr)
} }

View File

@ -1,4 +1,4 @@
import x.net import net
import time import time
const ( const (
@ -13,7 +13,7 @@ fn handle_conn(_c net.TcpConn) {
c.set_write_timeout(10 * time.second) c.set_write_timeout(10 * time.second)
for { for {
mut buf := []byte{len: 100, init: 0} mut buf := []byte{len: 100, init: 0}
read := c.read_into(mut buf) or { read := c.read(mut buf) or {
println('Server: connection dropped') println('Server: connection dropped')
return return
} }
@ -44,9 +44,9 @@ fn echo() ? {
c.set_read_timeout(10 * time.second) c.set_read_timeout(10 * time.second)
c.set_write_timeout(10 * time.second) c.set_write_timeout(10 * time.second)
data := 'Hello from vlib/net!' data := 'Hello from vlib/net!'
c.write_string(data)? c.write_str(data)?
mut buf := []byte{len: 100, init: 0} mut buf := []byte{len: 4096}
read := c.read_into(mut buf)? read := c.read(mut buf)?
assert read == data.len assert read == data.len
for i := 0; i < read; i++ { for i := 0; i < read; i++ {
assert buf[i] == data[i] assert buf[i] == data[i]

View File

@ -48,7 +48,7 @@ pub fn (c UdpConn) write(buf []byte) ? {
return c.write_ptr(buf.data, buf.len) 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) 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) 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 // read 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) { pub fn (c UdpConn) read(mut buf []byte) ?(int, Addr) {
mut addr_from := C.sockaddr{} mut addr_from := C.sockaddr{}
len := sizeof(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 { if res > 0 {
port_from := (&C.sockaddr_in(&addr_from)).sin_port addr := new_addr(addr_from)?
addr := new_addr(addr_from, '', port_from)?
return res, addr return res, addr
} }
@ -100,10 +99,11 @@ pub fn (c UdpConn) read_into(mut buf []byte) ?(int, Addr) {
match code { match code {
error_ewouldblock { error_ewouldblock {
c.wait_for_read()? 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)?
addr := new_addr(addr_from, '', port_from)?
return res2, addr return res2, addr
} }
else { else {
@ -114,12 +114,6 @@ pub fn (c UdpConn) read_into(mut buf []byte) ?(int, Addr) {
return none 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 { pub fn (c UdpConn) read_deadline() ?time.Time {
if c.read_deadline.unix == 0 { if c.read_deadline.unix == 0 {
return c.read_deadline return c.read_deadline

View File

@ -1,4 +1,4 @@
import x.net import net
import time import time
fn echo_server(_c net.UdpConn) { fn echo_server(_c net.UdpConn) {
@ -9,7 +9,7 @@ fn echo_server(_c net.UdpConn) {
c.set_write_timeout(10 * time.second) c.set_write_timeout(10 * time.second)
for { for {
mut buf := []byte{ len: 100, init: 0 } mut buf := []byte{ len: 100, init: 0 }
read, addr := c.read_into(mut buf) or { read, addr := c.read(mut buf) or {
continue continue
} }
@ -31,10 +31,10 @@ fn echo() ? {
data := 'Hello from vlib/net!' data := 'Hello from vlib/net!'
c.write_string(data)? c.write_str(data)?
mut buf := []byte{ len: 100, init: 0 } mut buf := []byte{ len: 100, init: 0 }
read, addr := c.read_into(mut buf)? read, addr := c.read(mut buf)?
assert read == data.len assert read == data.len
println('Got address $addr') println('Got address $addr')

View File

@ -1,7 +1,5 @@
module os module os
import strings
pub struct File { pub struct File {
cfile voidptr // Using void* instead of FILE* cfile voidptr // Using void* instead of FILE*
pub: pub:
@ -17,12 +15,13 @@ struct FileInfo {
[deprecated] [deprecated]
pub fn (f File) is_opened() bool { 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 return f.is_opened
} }
// **************************** Write ops *************************** // **************************** 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 { if !f.is_opened {
return error('file is not 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) written := C.fwrite(buf.data, buf.len, 1, f.cfile)
if written == 0 && s.len != 0 { if written == 0 && buf.len != 0 {
return error('0 bytes written') return error('0 bytes written')
} }
return written return written
@ -66,11 +65,23 @@ pub fn (mut f File) writeln(s string) ?int {
return (written + 1) 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 { 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) return C.fwrite(data, 1, size, f.cfile)
} }
[deprecated]
pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int { 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) C.fseek(f.cfile, pos, C.SEEK_SET)
res := C.fwrite(data, 1, size, f.cfile) res := C.fwrite(data, 1, size, f.cfile)
C.fseek(f.cfile, 0, C.SEEK_END) 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 ops ***************************
// read_bytes reads bytes from the beginning of the file // read_bytes reads bytes from the beginning of the file
[deprecated]
pub fn (f &File) read_bytes(size int) []byte { 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) return f.read_bytes_at(size, 0)
} }
// read_bytes_at reads bytes at the given position in the file // 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 { 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} mut arr := []byte{len: size}
nreadbytes := f.read_bytes_into(pos, mut arr) or { nreadbytes := f.read_bytes_into(pos, mut arr) or {
// return err // 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. // read_bytes_from fills `buf` with bytes at the given position in the file.
// `buf` must have length greater than zero. // `buf` must have length greater than zero.
// Returns number of bytes read or an error. // Returns number of bytes read or an error.
[deprecated]
pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int { 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 { if buf.len == 0 {
panic(@FN + ': `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 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 *********************** // **************************** 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() { pub fn (mut f File) flush() {
if !f.is_opened { if !f.is_opened {
return 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*. // 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 { pub fn (mut f File) get_line() ?string {
eprintln('File.get_line() is deprecated... Use a BufferedReader instead')
if !f.is_opened { if !f.is_opened {
return error('file is closed') return error('file is closed')
} }
$if !windows { return error('use io.new_buffered_reader')
mut zbuf := byteptr(0) /*
mut zblen := size_t(0) mut reader := io.new_buffered_reader({
mut zx := 0 reader: io.make_reader(f)
unsafe { })
zx = C.getline(&charptr(&zbuf), &zblen, f.cfile) return reader.read_line()
if zx == -1 { */
C.free(zbuf) }
if C.errno == 0 {
return error('end of file') pub fn (mut f File) write_str(s string) ? {
} if !f.is_opened {
return error(posix_get_error_msg(C.errno)) return error('file is closed')
} }
return zbuf.vstring_with_len(zx) f.write(s.bytes()) ?
}
}
//
// 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()
} }

View File

@ -860,7 +860,7 @@ pub fn home_dir() string {
// write_file writes `text` data to a file in `path`. // write_file writes `text` data to a file in `path`.
pub fn write_file(path string, text string) ? { pub fn write_file(path string, text string) ? {
mut f := os.create(path)? mut f := os.create(path)?
f.write(text) f.write(text.bytes())
f.close() f.close()
} }

View File

@ -33,7 +33,7 @@ fn test_open_file() {
mut file := os.open_file(filename, 'w+', 0o666) or { mut file := os.open_file(filename, 'w+', 0o666) or {
panic(err) panic(err)
} }
file.write(hello) file.write_str(hello)
file.close() file.close()
assert hello.len == os.file_size(filename) assert hello.len == os.file_size(filename)
read_hello := os.read_file(filename) or { read_hello := os.read_file(filename) or {
@ -64,26 +64,26 @@ fn test_open_file_binary() {
os.rm(filename) os.rm(filename)
} }
fn test_file_get_line() { // fn test_file_get_line() {
filename := './fgetline.txt' // filename := './fgetline.txt'
os.write_file(filename, 'line 1\nline 2') // os.write_file(filename, 'line 1\nline 2')
mut f := os.open_file(filename, 'r', 0) or { // mut f := os.open_file(filename, 'r', 0) or {
assert false // assert false
return // return
} // }
line1 := f.get_line() or { // line1 := f.get_line() or {
'' // ''
} // }
line2 := f.get_line() or { // line2 := f.get_line() or {
'' // ''
} // }
f.close() // f.close()
// // //
// eprintln('line1: $line1') // eprintln('line1: $line1 $line1.bytes()')
// eprintln('line2: $line2') // eprintln('line2: $line2 $line2.bytes()')
assert line1 == 'line 1\n' // assert line1 == 'line 1\n'
assert line2 == 'line 2' // assert line2 == 'line 2'
} // }
fn test_create_file() { fn test_create_file() {
filename := './test1.txt' filename := './test1.txt'
@ -91,7 +91,7 @@ fn test_create_file() {
mut f := os.create(filename) or { mut f := os.create(filename) or {
panic(err) panic(err)
} }
f.write(hello) f.write_str(hello)
f.close() f.close()
assert hello.len == os.file_size(filename) assert hello.len == os.file_size(filename)
os.rm(filename) os.rm(filename)

View File

@ -217,12 +217,6 @@ fn (mut g Gen) fn_args(args []table.Param, is_variadic bool) ([]string, []string
typ := g.unwrap_generic(arg.typ) typ := g.unwrap_generic(arg.typ)
arg_type_sym := g.table.get_type_symbol(typ) arg_type_sym := g.table.get_type_symbol(typ)
mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name) 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<T>() => foo_int(), foo_string() etc
}
is_varg := i == args.len - 1 && is_variadic is_varg := i == args.len - 1 && is_variadic
if is_varg { if is_varg {
varg_type_str := int(arg.typ).str() varg_type_str := int(arg.typ).str()

View File

@ -107,7 +107,7 @@ fn (am AssetManager) combine(asset_type string, to_file bool) string {
mut file := os.create(out_file) or { mut file := os.create(out_file) or {
panic(err) panic(err)
} }
file.write(out) file.write(out.bytes())
file.close() file.close()
return out_file return out_file
} }

View File

@ -3,6 +3,7 @@ import time
import json import json
import net import net
import net.http import net.http
import io
const ( const (
sport = 12380 sport = 12380
@ -10,6 +11,8 @@ const (
vexe = os.getenv('VEXE') vexe = os.getenv('VEXE')
vroot = os.dir(vexe) vroot = os.dir(vexe)
serverexe = os.join_path(os.cache_dir(), 'vweb_test_server.exe') 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 // setup of vweb webserver
@ -18,33 +21,26 @@ fn testsuite_begin() {
if os.exists(serverexe) { if os.exists(serverexe) {
os.rm(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() { 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 did_server_compile == 0
assert os.exists(serverexe) assert os.exists(serverexe)
} }
fn test_a_simple_vweb_app_runs_in_the_background() { 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 ? { $if debug_net_socket_client ? {
eprintln('running:\n$server_exec_cmd') eprintln('running:\n$server_exec_cmd')
} }
res := os.system(server_exec_cmd) $if windows {
assert res == 0 go os.system(server_exec_cmd)
} $else {
res := os.system(server_exec_cmd)
assert res == 0
}
time.sleep_ms(100) 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/zxcnbnm',
'http://127.0.0.1:$sport/JHKAJA', 'http://127.0.0.1:$sport/JHKAJA',
'http://127.0.0.1:$sport/unknown', 'http://127.0.0.1:$sport/unknown',
] ]
for url in url_404_list { for url in url_404_list {
res := http.get(url) or { res := http.get(url) or {
panic(err) panic(err)
@ -226,7 +222,7 @@ fn testsuite_end() {
// utility code: // utility code:
struct SimpleTcpClientConfig { struct SimpleTcpClientConfig {
retries int = 20 retries int = 20
host string = 'static.dev' host string = 'static.dev'
path string = '/' path string = '/'
agent string = 'v/net.tcp.v' agent string = 'v/net.tcp.v'
@ -235,11 +231,11 @@ struct SimpleTcpClientConfig {
} }
fn simple_tcp_client(config SimpleTcpClientConfig) ?string { fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
mut client := net.Socket{} mut client := net.TcpConn{}
mut tries := 0 mut tries := 0
for tries < config.retries { for tries < config.retries {
tries++ tries++
client = net.dial('127.0.0.1', sport) or { client = net.dial_tcp('127.0.0.1:$sport') or {
if tries > config.retries { if tries > config.retries {
return error(err) return error(err)
} }
@ -248,10 +244,11 @@ fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
} }
break break
} }
client.set_read_timeout(tcp_r_timeout)
client.set_write_timeout(tcp_w_timeout)
defer { defer {
client.close() or { } client.close()
} }
//
message := 'GET $config.path HTTP/1.1 message := 'GET $config.path HTTP/1.1
Host: $config.host Host: $config.host
User-Agent: $config.agent User-Agent: $config.agent
@ -261,11 +258,10 @@ $config.content'
$if debug_net_socket_client ? { $if debug_net_socket_client ? {
eprintln('sending:\n$message') eprintln('sending:\n$message')
} }
client.send(message.str, message.len)? client.write(message.bytes()) ?
bytes, blen := client.recv(4096) read := io.read_all(client) ?
received := unsafe {bytes.vstring_with_len(blen)}
$if debug_net_socket_client ? { $if debug_net_socket_client ? {
eprintln('received:\n$received') eprintln('received:\n$read')
} }
return received return read.bytestr()
} }

View File

@ -4,6 +4,7 @@
module vweb module vweb
import os import os
import io
import net import net
import net.http import net.http
import net.urllib import net.urllib
@ -44,8 +45,8 @@ mut:
content_type string = 'text/plain' content_type string = 'text/plain'
status string = '200 OK' status string = '200 OK'
pub: pub:
req http.Request req http.Request
conn net.Socket conn net.TcpConn
// TODO Response // TODO Response
pub mut: pub mut:
form map[string]string form map[string]string
@ -87,9 +88,7 @@ fn (mut ctx Context) send_response_to_client(mimetype string, res string) bool {
defer { defer {
s.free() s.free()
} }
ctx.conn.send_string(s) or { send_string(ctx.conn, s) or { return false }
return false
}
return true return true
} }
@ -117,9 +116,7 @@ pub fn (mut ctx Context) redirect(url string) Result {
return Result{} return Result{}
} }
ctx.done = true ctx.done = true
ctx.conn.send_string('HTTP/1.1 302 Found\r\nLocation: $url$ctx.headers\r\n$headers_close') or { send_string(ctx.conn, 'HTTP/1.1 302 Found\r\nLocation: ${url}${ctx.headers}\r\n${headers_close}') or { return Result{} }
return Result{}
}
return Result{} return Result{}
} }
@ -128,8 +125,8 @@ pub fn (mut ctx Context) not_found() Result {
return Result{} return Result{}
} }
ctx.done = true ctx.done = true
ctx.conn.send_string(http_404) or { } send_string(ctx.conn, http_404) or {}
return Result{} return vweb.Result{}
} }
pub fn (mut ctx Context) set_cookie(cookie Cookie) { pub fn (mut ctx Context) set_cookie(cookie Cookie) {
@ -202,9 +199,7 @@ pub fn run<T>(port int) {
pub fn run_app<T>(mut app T, port int) { pub fn run_app<T>(mut app T, port int) {
println('Running a Vweb app on http://localhost:$port') println('Running a Vweb app on http://localhost:$port')
l := net.listen(port) or { l := net.listen_tcp(port) or { panic('failed to listen') }
panic('failed to listen')
}
app.vweb = Context{} app.vweb = Context{}
app.init_once() app.init_once()
$for method in T.methods { $for method in T.methods {
@ -214,16 +209,13 @@ pub fn run_app<T>(mut app T, port int) {
} }
// app.reset() // app.reset()
for { for {
conn := l.accept() or { mut conn := l.accept() or { panic('accept() failed') }
panic('accept() failed') handle_conn<T>(mut conn, mut app)
} //app.vweb.page_gen_time = time.ticks() - t
// handle_conn<T>(conn, mut app) //eprintln('handle conn() took ${time.ticks()-t}ms')
handle_conn<T>(conn, mut app) //message := readall(conn)
// app.vweb.page_gen_time = time.ticks() - t //println(message)
// eprintln('handle conn() took ${time.ticks()-t}ms') /*
// message := readall(conn)
// println(message)
/*
if message.len > max_http_post_size { if message.len > max_http_post_size {
println('message.len = $message.len > max_http_post_size') println('message.len = $message.len > max_http_post_size')
conn.send_string(http_500) or {} conn.send_string(http_500) or {}
@ -243,15 +235,20 @@ pub fn run_app<T>(mut app T, port int) {
} }
} }
fn handle_conn<T>(conn net.Socket, mut app T) { fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
defer { conn.set_read_timeout(1 * time.second)
conn.close() or { } defer { conn.close() or {} }
} //fn handle_conn<T>(conn net.Socket, app_ T) T {
// fn handle_conn<T>(conn net.Socket, app_ T) T { //mut app := app_
// mut app := app_ //first_line := strip(lines[0])
// first_line := strip(lines[0])
mut reader := io.new_buffered_reader(reader: io.make_reader(conn))
page_gen_start := time.ticks() page_gen_start := time.ticks()
first_line := conn.read_line() first_line := reader.read_line() or {
println('Failed first_line')
return
}
$if debug { $if debug {
println('firstline="$first_line"') println('firstline="$first_line"')
} }
@ -261,29 +258,37 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
vals := first_line.split(' ') vals := first_line.split(' ')
if vals.len < 2 { if vals.len < 2 {
println('no vals for http') println('no vals for http')
conn.send_string(http_500) or { } send_string(conn, http_500) or {}
return return
} }
mut headers := []string{} mut headers := []string{}
mut body := '' mut body := ''
mut in_headers := true mut in_headers := true
mut len := 0 mut len := 0
// for line in lines[1..] { //for line in lines[1..] {
for _ in 0 .. 100 { for _ in 0..100 {
// println(j) //println(j)
line := conn.read_line() line := reader.read_line() or {
println('Failed read_line')
break
}
sline := strip(line) sline := strip(line)
if sline == '' { if sline == '' {
// if in_headers { //if in_headers {
// End of headers, no body => exit // End of headers, no body => exit
if len == 0 { if len == 0 {
break break
} }
// } //else { //} //else {
// End of body // End of body
// break //break
// } //}
in_headers = false
// read body
read_body := io.read_all(reader) or { []byte{} }
body += read_body.bytestr()
break
} }
if in_headers { if in_headers {
// println(sline) // println(sline)
@ -292,12 +297,6 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
len = sline.all_after(': ').int() len = sline.all_after(': ').int()
// println('GOT CL=$len') // println('GOT CL=$len')
} }
} else {
body += line.trim_left('\r\n')
if body.len >= len {
break
}
// println('body:$body')
} }
} }
req := http.Request{ req := http.Request{
@ -343,7 +342,7 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
mime_type := app.vweb.static_mime_types[static_file_name] mime_type := app.vweb.static_mime_types[static_file_name]
if static_file != '' && mime_type != '' { if static_file != '' && mime_type != '' {
data := os.read_file(static_file) or { data := os.read_file(static_file) or {
conn.send_string(http_404) or { } send_string(conn, http_404) or {}
return return
} }
app.vweb.send_response_to_client(mime_type, data) app.vweb.send_response_to_client(mime_type, data)
@ -480,7 +479,7 @@ fn handle_conn<T>(conn net.Socket, mut app T) {
} }
if action == '' { if action == '' {
// site not found // site not found
conn.send_string(http_404) or { } send_string(conn, http_404) or {}
return return
} }
$for method in T.methods { $for method in T.methods {
@ -580,9 +579,7 @@ pub fn (ctx &Context) ip() string {
ip = ip.all_before(',') ip = ip.all_before(',')
} }
if ip == '' { if ip == '' {
ip = ctx.conn.peer_ip() or { ip = ctx.conn.peer_ip() or { '' }
''
}
} }
return ip return ip
} }
@ -628,3 +625,8 @@ fn filter(s string) string {
} }
pub type RawHtml = string pub type RawHtml = string
fn send_string(conn net.TcpConn, s string) ? {
conn.write(s.bytes())?
}

View File

@ -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)
}

View File

@ -1,7 +1,7 @@
module openssl module openssl
import net.openssl import net.openssl
import x.net import net
import time import time
// const ( // const (

View File

@ -111,7 +111,7 @@ fn (mut ws Client) read_handshake_str() ?string {
mut msg := [1024]byte{} mut msg := [1024]byte{}
mut buffer := [1]byte{} mut buffer := [1]byte{}
for total_bytes_read < 1024 { 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 { if bytes_read == 0 {
return error_with_code('unexpected no response from handshake', 5) return error_with_code('unexpected no response from handshake', 5)
} }

View File

@ -1,22 +1,22 @@
module websocket module websocket
import x.net import net
import time import time
interface WebsocketIO { interface WebsocketIO {
socket_read_into(mut buffer []byte) ?int socket_read(mut buffer []byte) ?int
socket_write(bytes []byte) ? socket_write(bytes []byte) ?
} }
// socket_read_into reads into the provided buffer with its length // socket_read reads into the provided buffer with its length
fn (mut ws Client) socket_read_into(mut buffer []byte) ?int { fn (mut ws Client) socket_read(mut buffer []byte) ?int {
lock { lock {
if ws.is_ssl { if ws.is_ssl {
r := ws.ssl_conn.read_into(mut buffer)? r := ws.ssl_conn.read_into(mut buffer)?
return r return r
} else { } else {
for { for {
r := ws.conn.read_into(mut buffer) or { r := ws.conn.read(mut buffer) or {
if errcode == net.err_timed_out_code { if errcode == net.err_timed_out_code {
continue 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 { lock {
if ws.is_ssl { if ws.is_ssl {
r := ws.ssl_conn.socket_read_into_ptr(buf_ptr, len)? r := ws.ssl_conn.socket_read_into_ptr(buf_ptr, len)?
return r return r
} else { } else {
for { 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 { if errcode == net.err_timed_out_code {
continue continue
} }

View File

@ -86,7 +86,7 @@ fn (mut ws Client) read_payload(frame &Frame) ?[]byte {
mut read_buf := [1]byte{} mut read_buf := [1]byte{}
mut bytes_read := 0 mut bytes_read := 0
for bytes_read < frame.payload_len { 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 { if len != 1 {
return error('expected read all message, got zero') 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 { for ws.state == .open {
// Todo: different error scenarios to make sure we close correctly on error // Todo: different error scenarios to make sure we close correctly on error
// reader.read_into(mut rbuff) ? // 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 { if read_bytes == 0 {
// This is probably a timeout or close // This is probably a timeout or close
continue continue

View File

@ -7,7 +7,7 @@
// check with valgrind if you do any changes in the free calls // check with valgrind if you do any changes in the free calls
module websocket module websocket
import x.net import net
import x.openssl import x.openssl
import net.urllib import net.urllib
import time import time
@ -98,6 +98,8 @@ pub fn (mut ws Client) connect() ? {
ws.set_state(.connecting) ws.set_state(.connecting)
ws.logger.info('connecting to host $ws.uri') ws.logger.info('connecting to host $ws.uri')
ws.conn = ws.dial_socket()? ws.conn = ws.dial_socket()?
ws.conn.set_read_timeout(net.infinite_timeout)
ws.conn.set_write_timeout(net.infinite_timeout)
ws.handshake()? ws.handshake()?
ws.set_state(.open) ws.set_state(.open)
ws.logger.info('successfully connected to host $ws.uri') ws.logger.info('successfully connected to host $ws.uri')

View File

@ -1,7 +1,7 @@
// The module websocket implements the websocket server capabilities // The module websocket implements the websocket server capabilities
module websocket module websocket
import x.net import net
import x.openssl import x.openssl
import log import log
import sync import sync

View File

@ -1,6 +1,6 @@
module websocket module websocket
import x.net import net
fn error_code() int { fn error_code() int {
return C.WSAGetLastError() return C.WSAGetLastError()