x.net: new net module (#6130)

pull/6188/head
Emily Hudson 2020-08-20 22:01:37 +01:00 committed by GitHub
parent 9b171b76e0
commit b88569c845
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 2056 additions and 0 deletions

View File

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

View File

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

View File

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

122
vlib/x/net/common.v 100644
View File

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

View File

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

View File

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

View File

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

View File

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

336
vlib/x/net/tcp.v 100644
View File

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

View File

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

247
vlib/x/net/udp.v 100644
View File

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

View File

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

24
vlib/x/net/util.v 100644
View File

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