net: use mut and refs as receivers consistently (#8205)

pull/8221/head
Delyan Angelov 2021-01-20 12:11:01 +02:00 committed by GitHub
parent 158aefc37f
commit d92f5c55ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 236 additions and 206 deletions

View File

@ -55,7 +55,7 @@ fn (mut dtp DTP) read() ?[]byte {
return data return data
} }
fn (dtp DTP) close() { fn (mut dtp DTP) close() {
dtp.conn.close() dtp.conn.close()
} }

View File

@ -9,7 +9,7 @@ module net
#include <netdb.h> #include <netdb.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#flag solaris -lsocket
fn error_code() int { fn error_code() int {
return C.errno return C.errno
} }
@ -24,5 +24,3 @@ pub const (
const ( const (
error_ewouldblock = C.EWOULDBLOCK error_ewouldblock = C.EWOULDBLOCK
) )
#flag solaris -lsocket

View File

@ -12,14 +12,14 @@ import time
import io import io
const ( const (
recv_size = 128 recv_size = 128
) )
enum ReplyCode { enum ReplyCode {
ready = 220 ready = 220
close = 221 close = 221
auth_ok = 235 auth_ok = 235
action_ok = 250 action_ok = 250
mail_start = 354 mail_start = 354
} }
@ -39,7 +39,7 @@ pub:
password string password string
from string from string
pub mut: pub mut:
is_open bool is_open bool
} }
pub struct Mail { pub struct Mail {
@ -54,15 +54,19 @@ pub struct Mail {
} }
// new_client returns a new SMTP client and connects to it // new_client returns a new SMTP client and connects to it
pub fn new_client(config Client) ?Client { pub fn new_client(config Client) ?&Client {
mut c := config mut c := &Client{
c.reconnect()? ...config
}
c.reconnect() ?
return c return c
} }
// reconnect reconnects to the SMTP server if the connection was closed // reconnect reconnects to the SMTP server if the connection was closed
pub fn (mut c Client) reconnect() ? { pub fn (mut c Client) reconnect() ? {
if c.is_open { return error('Already connected to server') } if c.is_open {
return error('Already connected to server')
}
conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') } conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') }
c.conn = conn c.conn = conn
@ -76,29 +80,34 @@ pub fn (mut c Client) reconnect() ? {
} }
// send sends an email // send sends an email
pub fn (c Client) send(config Mail) ? { pub fn (mut c Client) send(config Mail) ? {
if !c.is_open { return error('Disconnected from server') } if !c.is_open {
return error('Disconnected from server')
}
from := if config.from != '' { config.from } else { c.from } from := if config.from != '' { config.from } else { c.from }
c.send_mailfrom(from) or { return error('Sending mailfrom failed') } c.send_mailfrom(from) or { return error('Sending mailfrom failed') }
c.send_mailto(config.to) or { return error('Sending mailto failed') } c.send_mailto(config.to) or { return error('Sending mailto failed') }
c.send_data() or { return error('Sending mail data failed') } c.send_data() or { return error('Sending mail data failed') }
c.send_body({ config | from: from }) or { return error('Sending mail body failed') } c.send_body({
config |
from: from
}) or { return error('Sending mail body failed') }
} }
// quit closes the connection to the server // quit closes the connection to the server
pub fn (mut c Client) quit() ? { pub fn (mut c Client) quit() ? {
c.send_str('QUIT\r\n') c.send_str('QUIT\r\n')
c.expect_reply(.close) c.expect_reply(.close)
c.conn.close()? c.conn.close() ?
c.is_open = false c.is_open = false
} }
// expect_reply checks if the SMTP server replied with the expected reply code // expect_reply checks if the SMTP server replied with the expected reply code
fn (c Client) expect_reply(expected ReplyCode) ? { fn (mut c Client) expect_reply(expected ReplyCode) ? {
bytes := io.read_all(reader: c.conn)? bytes := io.read_all(reader: c.conn) ?
str := bytes.bytestr().trim_space() str := bytes.bytestr().trim_space()
$if smtp_debug? { $if smtp_debug ? {
eprintln('\n\n[RECV]') eprintln('\n\n[RECV]')
eprint(str) eprint(str)
} }
@ -108,30 +117,32 @@ fn (c Client) expect_reply(expected ReplyCode) ? {
if ReplyCode(status) != expected { if ReplyCode(status) != expected {
return error('Received unexpected status code $status, expecting $expected') return error('Received unexpected status code $status, expecting $expected')
} }
} else { return error('Recieved unexpected SMTP data: $str') } } else {
return error('Recieved unexpected SMTP data: $str')
}
} }
[inline] [inline]
fn (c Client) send_str(s string) ? { fn (mut c Client) send_str(s string) ? {
$if smtp_debug? { $if smtp_debug ? {
eprintln('\n\n[SEND START]') eprintln('\n\n[SEND START]')
eprint(s.trim_space()) eprint(s.trim_space())
eprintln('\n[SEND END]') eprintln('\n[SEND END]')
} }
c.conn.write(s.bytes())? c.conn.write(s.bytes()) ?
} }
[inline] [inline]
fn (c Client) send_ehlo() ? { fn (mut c Client) send_ehlo() ? {
c.send_str('EHLO $c.server\r\n')? c.send_str('EHLO $c.server\r\n') ?
c.expect_reply(.action_ok)? c.expect_reply(.action_ok) ?
} }
[inline] [inline]
fn (c Client) send_auth() ? { fn (mut c Client) send_auth() ? {
if c.username.len == 0 { if c.username.len == 0 {
return return
} }
mut sb := strings.new_builder(100) mut sb := strings.new_builder(100)
sb.write_b(0) sb.write_b(0)
sb.write(c.username) sb.write(c.username)
@ -139,26 +150,26 @@ fn (c Client) send_auth() ? {
sb.write(c.password) sb.write(c.password)
a := sb.str() a := sb.str()
auth := 'AUTH PLAIN ${base64.encode(a)}\r\n' auth := 'AUTH PLAIN ${base64.encode(a)}\r\n'
c.send_str(auth)? c.send_str(auth) ?
c.expect_reply(.auth_ok)? c.expect_reply(.auth_ok) ?
} }
fn (c Client) send_mailfrom(from string) ? { fn (mut c Client) send_mailfrom(from string) ? {
c.send_str('MAIL FROM: <$from>\r\n')? c.send_str('MAIL FROM: <$from>\r\n') ?
c.expect_reply(.action_ok)? c.expect_reply(.action_ok) ?
} }
fn (c Client) send_mailto(to string) ? { fn (mut c Client) send_mailto(to string) ? {
c.send_str('RCPT TO: <$to>\r\n')? c.send_str('RCPT TO: <$to>\r\n') ?
c.expect_reply(.action_ok)? c.expect_reply(.action_ok) ?
} }
fn (c Client) send_data() ? { fn (mut c Client) send_data() ? {
c.send_str('DATA\r\n')? c.send_str('DATA\r\n') ?
c.expect_reply(.mail_start) c.expect_reply(.mail_start)
} }
fn (c Client) send_body(cfg Mail) ? { fn (mut c Client) send_body(cfg Mail) ? {
is_html := cfg.body_type == .html is_html := cfg.body_type == .html
date := cfg.date.utc_string().trim_right(' UTC') // TODO date := cfg.date.utc_string().trim_right(' UTC') // TODO
mut sb := strings.new_builder(200) mut sb := strings.new_builder(200)
@ -174,6 +185,6 @@ fn (c Client) send_body(cfg Mail) ? {
sb.write('\r\n\r\n') sb.write('\r\n\r\n')
sb.write(cfg.body) sb.write(cfg.body)
sb.write('\r\n.\r\n') sb.write('\r\n.\r\n')
c.send_str(sb.str())? c.send_str(sb.str()) ?
c.expect_reply(.action_ok)? c.expect_reply(.action_ok) ?
} }

View File

@ -3,7 +3,7 @@ import smtp
import time import time
// Used to test that a function call returns an error // Used to test that a function call returns an error
fn fn_errors(c smtp.Client, m smtp.Mail) bool { fn fn_errors(mut c smtp.Client, m smtp.Mail) bool {
c.send(m) or { return true } c.send(m) or { return true }
return false return false
} }
@ -14,7 +14,9 @@ fn fn_errors(c smtp.Client, m smtp.Mail) bool {
* Created by: nedimf (07/2020) * Created by: nedimf (07/2020)
*/ */
fn test_smtp() { fn test_smtp() {
$if !network ? { return } $if !network ? {
return
}
client_cfg := smtp.Client{ client_cfg := smtp.Client{
server: 'smtp.mailtrap.io' server: 'smtp.mailtrap.io'
@ -32,20 +34,57 @@ fn test_smtp() {
body: 'Plain text' body: 'Plain text'
} }
mut client := smtp.new_client(client_cfg) or { assert false return } mut client := smtp.new_client(client_cfg) or {
assert false
return
}
assert true assert true
client.send(send_cfg) or { assert false return } client.send(send_cfg) or {
assert false
return
}
assert true assert true
// client.send({ send_cfg | body_type: .html, body: '<html><h1>HTML V email!</h1></html>' }) or { assert false return } // client.send({ send_cfg | body_type: .html, body: '<html><h1>HTML V email!</h1></html>' }) or { assert false return }
client.send({ send_cfg | from: 'alexander@vlang.io' }) or { assert false return } client.send({
client.send({ send_cfg | cc: 'alexander@vlang.io,joe@vlang.io', bcc: 'spytheman@vlang.io' }) or { assert false return } send_cfg |
client.send({ send_cfg | date: time.now().add_days(1000) }) or { assert false return } from: 'alexander@vlang.io'
}) or {
assert false
return
}
client.send({
send_cfg |
cc: 'alexander@vlang.io,joe@vlang.io'
bcc: 'spytheman@vlang.io'
}) or {
assert false
return
}
client.send({
send_cfg |
date: time.now().add_days(1000)
}) or {
assert false
return
}
assert true assert true
client.quit() or { assert false return } client.quit() or {
assert false
return
}
assert true assert true
// This call should return an error, since the connection is closed // This call should return an error, since the connection is closed
if !fn_errors(client, send_cfg) { assert false return } if !fn_errors(mut client, send_cfg) {
client.reconnect() or { assert false return } assert false
client.send(send_cfg) or { assert false return } return
}
client.reconnect() or {
assert false
return
}
client.send(send_cfg) or {
assert false
return
}
assert true assert true
} }

View File

@ -8,8 +8,8 @@ const (
) )
pub struct TcpConn { pub struct TcpConn {
pub: pub mut:
sock TcpSocket sock TcpSocket
mut: mut:
write_deadline time.Time write_deadline time.Time
read_deadline time.Time read_deadline time.Time
@ -17,23 +17,23 @@ mut:
write_timeout time.Duration write_timeout time.Duration
} }
pub fn dial_tcp(address string) ?TcpConn { pub fn dial_tcp(address string) ?&TcpConn {
s := new_tcp_socket() ? mut s := new_tcp_socket() ?
s.connect(address) ? s.connect(address) ?
return TcpConn{ return &TcpConn{
sock: s sock: s
read_timeout: tcp_default_read_timeout read_timeout: tcp_default_read_timeout
write_timeout: tcp_default_write_timeout write_timeout: tcp_default_write_timeout
} }
} }
pub fn (c TcpConn) close() ? { pub fn (mut c TcpConn) close() ? {
c.sock.close() ? c.sock.close() ?
return none return none
} }
// write_ptr blocks and attempts to write all data // write_ptr blocks and attempts to write all data
pub fn (c TcpConn) write_ptr(b byteptr, len int) ? { pub fn (mut c TcpConn) write_ptr(b byteptr, len int) ? {
$if trace_tcp ? { $if trace_tcp ? {
eprintln('>>> TcpConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' + eprintln('>>> TcpConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' +
unsafe { b.vstring_with_len(len) }) unsafe { b.vstring_with_len(len) })
@ -61,16 +61,16 @@ pub fn (c TcpConn) write_ptr(b byteptr, len int) ? {
} }
// write blocks and attempts to write all data // write blocks and attempts to write all data
pub fn (c TcpConn) write(bytes []byte) ? { pub fn (mut c TcpConn) write(bytes []byte) ? {
return c.write_ptr(bytes.data, bytes.len) return c.write_ptr(bytes.data, bytes.len)
} }
// write_str blocks and attempts to write all data // write_str blocks and attempts to write all data
pub fn (c TcpConn) write_str(s string) ? { pub fn (mut c TcpConn) write_str(s string) ? {
return c.write_ptr(s.str, s.len) return c.write_ptr(s.str, s.len)
} }
pub fn (c TcpConn) read_ptr(buf_ptr byteptr, len int) ?int { pub fn (mut c TcpConn) read_ptr(buf_ptr byteptr, len int) ?int {
mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ? mut res := wrap_read_result(C.recv(c.sock.handle, buf_ptr, len, 0)) ?
$if trace_tcp ? { $if trace_tcp ? {
eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res') eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res')
@ -92,11 +92,11 @@ pub fn (c TcpConn) read_ptr(buf_ptr byteptr, len int) ?int {
return none return none
} }
pub fn (c TcpConn) read(mut buf []byte) ?int { pub fn (mut c TcpConn) read(mut buf []byte) ?int {
return c.read_ptr(buf.data, buf.len) return c.read_ptr(buf.data, buf.len)
} }
pub fn (c TcpConn) read_deadline() ?time.Time { pub fn (mut c TcpConn) read_deadline() ?time.Time {
if c.read_deadline.unix == 0 { if c.read_deadline.unix == 0 {
return c.read_deadline return c.read_deadline
} }
@ -107,7 +107,7 @@ pub fn (mut c TcpConn) set_read_deadline(deadline time.Time) {
c.read_deadline = deadline c.read_deadline = deadline
} }
pub fn (c TcpConn) write_deadline() ?time.Time { pub fn (mut c TcpConn) write_deadline() ?time.Time {
if c.write_deadline.unix == 0 { if c.write_deadline.unix == 0 {
return c.write_deadline return c.write_deadline
} }
@ -118,7 +118,7 @@ pub fn (mut c TcpConn) set_write_deadline(deadline time.Time) {
c.write_deadline = deadline c.write_deadline = deadline
} }
pub fn (c TcpConn) read_timeout() time.Duration { pub fn (c &TcpConn) read_timeout() time.Duration {
return c.read_timeout return c.read_timeout
} }
@ -126,7 +126,7 @@ pub fn (mut c TcpConn) set_read_timeout(t time.Duration) {
c.read_timeout = t c.read_timeout = t
} }
pub fn (c TcpConn) write_timeout() time.Duration { pub fn (c &TcpConn) write_timeout() time.Duration {
return c.write_timeout return c.write_timeout
} }
@ -135,23 +135,23 @@ pub fn (mut c TcpConn) set_write_timeout(t time.Duration) {
} }
[inline] [inline]
pub fn (c TcpConn) wait_for_read() ? { pub fn (mut c TcpConn) wait_for_read() ? {
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
} }
[inline] [inline]
pub fn (c TcpConn) wait_for_write() ? { pub fn (mut c TcpConn) wait_for_write() ? {
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
} }
pub fn (c TcpConn) peer_addr() ?Addr { pub fn (c &TcpConn) peer_addr() ?Addr {
mut addr := C.sockaddr{} mut addr := C.sockaddr{}
len := sizeof(C.sockaddr) len := sizeof(C.sockaddr)
socket_error(C.getpeername(c.sock.handle, &addr, &len)) ? socket_error(C.getpeername(c.sock.handle, &addr, &len)) ?
return new_addr(addr) return new_addr(addr)
} }
pub fn (c TcpConn) peer_ip() ?string { pub fn (c &TcpConn) peer_ip() ?string {
buf := [44]byte{} buf := [44]byte{}
peeraddr := C.sockaddr_in{} peeraddr := C.sockaddr_in{}
speeraddr := sizeof(peeraddr) speeraddr := sizeof(peeraddr)
@ -164,19 +164,20 @@ pub fn (c TcpConn) peer_ip() ?string {
return res return res
} }
pub fn (c TcpConn) str() string { pub fn (c &TcpConn) str() string {
// TODO // TODO
return 'TcpConn' return 'TcpConn {write_deadline: $c.write_deadline, read_deadline: $c.read_deadline, read_timeout: $c.read_timeout, write_timeout: $c.write_timeout, sock: $c.sock}'
} }
pub struct TcpListener { pub struct TcpListener {
sock TcpSocket pub mut:
sock TcpSocket
mut: mut:
accept_timeout time.Duration accept_timeout time.Duration
accept_deadline time.Time accept_deadline time.Time
} }
pub fn listen_tcp(port int) ?TcpListener { pub fn listen_tcp(port int) ?&TcpListener {
s := new_tcp_socket() ? s := new_tcp_socket() ?
validate_port(port) ? validate_port(port) ?
mut addr := C.sockaddr_in{} mut addr := C.sockaddr_in{}
@ -188,14 +189,14 @@ pub fn listen_tcp(port int) ?TcpListener {
sockaddr := unsafe { &C.sockaddr(&addr) } sockaddr := unsafe { &C.sockaddr(&addr) }
socket_error(C.bind(s.handle, sockaddr, size)) ? socket_error(C.bind(s.handle, sockaddr, size)) ?
socket_error(C.listen(s.handle, 128)) ? socket_error(C.listen(s.handle, 128)) ?
return TcpListener{ return &TcpListener{
sock: s sock: s
accept_deadline: no_deadline accept_deadline: no_deadline
accept_timeout: infinite_timeout accept_timeout: infinite_timeout
} }
} }
pub fn (l TcpListener) accept() ?TcpConn { pub fn (mut l TcpListener) accept() ?&TcpConn {
addr := C.sockaddr_storage{} addr := C.sockaddr_storage{}
unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_storage)) } unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_storage)) }
size := sizeof(C.sockaddr_storage) size := sizeof(C.sockaddr_storage)
@ -210,14 +211,14 @@ pub fn (l TcpListener) accept() ?TcpConn {
} }
} }
new_sock := tcp_socket_from_handle(new_handle) ? new_sock := tcp_socket_from_handle(new_handle) ?
return TcpConn{ return &TcpConn{
sock: new_sock sock: new_sock
read_timeout: tcp_default_read_timeout read_timeout: tcp_default_read_timeout
write_timeout: tcp_default_write_timeout write_timeout: tcp_default_write_timeout
} }
} }
pub fn (c TcpListener) accept_deadline() ?time.Time { pub fn (c &TcpListener) accept_deadline() ?time.Time {
if c.accept_deadline.unix != 0 { if c.accept_deadline.unix != 0 {
return c.accept_deadline return c.accept_deadline
} }
@ -228,7 +229,7 @@ pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) {
c.accept_deadline = deadline c.accept_deadline = deadline
} }
pub fn (c TcpListener) accept_timeout() time.Duration { pub fn (c &TcpListener) accept_timeout() time.Duration {
return c.accept_timeout return c.accept_timeout
} }
@ -236,16 +237,16 @@ pub fn (mut c TcpListener) set_accept_timeout(t time.Duration) {
c.accept_timeout = t c.accept_timeout = t
} }
pub fn (c TcpListener) wait_for_accept() ? { pub fn (mut c TcpListener) wait_for_accept() ? {
return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout) return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout)
} }
pub fn (c TcpListener) close() ? { pub fn (mut c TcpListener) close() ? {
c.sock.close() ? c.sock.close() ?
return none return none
} }
pub fn (c TcpListener) address() ?Addr { pub fn (c &TcpListener) address() ?Addr {
return c.sock.address() return c.sock.address()
} }
@ -256,7 +257,7 @@ pub:
fn new_tcp_socket() ?TcpSocket { fn new_tcp_socket() ?TcpSocket {
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.tcp, 0)) ? sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.tcp, 0)) ?
s := TcpSocket{ mut s := TcpSocket{
handle: sockfd handle: sockfd
} }
// s.set_option_bool(.reuse_addr, true)? // s.set_option_bool(.reuse_addr, true)?
@ -271,7 +272,7 @@ fn new_tcp_socket() ?TcpSocket {
} }
fn tcp_socket_from_handle(sockfd int) ?TcpSocket { fn tcp_socket_from_handle(sockfd int) ?TcpSocket {
s := TcpSocket{ mut s := TcpSocket{
handle: sockfd handle: sockfd
} }
// s.set_option_bool(.reuse_addr, true)? // s.set_option_bool(.reuse_addr, true)?
@ -285,7 +286,7 @@ fn tcp_socket_from_handle(sockfd int) ?TcpSocket {
return s return s
} }
pub fn (s TcpSocket) set_option_bool(opt SocketOption, value bool) ? { pub fn (mut s TcpSocket) set_option_bool(opt SocketOption, value bool) ? {
// TODO reenable when this `in` operation works again // TODO reenable when this `in` operation works again
// if opt !in opts_can_set { // if opt !in opts_can_set {
// return err_option_not_settable // return err_option_not_settable
@ -297,16 +298,16 @@ pub fn (s TcpSocket) set_option_bool(opt SocketOption, value bool) ? {
return none return none
} }
pub fn (s TcpSocket) set_option_int(opt SocketOption, value int) ? { pub fn (mut s TcpSocket) set_option_int(opt SocketOption, value int) ? {
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int))) ? socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int))) ?
return none return none
} }
fn (s TcpSocket) close() ? { fn (mut s TcpSocket) close() ? {
return shutdown(s.handle) return shutdown(s.handle)
} }
fn (s TcpSocket) @select(test Select, timeout time.Duration) ?bool { fn (mut s TcpSocket) @select(test Select, timeout time.Duration) ?bool {
return @select(s.handle, test, timeout) return @select(s.handle, test, timeout)
} }
@ -314,7 +315,7 @@ const (
connect_timeout = 5 * time.second connect_timeout = 5 * time.second
) )
fn (s TcpSocket) connect(a string) ? { fn (mut s TcpSocket) connect(a string) ? {
addr := resolve_addr(a, .inet, .tcp) ? addr := resolve_addr(a, .inet, .tcp) ?
res := C.connect(s.handle, &addr.addr, addr.len) res := C.connect(s.handle, &addr.addr, addr.len)
if res == 0 { if res == 0 {
@ -335,7 +336,7 @@ fn (s TcpSocket) connect(a string) ? {
} }
// address gets the address of a socket // address gets the address of a socket
pub fn (s TcpSocket) address() ?Addr { pub fn (s &TcpSocket) address() ?Addr {
mut addr := C.sockaddr_in{} mut addr := C.sockaddr_in{}
size := sizeof(C.sockaddr_in) size := sizeof(C.sockaddr_in)
// cast to the correct type // cast to the correct type

View File

@ -10,7 +10,7 @@ const (
// It will *always* return a line, ending with CRLF, or just '', on EOF. // It will *always* return a line, ending with CRLF, or just '', on EOF.
// NB: if you want more control over the buffer, please use a buffered IO // NB: if you want more control over the buffer, please use a buffered IO
// reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})` // reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})`
pub fn (con TcpConn) read_line() string { pub fn (mut con TcpConn) read_line() string {
mut buf := [max_read]byte{} // where C.recv will store the network data mut buf := [max_read]byte{} // where C.recv will store the network data
mut res := '' // The final result, including the ending \n. mut res := '' // The final result, including the ending \n.
for { for {

View File

@ -6,36 +6,28 @@ const (
server_port = 22334 server_port = 22334
) )
fn setup() (net.TcpListener, net.TcpConn, net.TcpConn) { fn setup() (&net.TcpListener, &net.TcpConn, &net.TcpConn) {
server := net.listen_tcp(server_port) or { mut server := net.listen_tcp(server_port) or { panic(err) }
panic(err) mut client := net.dial_tcp('127.0.0.1:$server_port') or { panic(err) }
} mut socket := server.accept() or { panic(err) }
mut client := net.dial_tcp('127.0.0.1:$server_port') or {
panic(err)
}
mut socket := server.accept() or {
panic(err)
}
$if debug_peer_ip ? { $if debug_peer_ip ? {
ip := con.peer_ip() or { ip := con.peer_ip() or { '$err' }
'$err'
}
eprintln('connection peer_ip: $ip') eprintln('connection peer_ip: $ip')
} }
assert true assert true
return server, client, socket return server, client, socket
} }
fn cleanup(server &net.TcpListener, client &net.TcpConn, socket &net.TcpConn) { fn cleanup(mut server net.TcpListener, mut client net.TcpConn, mut socket net.TcpConn) {
server.close() or { } server.close() or { }
client.close() or { } client.close() or { }
socket.close() or { } socket.close() or { }
} }
fn test_socket() { fn test_socket() {
server, client, socket := setup() mut server, mut client, mut socket := setup()
defer { defer {
cleanup(server, client, socket) cleanup(mut server, mut client, mut socket)
} }
message := 'Hello World' message := 'Hello World'
socket.write_str(message) or { socket.write_str(message) or {
@ -65,14 +57,12 @@ fn test_socket() {
} }
fn test_socket_write_and_read() { fn test_socket_write_and_read() {
server, client, socket := setup() mut server, mut client, mut socket := setup()
defer { defer {
cleanup(server, client, socket) cleanup(mut server, mut client, mut socket)
} }
message1 := 'a message 1' message1 := 'a message 1'
socket.write_str(message1) or { socket.write_str(message1) or { assert false }
assert false
}
mut rbuf := []byte{len: message1.len} mut rbuf := []byte{len: message1.len}
client.read(mut rbuf) client.read(mut rbuf)
line := rbuf.bytestr() line := rbuf.bytestr()
@ -80,18 +70,16 @@ fn test_socket_write_and_read() {
} }
fn test_socket_read_line() { fn test_socket_read_line() {
server, client, socket := setup() mut server, mut client, mut socket := setup()
mut reader := io.new_buffered_reader({ mut reader := io.new_buffered_reader(
reader: io.make_reader(client) reader: io.make_reader(client)
}) )
defer { defer {
cleanup(server, client, socket) cleanup(mut server, mut client, mut socket)
} }
message1, message2 := 'message1', 'message2' message1, message2 := 'message1', 'message2'
message := '$message1\n$message2\n' message := '$message1\n$message2\n'
socket.write_str(message) or { socket.write_str(message) or { assert false }
assert false
}
assert true assert true
// //
line1 := reader.read_line() or { line1 := reader.read_line() or {
@ -109,9 +97,9 @@ fn test_socket_read_line() {
} }
fn test_socket_write_fail_without_panic() { fn test_socket_write_fail_without_panic() {
server, client, socket := setup() mut server, mut client, mut socket := setup()
defer { defer {
cleanup(server, client, socket) cleanup(mut server, mut client, mut socket)
} }
message2 := 'a message 2' message2 := 'a message 2'
// ensure that socket.write (i.e. done on the server side) // ensure that socket.write (i.e. done on the server side)
@ -131,12 +119,12 @@ fn test_socket_write_fail_without_panic() {
} }
fn test_socket_read_line_long_line_without_eol() { fn test_socket_read_line_long_line_without_eol() {
server, client, socket := setup() mut server, mut client, mut socket := setup()
mut reader := io.new_buffered_reader({ mut reader := io.new_buffered_reader(
reader: io.make_reader(client) reader: io.make_reader(client)
}) )
defer { defer {
cleanup(server, client, socket) cleanup(mut server, mut client, mut socket)
} }
message := strings.repeat_string('123', 400) message := strings.repeat_string('123', 400)
socket.write_str(message) socket.write_str(message)

View File

@ -4,8 +4,7 @@ const (
test_port = 45123 test_port = 45123
) )
fn handle_conn(_c net.TcpConn) { fn handle_conn(mut c net.TcpConn) {
mut c := _c
for { for {
mut buf := []byte{len: 100, init: 0} mut buf := []byte{len: 100, init: 0}
read := c.read(mut buf) or { read := c.read(mut buf) or {
@ -19,25 +18,23 @@ fn handle_conn(_c net.TcpConn) {
} }
} }
fn echo_server(l net.TcpListener) ? { fn echo_server(mut l net.TcpListener) ? {
for { for {
new_conn := l.accept() or { mut new_conn := l.accept() or { continue }
continue go handle_conn(mut new_conn)
}
go handle_conn(new_conn)
} }
return none return none
} }
fn echo() ? { fn echo() ? {
mut c := net.dial_tcp('127.0.0.1:$test_port')? mut c := net.dial_tcp('127.0.0.1:$test_port') ?
defer { defer {
c.close() or { } c.close() or { }
} }
data := 'Hello from vlib/net!' data := 'Hello from vlib/net!'
c.write_str(data)? c.write_str(data) ?
mut buf := []byte{len: 4096} mut buf := []byte{len: 4096}
read := c.read(mut buf)? read := c.read(mut buf) ?
assert read == data.len assert read == data.len
for i := 0; i < read; i++ { for i := 0; i < read; i++ {
assert buf[i] == data[i] assert buf[i] == data[i]
@ -47,16 +44,8 @@ fn echo() ? {
} }
fn test_tcp() { fn test_tcp() {
l := net.listen_tcp(test_port) or { mut l := net.listen_tcp(test_port) or { panic(err) }
panic(err) go echo_server(mut l)
} echo() or { panic(err) }
go echo_server(l)
echo() or {
panic(err)
}
l.close() or { } l.close() or { }
} }
fn main() {
test_tcp()
}

View File

@ -8,7 +8,8 @@ const (
) )
pub struct UdpConn { pub struct UdpConn {
sock UdpSocket pub mut:
sock UdpSocket
mut: mut:
write_deadline time.Time write_deadline time.Time
read_deadline time.Time read_deadline time.Time
@ -16,7 +17,7 @@ mut:
write_timeout time.Duration write_timeout time.Duration
} }
pub fn dial_udp(laddr string, raddr string) ?UdpConn { pub fn dial_udp(laddr string, raddr string) ?&UdpConn {
// Dont have to do this when its fixed // Dont have to do this when its fixed
// this just allows us to store this `none` optional in a struct // this just allows us to store this `none` optional in a struct
resolve_wrapper := fn (raddr string) ?Addr { resolve_wrapper := fn (raddr string) ?Addr {
@ -30,27 +31,27 @@ pub fn dial_udp(laddr string, raddr string) ?UdpConn {
l: local l: local
r: resolve_wrapper(raddr) r: resolve_wrapper(raddr)
} }
return UdpConn{ return &UdpConn{
sock: sock sock: sock
read_timeout: udp_default_read_timeout read_timeout: udp_default_read_timeout
write_timeout: udp_default_write_timeout write_timeout: udp_default_write_timeout
} }
} }
pub fn (c UdpConn) write_ptr(b byteptr, len int) ? { pub fn (mut c UdpConn) write_ptr(b byteptr, len int) ? {
remote := c.sock.remote() or { return err_no_udp_remote } remote := c.sock.remote() or { return err_no_udp_remote }
return c.write_to_ptr(remote, b, len) return c.write_to_ptr(remote, b, len)
} }
pub fn (c UdpConn) write(buf []byte) ? { pub fn (mut c UdpConn) write(buf []byte) ? {
return c.write_ptr(buf.data, buf.len) return c.write_ptr(buf.data, buf.len)
} }
pub fn (c UdpConn) write_str(s string) ? { pub fn (mut c UdpConn) write_str(s string) ? {
return c.write_ptr(s.str, s.len) return c.write_ptr(s.str, s.len)
} }
pub fn (c UdpConn) write_to_ptr(addr Addr, b byteptr, len int) ? { pub fn (mut c UdpConn) write_to_ptr(addr Addr, b byteptr, len int) ? {
res := C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len) res := C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len)
if res >= 0 { if res >= 0 {
return none return none
@ -66,17 +67,17 @@ pub fn (c UdpConn) write_to_ptr(addr Addr, b byteptr, len int) ? {
} }
// write_to blocks and writes the buf to the remote addr specified // write_to blocks and writes the buf to the remote addr specified
pub fn (c UdpConn) write_to(addr Addr, buf []byte) ? { pub fn (mut c UdpConn) write_to(addr Addr, buf []byte) ? {
return c.write_to_ptr(addr, buf.data, buf.len) return c.write_to_ptr(addr, buf.data, buf.len)
} }
// write_to_string blocks and writes the buf to the remote addr specified // write_to_string blocks and writes the buf to the remote addr specified
pub fn (c UdpConn) write_to_string(addr Addr, s string) ? { pub fn (mut c UdpConn) write_to_string(addr Addr, s string) ? {
return c.write_to_ptr(addr, s.str, s.len) return c.write_to_ptr(addr, s.str, s.len)
} }
// read reads from the socket into buf up to buf.len returning the number of bytes read // read reads from the socket into buf up to buf.len returning the number of bytes read
pub fn (c UdpConn) read(mut buf []byte) ?(int, Addr) { pub fn (mut c UdpConn) read(mut buf []byte) ?(int, Addr) {
mut addr_from := C.sockaddr{} mut addr_from := C.sockaddr{}
len := sizeof(C.sockaddr) len := sizeof(C.sockaddr)
mut res := wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, mut res := wrap_read_result(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from,
@ -100,7 +101,7 @@ pub fn (c UdpConn) read(mut buf []byte) ?(int, Addr) {
return none return none
} }
pub fn (c UdpConn) read_deadline() ?time.Time { pub fn (c &UdpConn) read_deadline() ?time.Time {
if c.read_deadline.unix == 0 { if c.read_deadline.unix == 0 {
return c.read_deadline return c.read_deadline
} }
@ -111,7 +112,7 @@ pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) {
c.read_deadline = deadline c.read_deadline = deadline
} }
pub fn (c UdpConn) write_deadline() ?time.Time { pub fn (c &UdpConn) write_deadline() ?time.Time {
if c.write_deadline.unix == 0 { if c.write_deadline.unix == 0 {
return c.write_deadline return c.write_deadline
} }
@ -122,7 +123,7 @@ pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) {
c.write_deadline = deadline c.write_deadline = deadline
} }
pub fn (c UdpConn) read_timeout() time.Duration { pub fn (c &UdpConn) read_timeout() time.Duration {
return c.read_timeout return c.read_timeout
} }
@ -130,7 +131,7 @@ pub fn (mut c UdpConn) set_read_timeout(t time.Duration) {
c.read_timeout = t c.read_timeout = t
} }
pub fn (c UdpConn) write_timeout() time.Duration { pub fn (c &UdpConn) write_timeout() time.Duration {
return c.write_timeout return c.write_timeout
} }
@ -139,27 +140,27 @@ pub fn (mut c UdpConn) set_write_timeout(t time.Duration) {
} }
[inline] [inline]
pub fn (c UdpConn) wait_for_read() ? { pub fn (mut c UdpConn) wait_for_read() ? {
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
} }
[inline] [inline]
pub fn (c UdpConn) wait_for_write() ? { pub fn (mut c UdpConn) wait_for_write() ? {
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
} }
pub fn (c UdpConn) str() string { pub fn (c &UdpConn) str() string {
// TODO // TODO
return 'UdpConn' return 'UdpConn'
} }
pub fn (c UdpConn) close() ? { pub fn (mut c UdpConn) close() ? {
return c.sock.close() return c.sock.close()
} }
pub fn listen_udp(port int) ?UdpConn { pub fn listen_udp(port int) ?&UdpConn {
s := new_udp_socket(port) ? s := new_udp_socket(port) ?
return UdpConn{ return &UdpConn{
sock: s sock: s
read_timeout: udp_default_read_timeout read_timeout: udp_default_read_timeout
write_timeout: udp_default_write_timeout write_timeout: udp_default_write_timeout
@ -172,9 +173,9 @@ struct UdpSocket {
r ?Addr r ?Addr
} }
fn new_udp_socket(local_port int) ?UdpSocket { fn new_udp_socket(local_port int) ?&UdpSocket {
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.udp, 0)) ? sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.udp, 0)) ?
s := UdpSocket{ mut s := &UdpSocket{
handle: sockfd handle: sockfd
} }
s.set_option_bool(.reuse_addr, true) ? s.set_option_bool(.reuse_addr, true) ?
@ -197,11 +198,11 @@ fn new_udp_socket(local_port int) ?UdpSocket {
return s return s
} }
pub fn (s UdpSocket) remote() ?Addr { pub fn (s &UdpSocket) remote() ?Addr {
return s.r return s.r
} }
pub fn (s UdpSocket) set_option_bool(opt SocketOption, value bool) ? { pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ? {
// TODO reenable when this `in` operation works again // TODO reenable when this `in` operation works again
// if opt !in opts_can_set { // if opt !in opts_can_set {
// return err_option_not_settable // return err_option_not_settable
@ -213,10 +214,10 @@ pub fn (s UdpSocket) set_option_bool(opt SocketOption, value bool) ? {
return none return none
} }
fn (s UdpSocket) close() ? { fn (mut s UdpSocket) close() ? {
return shutdown(s.handle) return shutdown(s.handle)
} }
fn (s UdpSocket) @select(test Select, timeout time.Duration) ?bool { fn (mut s UdpSocket) @select(test Select, timeout time.Duration) ?bool {
return @select(s.handle, test, timeout) return @select(s.handle, test, timeout)
} }

View File

@ -1,12 +1,9 @@
import net import net
fn echo_server(_c net.UdpConn) { fn echo_server(mut c net.UdpConn) {
mut c := _c
for { for {
mut buf := []byte{ len: 100, init: 0 } mut buf := []byte{len: 100, init: 0}
read, addr := c.read(mut buf) or { read, addr := c.read(mut buf) or { continue }
continue
}
c.write_to(addr, buf[..read]) or { c.write_to(addr, buf[..read]) or {
println('Server: connection dropped') println('Server: connection dropped')
@ -16,14 +13,16 @@ fn echo_server(_c net.UdpConn) {
} }
fn echo() ? { fn echo() ? {
mut c := net.dial_udp('127.0.0.1:40003', '127.0.0.1:40001')? mut c := net.dial_udp('127.0.0.1:40003', '127.0.0.1:40001') ?
defer { c.close() or { } } defer {
c.close() or { }
}
data := 'Hello from vlib/net!' data := 'Hello from vlib/net!'
c.write_str(data)? c.write_str(data) ?
mut buf := []byte{ len: 100, init: 0 } mut buf := []byte{len: 100, init: 0}
read, addr := c.read(mut buf)? read, addr := c.read(mut buf) ?
assert read == data.len assert read == data.len
println('Got address $addr') println('Got address $addr')
@ -35,21 +34,21 @@ fn echo() ? {
assert buf[i] == data[i] assert buf[i] == data[i]
} }
println('Got "${buf.bytestr()}"') println('Got "$buf.bytestr()"')
c.close()? c.close() ?
return none return none
} }
fn test_udp() { fn test_udp() {
l := net.listen_udp(40001) or { mut l := net.listen_udp(40001) or {
println(err) println(err)
assert false assert false
panic('') panic('')
} }
go echo_server(l) go echo_server(mut l)
echo() or { echo() or {
println(err) println(err)
assert false assert false

View File

@ -230,7 +230,7 @@ struct SimpleTcpClientConfig {
} }
fn simple_tcp_client(config SimpleTcpClientConfig) ?string { fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
mut client := net.TcpConn{} mut client := &net.TcpConn(0)
mut tries := 0 mut tries := 0
for tries < config.retries { for tries < config.retries {
tries++ tries++

View File

@ -44,10 +44,10 @@ mut:
content_type string = 'text/plain' content_type string = 'text/plain'
status string = '200 OK' status string = '200 OK'
pub: pub:
req http.Request req http.Request
conn net.TcpConn
// TODO Response // TODO Response
pub mut: pub mut:
conn &net.TcpConn
static_files map[string]string static_files map[string]string
static_mime_types map[string]string static_mime_types map[string]string
form map[string]string form map[string]string
@ -123,7 +123,7 @@ pub fn (mut ctx Context) send_response_to_client(mimetype string, res string) bo
defer { defer {
s.free() s.free()
} }
send_string(ctx.conn, s) or { return false } send_string(mut ctx.conn, s) or { return false }
return true return true
} }
@ -152,7 +152,7 @@ pub fn (mut ctx Context) redirect(url string) Result {
return Result{} return Result{}
} }
ctx.done = true ctx.done = true
send_string(ctx.conn, 'HTTP/1.1 302 Found\r\nLocation: $url$ctx.headers\r\n$headers_close') or { send_string(mut ctx.conn, 'HTTP/1.1 302 Found\r\nLocation: $url$ctx.headers\r\n$headers_close') or {
return Result{} return Result{}
} }
return Result{} return Result{}
@ -163,7 +163,7 @@ pub fn (mut ctx Context) not_found() Result {
return Result{} return Result{}
} }
ctx.done = true ctx.done = true
send_string(ctx.conn, http_404) or { } send_string(mut ctx.conn, http_404) or { }
return Result{} return Result{}
} }
@ -242,8 +242,10 @@ pub fn run<T>(port int) {
pub fn run_app<T>(mut app T, port int) { pub fn run_app<T>(mut app T, port int) {
println('Running a Vweb app on http://localhost:$port') println('Running a Vweb app on http://localhost:$port')
l := net.listen_tcp(port) or { panic('failed to listen') } mut l := net.listen_tcp(port) or { panic('failed to listen') }
app.Context = Context{} app.Context = Context{
conn: 0
}
app.init_once() app.init_once()
$for method in T.methods { $for method in T.methods {
$if method.return_type is Result { $if method.return_type is Result {
@ -301,7 +303,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
vals := first_line.split(' ') vals := first_line.split(' ')
if vals.len < 2 { if vals.len < 2 {
println('no vals for http') println('no vals for http')
send_string(conn, http_500) or { } send_string(mut conn, http_500) or { }
return return
} }
mut headers := []string{} mut headers := []string{}
@ -383,7 +385,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
mime_type := app.static_mime_types[static_file_name] mime_type := app.static_mime_types[static_file_name]
if static_file != '' && mime_type != '' { if static_file != '' && mime_type != '' {
data := os.read_file(static_file) or { data := os.read_file(static_file) or {
send_string(conn, http_404) or { } send_string(mut conn, http_404) or { }
return return
} }
app.send_response_to_client(mime_type, data) app.send_response_to_client(mime_type, data)
@ -544,7 +546,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T) {
} }
if action == '' { if action == '' {
// site not found // site not found
send_string(conn, http_404) or { } send_string(mut conn, http_404) or { }
return return
} }
$for method in T.methods { $for method in T.methods {
@ -685,6 +687,6 @@ fn filter(s string) string {
pub type RawHtml = string pub type RawHtml = string
fn send_string(conn net.TcpConn, s string) ? { fn send_string(mut conn net.TcpConn, s string) ? {
conn.write(s.bytes()) ? conn.write(s.bytes()) ?
} }

View File

@ -86,7 +86,7 @@ fn (mut ws Client) shutdown_socket() ? {
} }
// dial_socket connects tcp socket and initializes default configurations // dial_socket connects tcp socket and initializes default configurations
fn (mut ws Client) dial_socket() ?net.TcpConn { fn (mut ws Client) dial_socket() ?&net.TcpConn {
tcp_address := '$ws.uri.hostname:$ws.uri.port' tcp_address := '$ws.uri.hostname:$ws.uri.port'
mut t := net.dial_tcp(tcp_address) ? mut t := net.dial_tcp(tcp_address) ?
optval := int(1) optval := int(1)

View File

@ -30,7 +30,7 @@ pub:
uri Uri // uri of current connection uri Uri // uri of current connection
id string // unique id of client id string // unique id of client
pub mut: pub mut:
conn net.TcpConn // underlying TCP socket connection conn &net.TcpConn // underlying TCP socket connection
nonce_size int = 16 // size of nounce used for masking nonce_size int = 16 // size of nounce used for masking
panic_on_callback bool // set to true of callbacks can panic panic_on_callback bool // set to true of callbacks can panic
state State // current state of connection state State // current state of connection
@ -75,6 +75,7 @@ pub enum OPCode {
pub fn new_client(address string) ?&Client { pub fn new_client(address string) ?&Client {
uri := parse_uri(address) ? uri := parse_uri(address) ?
return &Client{ return &Client{
conn: 0
is_server: false is_server: false
ssl_conn: openssl.new_ssl_conn() ssl_conn: openssl.new_ssl_conn()
is_ssl: address.starts_with('wss') is_ssl: address.starts_with('wss')

View File

@ -9,8 +9,8 @@ import rand
// Server represents a websocket server connection // Server represents a websocket server connection
pub struct Server { pub struct Server {
mut: mut:
logger &log.Log // logger used to log logger &log.Log // logger used to log
ls net.TcpListener // listener used to get incoming connection to socket ls &net.TcpListener // listener used to get incoming connection to socket
accept_client_callbacks []AcceptClientFn // accept client callback functions accept_client_callbacks []AcceptClientFn // accept client callback functions
message_callbacks []MessageEventHandler // new message callback functions message_callbacks []MessageEventHandler // new message callback functions
close_callbacks []CloseEventHandler // close message callback functions close_callbacks []CloseEventHandler // close message callback functions
@ -36,6 +36,7 @@ pub mut:
// new_server instance a new websocket server on provided port and route // new_server instance a new websocket server on provided port and route
pub fn new_server(port int, route string) &Server { pub fn new_server(port int, route string) &Server {
return &Server{ return &Server{
ls: 0
port: port port: port
logger: &log.Log{ logger: &log.Log{
level: .info level: .info