vlib/net: add buffered IO, x.net -> net (#6754)
parent
20bec81678
commit
cd2a2cef25
|
@ -71,3 +71,7 @@ thumbs.db
|
|||
/.bin
|
||||
|
||||
_docs
|
||||
|
||||
# ignore vs databases
|
||||
*.suo
|
||||
*.VC.db
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import net
|
||||
|
||||
conn := net.dial_tcp('google.com:80')?
|
||||
peer_addr := conn.peer_addr()?
|
||||
println('$peer_addr')
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))?
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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]
|
|
@ -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
|
|
@ -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')
|
118
vlib/os/file.v
118
vlib/os/file.v
|
@ -1,7 +1,5 @@
|
|||
module os
|
||||
|
||||
import strings
|
||||
|
||||
pub struct File {
|
||||
cfile voidptr // Using void* instead of FILE*
|
||||
pub:
|
||||
|
@ -17,12 +15,13 @@ struct FileInfo {
|
|||
|
||||
[deprecated]
|
||||
pub fn (f File) is_opened() bool {
|
||||
eprintln('warning: `file.is_opened()` has been deprecated, use `file.is_opened` instead')
|
||||
eprintln('warning: `File.is_opened()` has been deprecated, use `File.is_opened` instead')
|
||||
return f.is_opened
|
||||
}
|
||||
|
||||
// **************************** Write ops ***************************
|
||||
pub fn (mut f File) write(s string) ?int {
|
||||
// write implements the Writer interface
|
||||
pub fn (mut f File) write(buf []byte) ?int {
|
||||
if !f.is_opened {
|
||||
return error('file is not opened')
|
||||
}
|
||||
|
@ -34,8 +33,8 @@ pub fn (mut f File) write(s string) ?int {
|
|||
}
|
||||
}
|
||||
*/
|
||||
written := C.fwrite(s.str, s.len, 1, f.cfile)
|
||||
if written == 0 && s.len != 0 {
|
||||
written := C.fwrite(buf.data, buf.len, 1, f.cfile)
|
||||
if written == 0 && buf.len != 0 {
|
||||
return error('0 bytes written')
|
||||
}
|
||||
return written
|
||||
|
@ -66,11 +65,23 @@ pub fn (mut f File) writeln(s string) ?int {
|
|||
return (written + 1)
|
||||
}
|
||||
|
||||
// write_to implements the RandomWriter interface
|
||||
pub fn (mut f File) write_to(pos int, buf []byte) ?int {
|
||||
C.fseek(f.cfile, pos, C.SEEK_SET)
|
||||
res := C.fwrite(buf.data, 1, buf.len, f.cfile)
|
||||
C.fseek(f.cfile, 0, C.SEEK_END)
|
||||
return res
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
pub fn (mut f File) write_bytes(data voidptr, size int) int {
|
||||
eprintln('warning `File.write_bytes()` has been deprecated, use `File.write` instead')
|
||||
return C.fwrite(data, 1, size, f.cfile)
|
||||
}
|
||||
|
||||
[deprecated]
|
||||
pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int {
|
||||
eprintln('warning `File.write_bytes_at()` has been deprecated, use `File.write_at` instead')
|
||||
C.fseek(f.cfile, pos, C.SEEK_SET)
|
||||
res := C.fwrite(data, 1, size, f.cfile)
|
||||
C.fseek(f.cfile, 0, C.SEEK_END)
|
||||
|
@ -79,12 +90,16 @@ pub fn (mut f File) write_bytes_at(data voidptr, size int, pos int) int {
|
|||
|
||||
// **************************** Read ops ***************************
|
||||
// read_bytes reads bytes from the beginning of the file
|
||||
[deprecated]
|
||||
pub fn (f &File) read_bytes(size int) []byte {
|
||||
eprintln('warning `File.read_bytes()` has been deprecated, use `File.read` instead')
|
||||
return f.read_bytes_at(size, 0)
|
||||
}
|
||||
|
||||
// read_bytes_at reads bytes at the given position in the file
|
||||
[deprecated]
|
||||
pub fn (f &File) read_bytes_at(size int, pos int) []byte {
|
||||
eprintln('warning `File.read_bytes_at()` has been deprecated, use `File.read_at` instead')
|
||||
mut arr := []byte{len: size}
|
||||
nreadbytes := f.read_bytes_into(pos, mut arr) or {
|
||||
// return err
|
||||
|
@ -96,7 +111,9 @@ pub fn (f &File) read_bytes_at(size int, pos int) []byte {
|
|||
// read_bytes_from fills `buf` with bytes at the given position in the file.
|
||||
// `buf` must have length greater than zero.
|
||||
// Returns number of bytes read or an error.
|
||||
[deprecated]
|
||||
pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int {
|
||||
eprintln('warning `File.read_bytes_into()` has been deprecated, use `File.read_from_into` instead')
|
||||
if buf.len == 0 {
|
||||
panic(@FN + ': `buf.len` == 0')
|
||||
}
|
||||
|
@ -114,8 +131,35 @@ pub fn (f &File) read_bytes_into(pos int, mut buf []byte) ?int {
|
|||
return nbytes
|
||||
}
|
||||
|
||||
// read implements the Reader interface
|
||||
pub fn (f &File) read(mut buf []byte) ?int {
|
||||
if buf.len == 0 {
|
||||
return 0
|
||||
}
|
||||
C.errno = 0
|
||||
nbytes := C.fread(buf.data, 1, buf.len, f.cfile)
|
||||
if C.errno != 0 {
|
||||
return error(posix_get_error_msg(C.errno))
|
||||
}
|
||||
return nbytes
|
||||
}
|
||||
|
||||
// read_at reads buf.len bytes from pos in the file
|
||||
pub fn (f &File) read_at(pos int, mut buf []byte) ?int {
|
||||
if buf.len == 0 {
|
||||
return 0
|
||||
}
|
||||
C.fseek(f.cfile, pos, C.SEEK_SET)
|
||||
C.errno = 0
|
||||
nbytes := C.fread(buf.data, 1, buf.len, f.cfile)
|
||||
if C.errno != 0 {
|
||||
return error(posix_get_error_msg(C.errno))
|
||||
}
|
||||
return nbytes
|
||||
}
|
||||
|
||||
// **************************** Utility ops ***********************
|
||||
// write any unwritten data in stream's buffer
|
||||
// flush writes any unwritten data in stream's buffer
|
||||
pub fn (mut f File) flush() {
|
||||
if !f.is_opened {
|
||||
return
|
||||
|
@ -133,52 +177,24 @@ pub fn open_stdin() File {
|
|||
}
|
||||
|
||||
// File.get_line - get a single line from the file. NB: the ending newline is *included*.
|
||||
[deprecated]
|
||||
pub fn (mut f File) get_line() ?string {
|
||||
eprintln('File.get_line() is deprecated... Use a BufferedReader instead')
|
||||
if !f.is_opened {
|
||||
return error('file is closed')
|
||||
}
|
||||
$if !windows {
|
||||
mut zbuf := byteptr(0)
|
||||
mut zblen := size_t(0)
|
||||
mut zx := 0
|
||||
unsafe {
|
||||
zx = C.getline(&charptr(&zbuf), &zblen, f.cfile)
|
||||
if zx == -1 {
|
||||
C.free(zbuf)
|
||||
if C.errno == 0 {
|
||||
return error('end of file')
|
||||
}
|
||||
return error(posix_get_error_msg(C.errno))
|
||||
}
|
||||
return zbuf.vstring_with_len(zx)
|
||||
}
|
||||
}
|
||||
//
|
||||
// using C.fgets is less efficient than f.get_line_getline,
|
||||
// but is available everywhere, while C.getline does not work
|
||||
// on windows
|
||||
//
|
||||
buf := [4096]byte{}
|
||||
mut res := strings.new_builder(1024)
|
||||
mut x := charptr(0)
|
||||
for {
|
||||
unsafe {
|
||||
x = C.fgets(charptr(buf), 4096, f.cfile)
|
||||
}
|
||||
if x == 0 {
|
||||
if res.len > 0 {
|
||||
break
|
||||
}
|
||||
return error('end of file')
|
||||
}
|
||||
bufbp := byteptr(buf)
|
||||
mut blen := vstrlen(bufbp)
|
||||
res.write_bytes(bufbp, blen)
|
||||
unsafe {
|
||||
if blen == 0 || bufbp[blen - 1] == `\n` || bufbp[blen - 1] == `\r` {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.str()
|
||||
return error('use io.new_buffered_reader')
|
||||
/*
|
||||
mut reader := io.new_buffered_reader({
|
||||
reader: io.make_reader(f)
|
||||
})
|
||||
return reader.read_line()
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn (mut f File) write_str(s string) ? {
|
||||
if !f.is_opened {
|
||||
return error('file is closed')
|
||||
}
|
||||
f.write(s.bytes()) ?
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
116
vlib/vweb/vweb.v
116
vlib/vweb/vweb.v
|
@ -4,6 +4,7 @@
|
|||
module vweb
|
||||
|
||||
import os
|
||||
import io
|
||||
import net
|
||||
import net.http
|
||||
import net.urllib
|
||||
|
@ -44,8 +45,8 @@ mut:
|
|||
content_type string = 'text/plain'
|
||||
status string = '200 OK'
|
||||
pub:
|
||||
req http.Request
|
||||
conn net.Socket
|
||||
req http.Request
|
||||
conn net.TcpConn
|
||||
// TODO Response
|
||||
pub mut:
|
||||
form map[string]string
|
||||
|
@ -87,9 +88,7 @@ fn (mut ctx Context) send_response_to_client(mimetype string, res string) bool {
|
|||
defer {
|
||||
s.free()
|
||||
}
|
||||
ctx.conn.send_string(s) or {
|
||||
return false
|
||||
}
|
||||
send_string(ctx.conn, s) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -117,9 +116,7 @@ pub fn (mut ctx Context) redirect(url string) Result {
|
|||
return Result{}
|
||||
}
|
||||
ctx.done = true
|
||||
ctx.conn.send_string('HTTP/1.1 302 Found\r\nLocation: $url$ctx.headers\r\n$headers_close') or {
|
||||
return Result{}
|
||||
}
|
||||
send_string(ctx.conn, 'HTTP/1.1 302 Found\r\nLocation: ${url}${ctx.headers}\r\n${headers_close}') or { return Result{} }
|
||||
return Result{}
|
||||
}
|
||||
|
||||
|
@ -128,8 +125,8 @@ pub fn (mut ctx Context) not_found() Result {
|
|||
return Result{}
|
||||
}
|
||||
ctx.done = true
|
||||
ctx.conn.send_string(http_404) or { }
|
||||
return Result{}
|
||||
send_string(ctx.conn, http_404) or {}
|
||||
return vweb.Result{}
|
||||
}
|
||||
|
||||
pub fn (mut ctx Context) set_cookie(cookie Cookie) {
|
||||
|
@ -202,9 +199,7 @@ pub fn run<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())?
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
module openssl
|
||||
|
||||
import net.openssl
|
||||
import x.net
|
||||
import net
|
||||
import time
|
||||
|
||||
// const (
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module websocket
|
||||
|
||||
import x.net
|
||||
import net
|
||||
|
||||
fn error_code() int {
|
||||
return C.WSAGetLastError()
|
||||
|
|
Loading…
Reference in New Issue