x.net: new net module (#6130)
parent
9b171b76e0
commit
b88569c845
|
@ -0,0 +1,14 @@
|
|||
// Simple raw HTTP head request
|
||||
import x.net as net
|
||||
import time
|
||||
|
||||
// Make a new connection
|
||||
mut conn := net.dial_tcp('google.com:80')?
|
||||
// Simple http HEAD request for a file
|
||||
conn.write_string('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)
|
||||
// Read all the data that is waiting
|
||||
result := conn.read()?
|
||||
// Cast to string and print result
|
||||
println(result.bytestr())
|
|
@ -0,0 +1,95 @@
|
|||
module net
|
||||
|
||||
// Select represents a select operation
|
||||
enum Select {
|
||||
read write except
|
||||
}
|
||||
|
||||
// SocketType are the available sockets
|
||||
pub enum SocketType {
|
||||
udp = C.SOCK_DGRAM
|
||||
tcp = C.SOCK_STREAM
|
||||
}
|
||||
|
||||
// SocketFamily are the available address families
|
||||
pub enum SocketFamily {
|
||||
inet = C. AF_INET
|
||||
}
|
||||
|
||||
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.sendto() int
|
||||
|
||||
fn C.recv() int
|
||||
fn C.recvfrom() int
|
||||
|
||||
fn C.shutdown() int
|
||||
|
||||
fn C.ntohs() int
|
||||
|
||||
fn C.inet_ntop() int
|
||||
|
||||
fn C.getsockname() int
|
||||
|
||||
// defined in builtin
|
||||
// fn C.read() int
|
||||
// fn C.close() int
|
||||
|
||||
fn C.ioctlsocket() int
|
||||
fn C.fcntl() int
|
||||
|
||||
fn C.@select() int
|
||||
fn C.FD_ZERO()
|
||||
fn C.FD_SET()
|
||||
fn C.FD_ISSET() bool
|
||||
|
||||
[typedef]
|
||||
struct C.fd_set {}
|
|
@ -0,0 +1,74 @@
|
|||
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.inet_ntop(SocketFamily.inet, &addr, 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 reccomended 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)
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
module net
|
||||
|
||||
import time
|
||||
|
||||
// Shutdown shutsdown a socket and closes it
|
||||
fn shutdown(handle int) ? {
|
||||
$if windows {
|
||||
C.shutdown(handle, C.SD_BOTH)
|
||||
socket_error(C.closesocket(handle))?
|
||||
} $else {
|
||||
C.shutdown(handle, C.SHUT_RDWR)
|
||||
socket_error(C.close(handle))?
|
||||
}
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
// Select waits for an io operation (specified by parameter `test`) to be available
|
||||
fn @select(handle int, test Select, timeout time.Duration) ?bool {
|
||||
set := C.fd_set{}
|
||||
|
||||
C.FD_ZERO(&set)
|
||||
C.FD_SET(handle, &set)
|
||||
|
||||
seconds := timeout.milliseconds() / 1000
|
||||
microseconds := timeout - (seconds * time.second)
|
||||
|
||||
mut tt := C.timeval{
|
||||
tv_sec: u64(seconds)
|
||||
tv_usec: u64(microseconds)
|
||||
}
|
||||
|
||||
mut timeval_timeout := &tt
|
||||
|
||||
// infinite timeout is signaled by passing null as the timeout to
|
||||
// select
|
||||
if timeout == infinite_timeout {
|
||||
timeval_timeout = &C.timeval(0)
|
||||
}
|
||||
|
||||
match test {
|
||||
.read {
|
||||
socket_error(C.@select(handle+1, &set, C.NULL, C.NULL, timeval_timeout))?
|
||||
}
|
||||
.write {
|
||||
socket_error(C.@select(handle+1, C.NULL, &set, C.NULL, timeval_timeout))?
|
||||
}
|
||||
.except {
|
||||
socket_error(C.@select(handle+1, C.NULL, C.NULL, &set, timeval_timeout))?
|
||||
}
|
||||
}
|
||||
|
||||
return C.FD_ISSET(handle, &set)
|
||||
}
|
||||
|
||||
// wait_for_common wraps the common wait code
|
||||
fn wait_for_common(
|
||||
handle int,
|
||||
deadline time.Time,
|
||||
timeout time.Duration,
|
||||
test Select) ? {
|
||||
if deadline.unix == 0 {
|
||||
// only accept infinite_timeout as a valid
|
||||
// negative timeout - it is handled in @select however
|
||||
if timeout < 0 && timeout != infinite_timeout {
|
||||
return err_timed_out
|
||||
}
|
||||
ready := @select(handle, test, timeout)?
|
||||
if ready {
|
||||
return none
|
||||
}
|
||||
return err_timed_out
|
||||
}
|
||||
// Convert the deadline into a timeout
|
||||
// and use that
|
||||
d_timeout := deadline.unix - time.now().unix
|
||||
if d_timeout < 0 {
|
||||
// deadline is in the past so this has already
|
||||
// timed out
|
||||
return err_timed_out
|
||||
}
|
||||
|
||||
ready := @select(handle, test, d_timeout)?
|
||||
if ready {
|
||||
return none
|
||||
}
|
||||
return err_timed_out
|
||||
}
|
||||
|
||||
// wait_for_write waits for a write io operation to be available
|
||||
fn wait_for_write(
|
||||
handle int,
|
||||
deadline time.Time,
|
||||
timeout time.Duration) ? {
|
||||
return wait_for_common(handle, deadline, timeout, .write)
|
||||
}
|
||||
|
||||
// wait_for_read waits for a read io operation to be available
|
||||
fn wait_for_read(
|
||||
handle int,
|
||||
deadline time.Time,
|
||||
timeout time.Duration) ? {
|
||||
return wait_for_common(handle, deadline, timeout, .read)
|
||||
}
|
||||
|
||||
// no_deadline should be given to functions when no deadline is wanted (i.e. all functions
|
||||
// return instantly)
|
||||
const (
|
||||
no_deadline = time.Time{unix: 0}
|
||||
)
|
||||
|
||||
// no_timeout should be given to functions when no timeout is wanted (i.e. all functions
|
||||
// return instantly)
|
||||
const (
|
||||
no_timeout = time.Duration(0)
|
||||
)
|
||||
|
||||
// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions
|
||||
// only ever return with data)
|
||||
const (
|
||||
infinite_timeout = time.Duration(-1)
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
module net
|
||||
|
||||
// Well defined errors that are returned from socket functions
|
||||
const (
|
||||
errors_base = 0
|
||||
err_new_socket_failed = error_with_code('net: new_socket failed to create socket', errors_base+1)
|
||||
err_option_not_settable = error_with_code('net: set_option_xxx option not settable', errors_base+2)
|
||||
err_option_wrong_type = error_with_code('net: set_option_xxx option wrong type', errors_base+3)
|
||||
err_port_out_of_range = error_with_code('', errors_base+5)
|
||||
err_no_udp_remote = error_with_code('', errors_base+6)
|
||||
err_connect_failed = error_with_code('net: connect failed', errors_base+7)
|
||||
err_connect_timed_out = error_with_code('net: connect timed out', errors_base+8)
|
||||
|
||||
err_timed_out = error_with_code('net: op timed out', errors_base+9)
|
||||
err_timed_out_code = errors_base+9
|
||||
)
|
||||
|
||||
pub fn socket_error(potential_code int) ?int {
|
||||
$if windows {
|
||||
if potential_code < 0 {
|
||||
last_error_int := C.WSAGetLastError()
|
||||
last_error := wsa_error(last_error_int)
|
||||
return error_with_code('net: socket error: ($last_error_int) $last_error', last_error)
|
||||
}
|
||||
}
|
||||
$else {
|
||||
if potential_code < 0 {
|
||||
last_error := error_code()
|
||||
return error_with_code('net: socket error: $last_error', last_error)
|
||||
}
|
||||
}
|
||||
|
||||
return potential_code
|
||||
}
|
||||
|
||||
pub fn wrap_error(error_code int) ? {
|
||||
$if windows {
|
||||
enum_error := wsa_error(error_code)
|
||||
return error_with_code('socket error: $enum_error', error_code)
|
||||
}
|
||||
$else {
|
||||
return error_with_code('net: socket error: $error_code', error_code)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
module net
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
fn error_code() int {
|
||||
return C.errno
|
||||
}
|
||||
|
||||
fn init() {
|
||||
}
|
||||
|
||||
pub const (
|
||||
msg_nosignal = 0x4000
|
||||
)
|
||||
|
||||
const (
|
||||
error_ewouldblock = C.EWOULDBLOCK
|
||||
)
|
||||
|
||||
#flag solaris -lsocket
|
|
@ -0,0 +1,868 @@
|
|||
module net
|
||||
|
||||
// WsaError is all of the socket errors that WSA provides from WSAGetLastError
|
||||
pub enum WsaError {
|
||||
//
|
||||
// MessageId: WSAEINTR
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A blocking operation was interrupted by a call to WSACancelBlockingCall.
|
||||
//
|
||||
wsaeintr = 10004
|
||||
|
||||
//
|
||||
// MessageId: WSAEBADF
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The file handle supplied is not valid.
|
||||
//
|
||||
wsaebadf = 10009
|
||||
|
||||
//
|
||||
// MessageId: WSAEACCES
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An attempt was made to access a socket in a way forbidden by its access permissions.
|
||||
//
|
||||
wsaeacces = 10013
|
||||
|
||||
//
|
||||
// MessageId: WSAEFAULT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The system detected an invalid pointer address in attempting to use a pointer argument in a call.
|
||||
//
|
||||
wsaefault = 10014
|
||||
|
||||
//
|
||||
// MessageId: WSAEINVAL
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid argument was supplied.
|
||||
//
|
||||
wsaeinval = 10022
|
||||
|
||||
//
|
||||
// MessageId: WSAEMFILE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Too many open sockets.
|
||||
//
|
||||
wsaemfile = 10024
|
||||
|
||||
//
|
||||
// MessageId: WSAEWOULDBLOCK
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A non-blocking socket operation could not be completed immediately.
|
||||
//
|
||||
wsaewouldblock = 10035
|
||||
|
||||
//
|
||||
// MessageId: WSAEINPROGRESS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A blocking operation is currently executing.
|
||||
//
|
||||
wsaeinprogress = 10036
|
||||
|
||||
//
|
||||
// MessageId: WSAEALREADY
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An operation was attempted on a non-blocking socket that already had an operation in progress.
|
||||
//
|
||||
wsaealready = 10037
|
||||
|
||||
//
|
||||
// MessageId: WSAENOTSOCK
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An operation was attempted on something that is not a socket.
|
||||
//
|
||||
wsaenotsock = 10038
|
||||
|
||||
//
|
||||
// MessageId: WSAEDESTADDRREQ
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A required address was omitted from an operation on a socket.
|
||||
//
|
||||
wsaedestaddrreq = 10039
|
||||
|
||||
//
|
||||
// MessageId: WSAEMSGSIZE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself.
|
||||
//
|
||||
wsaemsgsize = 10040
|
||||
|
||||
//
|
||||
// MessageId: WSAEPROTOTYPE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A protocol was specified in the socket function call that does not support the semantics of the socket type requested.
|
||||
//
|
||||
wsaeprototype = 10041
|
||||
|
||||
//
|
||||
// MessageId: WSAENOPROTOOPT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call.
|
||||
//
|
||||
wsaenoprotoopt = 10042
|
||||
|
||||
//
|
||||
// MessageId: WSAEPROTONOSUPPORT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The requested protocol has not been configured into the system, or no implementation for it exists.
|
||||
//
|
||||
wsaeprotonosupport = 10043
|
||||
|
||||
//
|
||||
// MessageId: WSAESOCKTNOSUPPORT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The support for the specified socket type does not exist in this address family.
|
||||
//
|
||||
wsaesocktnosupport = 10044
|
||||
|
||||
//
|
||||
// MessageId: WSAEOPNOTSUPP
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The attempted operation is not supported for the type of object referenced.
|
||||
//
|
||||
wsaeopnotsupp = 10045
|
||||
|
||||
//
|
||||
// MessageId: WSAEPFNOSUPPORT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The protocol family has not been configured into the system or no implementation for it exists.
|
||||
//
|
||||
wsaepfnosupport = 10046
|
||||
|
||||
//
|
||||
// MessageId: WSAEAFNOSUPPORT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An address incompatible with the requested protocol was used.
|
||||
//
|
||||
wsaeafnosupport = 10047
|
||||
|
||||
//
|
||||
// MessageId: WSAEADDRINUSE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Only one usage of each socket address (protocol/network address/port) is normally permitted.
|
||||
//
|
||||
wsaeaddrinuse = 10048
|
||||
|
||||
//
|
||||
// MessageId: WSAEADDRNOTAVAIL
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The requested address is not valid in its context.
|
||||
//
|
||||
wsaeaddrnotavail = 10049
|
||||
|
||||
//
|
||||
// MessageId: WSAENETDOWN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A socket operation encountered a dead network.
|
||||
//
|
||||
wsaenetdown = 10050
|
||||
|
||||
//
|
||||
// MessageId: WSAENETUNREACH
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A socket operation was attempted to an unreachable network.
|
||||
//
|
||||
wsaenetunreach = 10051
|
||||
|
||||
//
|
||||
// MessageId: WSAENETRESET
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress.
|
||||
//
|
||||
wsaenetreset = 10052
|
||||
|
||||
//
|
||||
// MessageId: WSAECONNABORTED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An established connection was aborted by the software in your host machine.
|
||||
//
|
||||
wsaeconnaborted = 10053
|
||||
|
||||
//
|
||||
// MessageId: WSAECONNRESET
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An existing connection was forcibly closed by the remote host.
|
||||
//
|
||||
wsaeconnreset = 10054
|
||||
|
||||
//
|
||||
// MessageId: WSAENOBUFS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.
|
||||
//
|
||||
wsaenobufs = 10055
|
||||
|
||||
//
|
||||
// MessageId: WSAEISCONN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A connect request was made on an already connected socket.
|
||||
//
|
||||
wsaeisconn = 10056
|
||||
|
||||
//
|
||||
// MessageId: WSAENOTCONN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied.
|
||||
//
|
||||
wsaenotconn = 10057
|
||||
|
||||
//
|
||||
// MessageId: WSAESHUTDOWN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call.
|
||||
//
|
||||
wsaeshutdown = 10058
|
||||
|
||||
//
|
||||
// MessageId: WSAETOOMANYREFS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Too many references to some kernel object.
|
||||
//
|
||||
wsaetoomanyrefs = 10059
|
||||
|
||||
//
|
||||
// MessageId: WSAETIMEDOUT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
|
||||
//
|
||||
wsaetimedout = 10060
|
||||
|
||||
//
|
||||
// MessageId: WSAECONNREFUSED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No connection could be made because the target machine actively refused it.
|
||||
//
|
||||
wsaeconnrefused = 10061
|
||||
|
||||
//
|
||||
// MessageId: WSAELOOP
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Cannot translate name.
|
||||
//
|
||||
wsaeloop = 10062
|
||||
|
||||
//
|
||||
// MessageId: WSAENAMETOOLONG
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Name component or name was too long.
|
||||
//
|
||||
wsaenametoolong = 10063
|
||||
|
||||
//
|
||||
// MessageId: WSAEHOSTDOWN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A socket operation failed because the destination host was down.
|
||||
//
|
||||
wsaehostdown = 10064
|
||||
|
||||
//
|
||||
// MessageId: WSAEHOSTUNREACH
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A socket operation was attempted to an unreachable host.
|
||||
//
|
||||
wsaehostunreach = 10065
|
||||
|
||||
//
|
||||
// MessageId: WSAENOTEMPTY
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Cannot remove a directory that is not empty.
|
||||
//
|
||||
wsaenotempty = 10066
|
||||
|
||||
//
|
||||
// MessageId: WSAEPROCLIM
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously.
|
||||
//
|
||||
wsaeproclim = 10067
|
||||
|
||||
//
|
||||
// MessageId: WSAEUSERS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Ran out of quota.
|
||||
//
|
||||
wsaeusers = 10068
|
||||
|
||||
//
|
||||
// MessageId: WSAEDQUOT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Ran out of disk quota.
|
||||
//
|
||||
wsaedquot = 10069
|
||||
|
||||
//
|
||||
// MessageId: WSAESTALE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// File handle reference is no longer available.
|
||||
//
|
||||
wsaestale = 10070
|
||||
|
||||
//
|
||||
// MessageId: WSAEREMOTE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Item is not available locally.
|
||||
//
|
||||
wsaeremote = 10071
|
||||
|
||||
//
|
||||
// MessageId: WSASYSNOTREADY
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable.
|
||||
//
|
||||
wsasysnotready = 10091
|
||||
|
||||
//
|
||||
// MessageId: WSAVERNOTSUPPORTED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The Windows Sockets version requested is not supported.
|
||||
//
|
||||
wsavernotsupported = 10092
|
||||
|
||||
//
|
||||
// MessageId: WSANOTINITIALISED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Either the application has not called WSAStartup, or WSAStartup failed.
|
||||
//
|
||||
wsanotinitialised = 10093
|
||||
|
||||
//
|
||||
// MessageId: WSAEDISCON
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence.
|
||||
//
|
||||
wsaediscon = 10101
|
||||
|
||||
//
|
||||
// MessageId: WSAENOMORE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No more results can be returned by WSALookupServiceNext.
|
||||
//
|
||||
wsaenomore = 10102
|
||||
|
||||
//
|
||||
// MessageId: WSAECANCELLED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled.
|
||||
//
|
||||
wsaecancelled = 10103
|
||||
|
||||
//
|
||||
// MessageId: WSAEINVALIDPROCTABLE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The procedure call table is invalid.
|
||||
//
|
||||
wsaeinvalidproctable = 10104
|
||||
|
||||
//
|
||||
// MessageId: WSAEINVALIDPROVIDER
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The requested service provider is invalid.
|
||||
//
|
||||
wsaeinvalidprovider = 10105
|
||||
|
||||
//
|
||||
// MessageId: WSAEPROVIDERFAILEDINIT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The requested service provider could not be loaded or initialized.
|
||||
//
|
||||
wsaeproviderfailedinit = 10106
|
||||
|
||||
//
|
||||
// MessageId: WSASYSCALLFAILURE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A system call has failed.
|
||||
//
|
||||
wsasyscallfailure = 10107
|
||||
|
||||
//
|
||||
// MessageId: WSASERVICE_NOT_FOUND
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No such service is known. The service cannot be found in the specified name space.
|
||||
//
|
||||
wsaservice_not_found = 10108
|
||||
|
||||
//
|
||||
// MessageId: WSATYPE_NOT_FOUND
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The specified class was not found.
|
||||
//
|
||||
wsatype_not_found = 10109
|
||||
|
||||
//
|
||||
// MessageId: WSA_E_NO_MORE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No more results can be returned by WSALookupServiceNext.
|
||||
//
|
||||
wsa_e_no_more = 10110
|
||||
|
||||
//
|
||||
// MessageId: WSA_E_CANCELLED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled.
|
||||
//
|
||||
wsa_e_cancelled = 10111
|
||||
|
||||
//
|
||||
// MessageId: WSAEREFUSED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A database query failed because it was actively refused.
|
||||
//
|
||||
wsaerefused = 10112
|
||||
|
||||
//
|
||||
// MessageId: WSAHOST_NOT_FOUND
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No such host is known.
|
||||
//
|
||||
wsahost_not_found = 11001
|
||||
|
||||
//
|
||||
// MessageId: WSATRY_AGAIN
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server.
|
||||
//
|
||||
wsatry_again = 11002
|
||||
|
||||
//
|
||||
// MessageId: WSANO_RECOVERY
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A non-recoverable error occurred during a database lookup.
|
||||
//
|
||||
wsano_recovery = 11003
|
||||
|
||||
//
|
||||
// MessageId: WSANO_DATA
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// The requested name is valid, but no data of the requested type was found.
|
||||
//
|
||||
wsano_data = 11004
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_RECEIVERS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// At least one reserve has arrived.
|
||||
//
|
||||
wsa_qos_receivers = 11005
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_SENDERS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// At least one path has arrived.
|
||||
//
|
||||
wsa_qos_senders = 11006
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_NO_SENDERS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// There are no senders.
|
||||
//
|
||||
wsa_qos_no_senders = 11007
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_NO_RECEIVERS
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// There are no receivers.
|
||||
//
|
||||
wsa_qos_no_receivers = 11008
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_REQUEST_CONFIRMED
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Reserve has been confirmed.
|
||||
//
|
||||
wsa_qos_request_confirmed = 11009
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_ADMISSION_FAILURE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Error due to lack of resources.
|
||||
//
|
||||
wsa_qos_admission_failure = 11010
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_POLICY_FAILURE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Rejected for administrative reasons - bad credentials.
|
||||
//
|
||||
wsa_qos_policy_failure = 11011
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_BAD_STYLE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Unknown or conflicting style.
|
||||
//
|
||||
wsa_qos_bad_style = 11012
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_BAD_OBJECT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Problem with some part of the filterspec or providerspecific buffer in general.
|
||||
//
|
||||
wsa_qos_bad_object = 11013
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_TRAFFIC_CTRL_ERROR
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Problem with some part of the flowspec.
|
||||
//
|
||||
wsa_qos_traffic_ctrl_error = 11014
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_GENERIC_ERROR
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// General QOS error.
|
||||
//
|
||||
wsa_qos_generic_error = 11015
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_ESERVICETYPE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid or unrecognized service type was found in the flowspec.
|
||||
//
|
||||
wsa_qos_eservicetype = 11016
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFLOWSPEC
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid or inconsistent flowspec was found in the QOS structure.
|
||||
//
|
||||
wsa_qos_eflowspec = 11017
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EPROVSPECBUF
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Invalid QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_eprovspecbuf = 11018
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFILTERSTYLE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid QOS filter style was used.
|
||||
//
|
||||
wsa_qos_efilterstyle = 11019
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFILTERTYPE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid QOS filter type was used.
|
||||
//
|
||||
wsa_qos_efiltertype = 11020
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFILTERCOUNT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An incorrect number of QOS FILTERSPECs were specified in the FLOWDESCRIPTOR.
|
||||
//
|
||||
wsa_qos_efiltercount = 11021
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EOBJLENGTH
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An object with an invalid ObjectLength field was specified in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_eobjlength = 11022
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFLOWCOUNT
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An incorrect number of flow descriptors was specified in the QOS structure.
|
||||
//
|
||||
wsa_qos_eflowcount = 11023
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EUNKOWNPSOBJ
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An unrecognized object was found in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_eunkownpsobj = 11024
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EPOLICYOBJ
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid policy object was found in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_epolicyobj = 11025
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EFLOWDESC
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid QOS flow descriptor was found in the flow descriptor list.
|
||||
//
|
||||
wsa_qos_eflowdesc = 11026
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EPSFLOWSPEC
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid or inconsistent flowspec was found in the QOS provider specific buffer.
|
||||
//
|
||||
wsa_qos_epsflowspec = 11027
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_EPSFILTERSPEC
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid FILTERSPEC was found in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_epsfilterspec = 11028
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_ESDMODEOBJ
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid shape discard mode object was found in the QOS provider specific buffer.
|
||||
//
|
||||
wsa_qos_esdmodeobj = 11029
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_ESHAPERATEOBJ
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// An invalid shaping rate object was found in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_eshaperateobj = 11030
|
||||
|
||||
//
|
||||
// MessageId: WSA_QOS_RESERVED_PETYPE
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// A reserved policy element was found in the QOS provider-specific buffer.
|
||||
//
|
||||
wsa_qos_reserved_petype = 11031
|
||||
|
||||
//
|
||||
// MessageId: WSA_SECURE_HOST_NOT_FOUND
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// No such host is known securely.
|
||||
//
|
||||
wsa_secure_host_not_found = 11032
|
||||
|
||||
//
|
||||
// MessageId: WSA_IPSEC_NAME_POLICY_ERROR
|
||||
//
|
||||
// MessageText:
|
||||
//
|
||||
// Name based IPSEC policy could not be added.
|
||||
//
|
||||
wsa_ipsec_name_policy_error = 11033
|
||||
}
|
||||
|
||||
// wsa_error casts an int to its WsaError value
|
||||
pub fn wsa_error(code int) WsaError {
|
||||
return WsaError(code)
|
||||
}
|
||||
|
||||
const (
|
||||
error_ewouldblock = WsaError.wsaewouldblock
|
||||
)
|
||||
|
||||
// Link to Winsock library
|
||||
#flag -lws2_32
|
||||
#include <winsock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
|
||||
// Constants that windows needs
|
||||
const (
|
||||
fionbio = C.FIONBIO
|
||||
msg_nosignal = 0
|
||||
wsa_v22 = 0x202 // C.MAKEWORD(2, 2)
|
||||
)
|
||||
|
||||
// Error code returns the last socket error
|
||||
fn error_code() int {
|
||||
return C.WSAGetLastError()
|
||||
}
|
||||
|
||||
struct C.WSAData {
|
||||
mut:
|
||||
wVersion u16
|
||||
wHighVersion u16
|
||||
szDescription [257]byte
|
||||
szSystemStatus [129]byte
|
||||
iMaxSockets u16
|
||||
iMaxUdpDg u16
|
||||
lpVendorInfo byteptr
|
||||
}
|
||||
|
||||
fn init() {
|
||||
mut wsadata := C.WSAData{}
|
||||
res := C.WSAStartup(wsa_v22, &wsadata)
|
||||
if res != 0 {
|
||||
panic('socket: WSAStartup failed')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
module net
|
||||
|
||||
enum SocketOption {
|
||||
// TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it
|
||||
// and there is no easy way to define it
|
||||
|
||||
broadcast = C.SO_BROADCAST
|
||||
debug = C.SO_DEBUG
|
||||
dont_route = C.SO_DONTROUTE
|
||||
error = C.SO_ERROR
|
||||
keep_alive = C.SO_KEEPALIVE
|
||||
linger = C.SO_LINGER
|
||||
oob_inline = C.SO_OOBINLINE
|
||||
|
||||
reuse_addr = C.SO_REUSEADDR
|
||||
|
||||
recieve_buf_size = C.SO_RCVBUF
|
||||
recieve_low_size = C.SO_RCVLOWAT
|
||||
recieve_timeout = C.SO_RCVTIMEO
|
||||
|
||||
send_buf_size = C.SO_SNDBUF
|
||||
send_low_size = C.SO_SNDLOWAT
|
||||
send_timeout = C.SO_SNDTIMEO
|
||||
|
||||
socket_type = C.SO_TYPE
|
||||
}
|
||||
|
||||
const (
|
||||
opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline]
|
||||
opts_int = [
|
||||
.recieve_buf_size,
|
||||
.recieve_low_size,
|
||||
.recieve_timeout,
|
||||
|
||||
.send_buf_size,
|
||||
.send_low_size,
|
||||
.send_timeout,
|
||||
]
|
||||
|
||||
opts_can_set = [
|
||||
SocketOption.broadcast,
|
||||
.debug,
|
||||
.dont_route,
|
||||
.keep_alive,
|
||||
.linger,
|
||||
.oob_inline,
|
||||
.recieve_buf_size,
|
||||
.recieve_low_size,
|
||||
.recieve_timeout,
|
||||
|
||||
.send_buf_size,
|
||||
.send_low_size,
|
||||
.send_timeout,
|
||||
]
|
||||
)
|
|
@ -0,0 +1,336 @@
|
|||
module net
|
||||
|
||||
import time
|
||||
|
||||
pub struct TcpConn {
|
||||
pub:
|
||||
sock TcpSocket
|
||||
|
||||
mut:
|
||||
write_deadline time.Time
|
||||
read_deadline time.Time
|
||||
|
||||
read_timeout time.Duration
|
||||
write_timeout time.Duration
|
||||
}
|
||||
|
||||
pub fn dial_tcp(address string) ?TcpConn {
|
||||
s := new_tcp_socket()?
|
||||
s.connect(address)?
|
||||
|
||||
return TcpConn {
|
||||
sock: s
|
||||
|
||||
read_timeout: -1
|
||||
write_timeout: -1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) close() ? {
|
||||
c.sock.close()?
|
||||
return none
|
||||
}
|
||||
|
||||
// write_ptr blocks and attempts to write all data
|
||||
pub fn (c TcpConn) write_ptr(b byteptr, len int) ? {
|
||||
unsafe {
|
||||
mut ptr_base := byteptr(b)
|
||||
mut total_sent := 0
|
||||
|
||||
for total_sent < len {
|
||||
ptr := ptr_base + total_sent
|
||||
remaining := len - total_sent
|
||||
mut sent := C.send(c.sock.handle, ptr, remaining, msg_nosignal)
|
||||
|
||||
if sent < 0 {
|
||||
code := error_code()
|
||||
match code {
|
||||
error_ewouldblock {
|
||||
c.wait_for_write()
|
||||
continue
|
||||
}
|
||||
else {
|
||||
wrap_error(code)?
|
||||
}
|
||||
}
|
||||
}
|
||||
total_sent += sent
|
||||
}
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
// write blocks and attempts to write all data
|
||||
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) ? {
|
||||
return c.write_ptr(s.str, s.len)
|
||||
}
|
||||
|
||||
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 {
|
||||
buf := []byte { len: 1024 }
|
||||
read := c.read_into(buf)?
|
||||
return buf[..read]
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) read_deadline() ?time.Time {
|
||||
if c.read_deadline.unix == 0 {
|
||||
return c.read_deadline
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c TcpConn) set_read_deadline(deadline time.Time) {
|
||||
c.read_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) write_deadline() ?time.Time {
|
||||
if c.write_deadline.unix == 0 {
|
||||
return c.write_deadline
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c TcpConn) set_write_deadline(deadline time.Time) {
|
||||
c.write_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) read_timeout() time.Duration {
|
||||
return c.read_timeout
|
||||
}
|
||||
|
||||
pub fn(mut c TcpConn) set_read_timeout(t time.Duration) {
|
||||
c.read_timeout = t
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) write_timeout() time.Duration {
|
||||
return c.write_timeout
|
||||
}
|
||||
|
||||
pub fn (mut c TcpConn) set_write_timeout(t time.Duration) {
|
||||
c.write_timeout = t
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (c TcpConn) wait_for_read() ? {
|
||||
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (c TcpConn) wait_for_write() ? {
|
||||
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
|
||||
}
|
||||
|
||||
pub fn (c TcpConn) str() string {
|
||||
// TODO
|
||||
return 'TcpConn'
|
||||
}
|
||||
|
||||
pub struct TcpListener {
|
||||
sock TcpSocket
|
||||
|
||||
mut:
|
||||
accept_timeout time.Duration
|
||||
accept_deadline time.Time
|
||||
}
|
||||
|
||||
pub fn listen_tcp(port int) ?TcpListener {
|
||||
s := new_tcp_socket()?
|
||||
|
||||
validate_port(port)?
|
||||
|
||||
mut addr := C.sockaddr_in{}
|
||||
addr.sin_family = SocketFamily.inet
|
||||
addr.sin_port = C.htons(port)
|
||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
||||
size := sizeof(C.sockaddr_in)
|
||||
|
||||
// cast to the correct type
|
||||
sockaddr := &C.sockaddr(&addr)
|
||||
|
||||
socket_error(C.bind(s.handle, sockaddr, size))?
|
||||
socket_error(C.listen(s.handle, 128))?
|
||||
|
||||
return TcpListener {
|
||||
sock: s
|
||||
accept_timeout: -1
|
||||
accept_deadline: no_deadline
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (l TcpListener) accept() ?TcpConn {
|
||||
addr := C.sockaddr_storage{}
|
||||
unsafe {
|
||||
C.memset(&addr, 0, sizeof(C.sockaddr_storage))
|
||||
}
|
||||
size := sizeof(C.sockaddr_storage)
|
||||
|
||||
// cast to correct type
|
||||
sock_addr := &C.sockaddr(&addr)
|
||||
mut new_handle := C.accept(l.sock.handle, sock_addr, &size)
|
||||
|
||||
if new_handle <= 0 {
|
||||
l.wait_for_accept()?
|
||||
|
||||
new_handle = C.accept(l.sock.handle, sock_addr, &size)
|
||||
|
||||
if new_handle == -1 || new_handle == 0 {
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
new_sock := TcpSocket {
|
||||
handle: new_handle
|
||||
}
|
||||
|
||||
return TcpConn{
|
||||
sock: new_sock
|
||||
read_timeout: -1
|
||||
write_timeout: -1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (c TcpListener) accept_deadline() ?time.Time {
|
||||
if c.accept_deadline.unix != 0 {
|
||||
return c.accept_deadline
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) {
|
||||
c.accept_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c TcpListener) accept_timeout() time.Duration {
|
||||
return c.accept_timeout
|
||||
}
|
||||
|
||||
pub fn(mut c TcpListener) set_accept_timeout(t time.Duration) {
|
||||
c.accept_timeout = t
|
||||
}
|
||||
|
||||
pub fn (c TcpListener) wait_for_accept() ? {
|
||||
return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout)
|
||||
}
|
||||
|
||||
pub fn (c TcpListener) close() ? {
|
||||
c.sock.close()?
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (c TcpListener) address() ?Addr {
|
||||
return c.sock.address()
|
||||
}
|
||||
|
||||
struct TcpSocket {
|
||||
pub:
|
||||
handle int
|
||||
}
|
||||
|
||||
fn new_tcp_socket() ?TcpSocket {
|
||||
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.tcp, 0))?
|
||||
s := TcpSocket {
|
||||
handle: sockfd
|
||||
}
|
||||
s.set_option_bool(.reuse_addr, true)?
|
||||
$if windows {
|
||||
t := true
|
||||
socket_error(C.ioctlsocket(sockfd, fionbio, &t))?
|
||||
} $else {
|
||||
socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s TcpSocket) set_option_bool(opt SocketOption, value bool) ? {
|
||||
// TODO reenable when this `in` operation works again
|
||||
// if opt !in opts_can_set {
|
||||
// return err_option_not_settable
|
||||
// }
|
||||
|
||||
// if opt !in opts_bool {
|
||||
// return err_option_wrong_type
|
||||
// }
|
||||
|
||||
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(bool)))?
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (s TcpSocket) set_option_int(opt SocketOption, value int) ? {
|
||||
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int)))?
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
fn (s TcpSocket) close() ? {
|
||||
return shutdown(s.handle)
|
||||
}
|
||||
|
||||
fn (s TcpSocket) @select(test Select, timeout time.Duration) ?bool {
|
||||
return @select(s.handle, test, timeout)
|
||||
}
|
||||
|
||||
const (
|
||||
connect_timeout = 5 * time.second
|
||||
)
|
||||
|
||||
fn (s TcpSocket) connect(a string) ? {
|
||||
addr := resolve_addr(a, .inet, .tcp)?
|
||||
|
||||
res := C.connect(s.handle, &addr.addr, addr.len)
|
||||
|
||||
if res == 0 {
|
||||
return none
|
||||
}
|
||||
|
||||
errcode := 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
|
||||
}
|
||||
|
||||
return wrap_error(errcode)
|
||||
}
|
||||
|
||||
// address gets the address of a socket
|
||||
pub fn (s TcpSocket) address() ?Addr {
|
||||
mut addr := C.sockaddr_in{}
|
||||
size := sizeof(C.sockaddr_in)
|
||||
// cast to the correct type
|
||||
sockaddr := &C.sockaddr(&addr)
|
||||
C.getsockname(s.handle, sockaddr, &size)
|
||||
return new_addr(sockaddr, '', 0)
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
import x.net as net
|
||||
import time
|
||||
|
||||
fn handle_conn(_c net.TcpConn) {
|
||||
mut c := _c
|
||||
// arbitrary timeouts to ensure that it doesnt
|
||||
// instantly throw its hands in the air and give up
|
||||
c.set_read_timeout(10 * time.second)
|
||||
c.set_write_timeout(10 * time.second)
|
||||
for {
|
||||
buf := []byte{ len: 100, init: 0 }
|
||||
read := c.read_into(mut buf) or {
|
||||
println('Server: connection dropped')
|
||||
return
|
||||
}
|
||||
|
||||
c.write(buf[..read]) or {
|
||||
println('Server: connection dropped')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn echo_server(l net.TcpListener) ? {
|
||||
for {
|
||||
new_conn := l.accept() or { continue }
|
||||
go handle_conn(new_conn)
|
||||
}
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
fn echo() ? {
|
||||
mut c := net.dial_tcp('127.0.0.1:30000')?
|
||||
defer { c.close() or {} }
|
||||
|
||||
// arbitrary timeouts to ensure that it doesnt
|
||||
// instantly throw its hands in the air and give up
|
||||
c.set_read_timeout(10 * time.second)
|
||||
c.set_write_timeout(10 * time.second)
|
||||
|
||||
data := 'Hello from vlib/net!'
|
||||
c.write_string(data)?
|
||||
|
||||
buf := []byte{ len: 100, init: 0 }
|
||||
read := c.read_into(mut buf)?
|
||||
|
||||
assert read == data.len
|
||||
|
||||
for i := 0; i < read; i++ {
|
||||
assert buf[i] == data[i]
|
||||
}
|
||||
|
||||
println('Got "${buf.bytestr()}"')
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
fn test_tcp() {
|
||||
l := net.listen_tcp(30000) or {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go echo_server(l)
|
||||
echo() or {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
l.close() or {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_tcp()
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
module net
|
||||
|
||||
import time
|
||||
|
||||
pub struct UdpConn {
|
||||
sock UdpSocket
|
||||
|
||||
mut:
|
||||
write_deadline time.Time
|
||||
read_deadline time.Time
|
||||
|
||||
read_timeout time.Duration
|
||||
write_timeout time.Duration
|
||||
}
|
||||
|
||||
pub fn dial_udp(laddr, raddr string) ?UdpConn {
|
||||
// Dont have to do this when its fixed
|
||||
// this just allows us to store this `none` optional in a struct
|
||||
resolve_wrapper := fn(raddr string) ?Addr {
|
||||
x := resolve_addr(raddr, .inet, .udp) or { return none }
|
||||
return x
|
||||
}
|
||||
|
||||
local := resolve_addr(laddr, .inet, .udp)?
|
||||
sbase := new_udp_socket(local.port)?
|
||||
|
||||
sock := UdpSocket {
|
||||
handle: sbase.handle
|
||||
|
||||
l: local
|
||||
r: resolve_wrapper(raddr)
|
||||
}
|
||||
|
||||
return UdpConn {
|
||||
sock
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write_ptr(b byteptr, len int) ? {
|
||||
remote := c.sock.remote() or {
|
||||
return err_no_udp_remote
|
||||
}
|
||||
|
||||
return c.write_to_ptr(remote, b, len)
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write(buf []byte) ? {
|
||||
return c.write_ptr(buf.data, buf.len)
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write_string(s string) ? {
|
||||
return c.write_ptr(s.str, s.len)
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write_to_ptr(addr Addr, b byteptr, len int) ? {
|
||||
res := C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len)
|
||||
|
||||
if res >= 0 {
|
||||
return none
|
||||
}
|
||||
|
||||
code := error_code()
|
||||
match code {
|
||||
error_ewouldblock {
|
||||
c.wait_for_write()?
|
||||
socket_error(C.sendto(c.sock.handle, b, len, 0, &addr.addr, addr.len))?
|
||||
}
|
||||
else {
|
||||
wrap_error(code)?
|
||||
}
|
||||
}
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
// write_to blocks and writes the buf to the remote addr specified
|
||||
pub fn (c UdpConn) write_to(addr Addr, buf []byte) ? {
|
||||
return c.write_to_ptr(addr, buf.data, buf.len)
|
||||
}
|
||||
|
||||
// write_to_string blocks and writes the buf to the remote addr specified
|
||||
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) {
|
||||
mut addr_from := C.sockaddr{}
|
||||
len := sizeof(C.sockaddr)
|
||||
|
||||
res := 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)?
|
||||
return res, addr
|
||||
}
|
||||
|
||||
code := error_code()
|
||||
match code {
|
||||
error_ewouldblock {
|
||||
c.wait_for_read()?
|
||||
res2 := socket_error(C.recvfrom(c.sock.handle, buf.data, buf.len, 0, &addr_from, &len))?
|
||||
|
||||
port_from := (&C.sockaddr_in(&addr_from)).sin_port
|
||||
addr := new_addr(addr_from, '', port_from)?
|
||||
return res2, addr
|
||||
}
|
||||
else {
|
||||
wrap_error(code)?
|
||||
}
|
||||
}
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) read() ?([]byte, Addr) {
|
||||
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
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) {
|
||||
c.read_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write_deadline() ?time.Time {
|
||||
if c.write_deadline.unix == 0 {
|
||||
return c.write_deadline
|
||||
}
|
||||
return none
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) {
|
||||
c.write_deadline = deadline
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) read_timeout() time.Duration {
|
||||
return c.read_timeout
|
||||
}
|
||||
|
||||
pub fn(mut c UdpConn) set_read_timeout(t time.Duration) {
|
||||
c.read_timeout = t
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) write_timeout() time.Duration {
|
||||
return c.write_timeout
|
||||
}
|
||||
|
||||
pub fn (mut c UdpConn) set_write_timeout(t time.Duration) {
|
||||
c.write_timeout = t
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (c UdpConn) wait_for_read() ? {
|
||||
return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout)
|
||||
}
|
||||
|
||||
[inline]
|
||||
pub fn (c UdpConn) wait_for_write() ? {
|
||||
return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout)
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) str() string {
|
||||
// TODO
|
||||
return 'UdpConn'
|
||||
}
|
||||
|
||||
pub fn (c UdpConn) close() ? {
|
||||
return c.sock.close()
|
||||
}
|
||||
|
||||
pub fn listen_udp(port int) ?UdpConn {
|
||||
s := new_udp_socket(port)?
|
||||
|
||||
return UdpConn {
|
||||
sock: s
|
||||
}
|
||||
}
|
||||
|
||||
struct UdpSocket {
|
||||
handle int
|
||||
|
||||
l Addr
|
||||
r ?Addr
|
||||
}
|
||||
|
||||
fn new_udp_socket(local_port int) ?UdpSocket {
|
||||
sockfd := socket_error(C.socket(SocketFamily.inet, SocketType.udp, 0))?
|
||||
s := UdpSocket {
|
||||
handle: sockfd
|
||||
}
|
||||
s.set_option_bool(.reuse_addr, true)?
|
||||
$if windows {
|
||||
t := true
|
||||
socket_error(C.ioctlsocket(sockfd, fionbio, &t))?
|
||||
} $else {
|
||||
socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK))
|
||||
}
|
||||
|
||||
// In UDP we always have to bind to a port
|
||||
validate_port(local_port)?
|
||||
|
||||
mut addr := C.sockaddr_in{}
|
||||
addr.sin_family = SocketFamily.inet
|
||||
addr.sin_port = C.htons(local_port)
|
||||
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
|
||||
size := sizeof(C.sockaddr_in)
|
||||
|
||||
// cast to the correct type
|
||||
sockaddr := &C.sockaddr(&addr)
|
||||
|
||||
socket_error(C.bind(s.handle, sockaddr, size))?
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (s UdpSocket) remote() ?Addr {
|
||||
return s.r
|
||||
}
|
||||
|
||||
pub fn (s UdpSocket) set_option_bool(opt SocketOption, value bool) ? {
|
||||
// TODO reenable when this `in` operation works again
|
||||
// if opt !in opts_can_set {
|
||||
// return err_option_not_settable
|
||||
// }
|
||||
// if opt !in opts_bool {
|
||||
// return err_option_wrong_type
|
||||
// }
|
||||
socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(bool)))?
|
||||
return none
|
||||
}
|
||||
|
||||
fn (s UdpSocket) close() ? {
|
||||
return shutdown(s.handle)
|
||||
}
|
||||
|
||||
fn (s UdpSocket) @select(test Select, timeout time.Duration) ?bool {
|
||||
return @select(s.handle, test, timeout)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import x.net as net
|
||||
import time
|
||||
|
||||
fn echo_server(_c net.UdpConn) {
|
||||
mut c := _c
|
||||
// arbitrary timeouts to ensure that it doesnt
|
||||
// instantly throw its hands in the air and give up
|
||||
c.set_read_timeout(10 * time.second)
|
||||
c.set_write_timeout(10 * time.second)
|
||||
for {
|
||||
buf := []byte{ len: 100, init: 0 }
|
||||
read, addr := c.read_into(mut buf) or {
|
||||
continue
|
||||
}
|
||||
|
||||
c.write_to(addr, buf[..read]) or {
|
||||
println('Server: connection dropped')
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn echo() ? {
|
||||
mut c := net.dial_udp('127.0.0.1:40003', '127.0.0.1:40001')?
|
||||
defer { c.close() or { } }
|
||||
|
||||
// arbitrary timeouts to ensure that it doesnt
|
||||
// instantly throw its hands in the air and give up
|
||||
c.set_read_timeout(10 * time.second)
|
||||
c.set_write_timeout(10 * time.second)
|
||||
|
||||
data := 'Hello from vlib/net!'
|
||||
|
||||
c.write_string(data)?
|
||||
|
||||
buf := []byte{ len: 100, init: 0 }
|
||||
read, addr := c.read_into(mut buf)?
|
||||
|
||||
assert read == data.len
|
||||
println('Got address $addr')
|
||||
// Can't test this here because loopback addresses
|
||||
// are mapped to other addresses
|
||||
// assert addr.str() == '127.0.0.1:30001'
|
||||
|
||||
for i := 0; i < read; i++ {
|
||||
assert buf[i] == data[i]
|
||||
}
|
||||
|
||||
println('Got "${buf.bytestr()}"')
|
||||
|
||||
c.close()?
|
||||
|
||||
return none
|
||||
}
|
||||
|
||||
fn test_udp() {
|
||||
l := net.listen_udp(40001) or {
|
||||
println(err)
|
||||
assert false
|
||||
panic('')
|
||||
}
|
||||
|
||||
go echo_server(l)
|
||||
echo() or {
|
||||
println(err)
|
||||
assert false
|
||||
}
|
||||
|
||||
l.close() or { }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_udp()
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
module net
|
||||
|
||||
const (
|
||||
socket_max_port = u16(0xFFFF)
|
||||
)
|
||||
|
||||
// validate_port checks whether a port is valid
|
||||
// and returns the port or an error
|
||||
pub fn validate_port(port int) ?u16 {
|
||||
if port <= socket_max_port {
|
||||
return u16(port)
|
||||
} else {
|
||||
return err_port_out_of_range
|
||||
}
|
||||
}
|
||||
|
||||
// split address splits an address into its host name and its port
|
||||
pub fn split_address(addr string) ?(string, u16) {
|
||||
port := addr.all_after_last(':').int()
|
||||
address := addr.all_before_last(':')
|
||||
|
||||
p := validate_port(port)?
|
||||
return address, p
|
||||
}
|
Loading…
Reference in New Issue