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
_docs
# ignore vs databases
*.suo
*.VC.db

View File

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

View File

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

View File

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

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

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_
}
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.
// []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 {
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

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

View File

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

View File

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

View File

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

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 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]

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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)
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<T>() => foo_int(), foo_string() etc
}
is_varg := i == args.len - 1 && is_variadic
if is_varg {
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 {
panic(err)
}
file.write(out)
file.write(out.bytes())
file.close()
return out_file
}

View File

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

View File

@ -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<T>(port int) {
pub fn run_app<T>(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<T>(mut app T, port int) {
}
// app.reset()
for {
conn := l.accept() or {
panic('accept() failed')
}
// handle_conn<T>(conn, mut app)
handle_conn<T>(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<T>(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<T>(mut app T, port int) {
}
}
fn handle_conn<T>(conn net.Socket, mut app T) {
defer {
conn.close() or { }
}
// fn handle_conn<T>(conn net.Socket, app_ T) T {
// mut app := app_
// first_line := strip(lines[0])
fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
conn.set_read_timeout(1 * time.second)
defer { conn.close() or {} }
//fn handle_conn<T>(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<T>(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<T>(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<T>(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<T>(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())?
}

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
import net.openssl
import x.net
import net
import time
// const (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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