Hi Michael,
- extern proc sys_freeaddrinfo(res:sys_addrinfo_ptr_t);
+ extern proc sys_freeaddrinfo(inout res:sys_addrinfo_ptr_t);
I don't think the inout belongs there, since sys_freeaddrinfo takes in
a pointer (not pointer to pointer),
and you didn't change the prototype in sys.h other than to remove an _...
The sys_freeaddrinfo implementation that was already there took a
sys_addrinfo_ptr_t* and set the referent to NULL. I assume the reason is
to keep dangling pointers from leaking into Chapel code. I decided to
make the Sys.chpl prototype match the sys.c implementation instead of
vice versa.
In qio.c
- if( src_addr_out ) src_addr_out->len = msg.msg_namelen;
Why are you removing the setting of the length here? Is there
some other way to get msg.msg_namelen out, or is it irrelevant for
some reason?
There are also changes like this around line 1064 in sys.c, and also in
getaddrinfo_addr. My understanding is that this length could be
arbitrarily
long (for filename-ish sockets, like named pipes). I don't have any
issue
with moving around a fixed-size struct and passing the length of that
into the system calls... but shouldn't we keeping the length field too?
Can you explain why the length is never needed?
Oh, yes, I forgot to consider storage for named pipe addresses. They
complicate things a little. What we might opt for instead is some kind
of an intermediary structure on the user-facing side that fits in
sys_sockaddr_t and stores a pointer to a C string. Then in sys.c we can
convert between that and the relevant structures for named paths,
performing all the storage-related stuff in sys.c functions. Then we'd
only use the length internally if we know we're dealing with a path
address, and convert it back into the intermediary string pointer type
on its way back to Chapel space. Otherwise I think we should assume
sys_sockaddr_t has enough room to store any other addresses directly.
In httpd.chpl, is this really an HTTP server, or just something that
listens on port 80?
(ie, does it do anything with the HTTP protocol?) If it's just a port
80 sockets demo,
I'd prefer to call it something else...
That was just the file name I was using, and never changed it. :) A
better name might be echo_server.chpl.
It looks like you didn't provide the Chapel module code for listen
and the socket record type. But, the example looks nice... although
most of the other I/O stuff uses a return-in-arguments style rather
than a return-in-tuple style.
Oh, you're right! I forgot to svn add the actual module code. I suppose
I should review my own patch files more carefully. :) I attached a new
patch which contains Net.chpl as well.
-- Brandon
Index: compiler/passes/reservedSymbolNames
===================================================================
--- compiler/passes/reservedSymbolNames (revision 22391)
+++ compiler/passes/reservedSymbolNames (working copy)
@@ -246,3 +246,15 @@
ten23
ts
two
+
+ // symbols from sys/socket.h
+ accept
+ accept4
+ bind
+ connect
+ listen
+ recv
+ recvfrom
+ socket
+ send
+ sendto
Index: modules/standard/IO.chpl
===================================================================
--- modules/standard/IO.chpl (revision 22391)
+++ modules/standard/IO.chpl (working copy)
@@ -126,6 +126,7 @@
extern const QIO_HINT_PARALLEL:c_int;
extern const QIO_HINT_DIRECT:c_int;
extern const QIO_HINT_NOREUSE:c_int;
+extern const QIO_HINT_OWNED:c_int;
/** NONE means normal operation, nothing special
to hint. Expect to use NONE most of the time.
Index: modules/standard/Sys.chpl
===================================================================
--- modules/standard/Sys.chpl (revision 22391)
+++ modules/standard/Sys.chpl (working copy)
@@ -66,7 +66,16 @@
extern const AF_ATMPVC:c_int;
extern const AF_APPLETALK:c_int;
extern const AF_PACKET:c_int;
+ extern const AF_UNSPEC:c_int;
+ // addrinfo flags
+ extern const AI_PASSIVE:c_int;
+ extern const AI_CANONNAME:c_int;
+ extern const AI_NUMERICHOST:c_int;
+ extern const AI_V4MAPPED:c_int;
+ extern const AI_ALL:c_int;
+ extern const AI_ADDRCONFIG:c_int;
+
// socket types
extern const SOCK_STREAM:c_int;
extern const SOCK_DGRAM:c_int;
@@ -107,6 +116,11 @@
extern const IPPROTO_TCP:c_int;
extern const IPPROTO_UDP:c_int;
+ // general socket options
+ extern const SO_REUSEADDR:c_int;
+ extern const SO_BINDTODEVICE:c_int;
+ extern const SO_BROADCAST:c_int;
+
// IP socket options
extern const IP_ADD_MEMBERSHIP:c_int;
extern const IP_DROP_MEMBERSHIP:c_int;
@@ -168,14 +182,7 @@
/* SOCKET STRUCTURE TYPES */
- extern type sys_sockaddr_storage_t;
- extern record sys_sockaddr_t {
- var addr:sys_sockaddr_storage_t;
- var len:socklen_t;
- proc sys_sockaddr_t() {
- sys_init_sys_sockaddr_t(this);
- }
- }
+ extern type sys_sockaddr_t;
extern record sys_addrinfo_t {
var ai_flags: c_int;
@@ -194,6 +201,9 @@
proc sys_addrinfo_ptr_t.canonname:string { return
sys_getaddrinfo_cannonname(this); }
proc sys_addrinfo_ptr_t.next:sys_addrinfo_ptr_t { return
sys_getaddrinfo_next(this); }
+ proc sys_sockaddr_t.family:c_int { return sys_sockaddr_family(this); }
+ proc sys_sockaddr_t.port:c_int { return sys_sockaddr_port(this); }
+
extern proc sys_init_sys_sockaddr(inout addr:sys_sockaddr_t);
extern proc sys_strerror(error:err_t, inout string_out:string):err_t;
@@ -217,6 +227,7 @@
extern proc sys_accept(sockfd:fd_t, inout add_out:sys_sockaddr_t, inout
fd_out:fd_t):err_t;
extern proc sys_bind(sockfd:fd_t, inout addr:sys_sockaddr_t):err_t;
extern proc sys_connect(sockfd:fd_t, inout addr:sys_sockaddr_t):err_t;
+
extern proc sys_getaddrinfo(node:string, service:string, inout
hints:sys_addrinfo_t, inout res_out:sys_addrinfo_ptr_t):err_t;
extern proc sys_getaddrinfo_flags(res:sys_addrinfo_ptr_t):c_int;
extern proc sys_getaddrinfo_family(res:sys_addrinfo_ptr_t):c_int;
@@ -224,8 +235,11 @@
extern proc sys_getaddrinfo_protocol(res:sys_addrinfo_ptr_t):c_int;
extern proc sys_getaddrinfo_addr(res:sys_addrinfo_ptr_t):sys_sockaddr_t;
extern proc sys_getaddrinfo_next(res:sys_addrinfo_ptr_t):sys_addrinfo_ptr_t;
- extern proc sys_freeaddrinfo(res:sys_addrinfo_ptr_t);
+ extern proc sys_freeaddrinfo(inout res:sys_addrinfo_ptr_t);
+ extern proc sys_sockaddr_family(sa:sys_sockaddr_t):c_int;
+ extern proc sys_sockaddr_port(sa:sys_sockaddr_t):c_int;
+
extern proc sys_getnameinfo(inout addr:sys_sockaddr_t, inout
host_out:string, inout serv_out:string, flags:c_int):err_t;
extern proc sys_getpeername(sockfd:fd_t, inout addr:sys_sockaddr_t):err_t;
extern proc sys_getsockname(sockfd:fd_t, inout addr:sys_sockaddr_t):err_t;
@@ -233,13 +247,16 @@
// TODO -- these should be generic, assuming caller knows what they
// are doing.
extern proc sys_getsockopt(sockfd:fd_t, level:c_int, optname:c_int,
optval:c_void_ptr, inout optlen:socklen_t):err_t;
- extern proc sys_setsockopt(sockfd:fd_t, level:c_int, optname:c_int,
optval:c_void_ptr, optlen:socklen_t):err_t;
+ extern proc sys_setsockopt(sockfd:fd_t, level:c_int, optname:c_int, ref
optval:?, optlen:socklen_t):err_t;
extern proc sys_listen(sockfd:fd_t, backlog:c_int):err_t;
extern proc sys_shutdown(sockfd:fd_t, how:c_int):err_t;
extern proc sys_socket(_domain:c_int, _type:c_int, protocol:c_int, inout
sockfd_out:fd_t):err_t;
extern proc sys_socketpair(_domain:c_int, _type:c_int, protocol:c_int, inout
sockfd_out_a:fd_t, inout sockfd_out_b:fd_t):err_t;
+ // arpa/inet.h
+ extern proc sys_inet_ntop(family:c_int, ref addr:sys_sockaddr_t, ref
str:string):err_t;
+
// recv, recvfrom, recvmsg, send, sendto, sendmsg are in io
}
Index: runtime/include/qio/sys.h
===================================================================
--- runtime/include/qio/sys.h (revision 22391)
+++ runtime/include/qio/sys.h (working copy)
@@ -17,6 +17,7 @@
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
+#include <arpa/inet.h>
typedef int fd_t;
@@ -25,15 +26,8 @@
#define HAS_GETADDRINFO
#endif
+typedef struct sockaddr_storage sys_sockaddr_t;
-typedef struct sockaddr_storage sys_sockaddr_storage_t;
-
-
-typedef struct sys_sockaddr_s {
- sys_sockaddr_storage_t addr;
- socklen_t len;
-} sys_sockaddr_t;
-
#ifdef HAS_GETADDRINFO
typedef struct addrinfo sys_addrinfo_t;
typedef struct addrinfo* sys_addrinfo_ptr_t;
@@ -170,12 +164,16 @@
sys_sockaddr_t sys_getaddrinfo_addr(sys_addrinfo_ptr_t a);
sys_addrinfo_ptr_t sys_getaddrinfo_next(sys_addrinfo_ptr_t a);
-void sys_freeaddr_info(sys_addrinfo_ptr_t* p);
+void sys_freeaddrinfo(sys_addrinfo_ptr_t* p);
+sys_addrinfo_t sys_init_addrinfo(void);
err_t sys_getnameinfo(const sys_sockaddr_t* addr, char** host_out, char**
serv_out, int flags);
#endif
+int sys_sockaddr_family(sys_sockaddr_t addr);
+int sys_sockaddr_port(sys_sockaddr_t addr);
+
err_t sys_getpeername(fd_t sockfd, sys_sockaddr_t* addr);
@@ -222,6 +220,9 @@
// Allocates a string to store the current directory which must be freed.
err_t sys_getcwd(const char** path_out);
+// arpa/inet.h
+err_t sys_inet_ntop(int family, sys_sockaddr_t *addr, const char **str_out);
+
#ifdef __cplusplus
} // end extern "C"
#endif
Index: runtime/src/qio/qio.c
===================================================================
--- runtime/src/qio/qio.c (revision 22391)
+++ runtime/src/qio/qio.c (working copy)
@@ -402,8 +402,8 @@
memset(&msg, 0, sizeof(struct msghdr));
if( src_addr_out ) {
- msg.msg_name = (void*) &src_addr_out->addr;
- msg.msg_namelen = src_addr_out->len;
+ msg.msg_name = (void*) src_addr_out;
+ msg.msg_namelen = sizeof(sys_sockaddr_t);
}
msg.msg_iov = iov;
msg.msg_iovlen = num_parts;
@@ -414,7 +414,6 @@
err = sys_recvmsg(sockfd, &msg, flags, &nrecvd);
if( ! err ) {
- if( src_addr_out ) src_addr_out->len = msg.msg_namelen;
if( ancillary_out && ancillary_len_inout ) *ancillary_len_inout =
msg.msg_controllen;
}
@@ -461,8 +460,8 @@
memset(&msg, 0, sizeof(struct msghdr));
if( dst_addr ) {
- msg.msg_name = (void*) &dst_addr->addr;
- msg.msg_namelen = dst_addr->len;
+ msg.msg_name = (void*) dst_addr;
+ msg.msg_namelen = sizeof(sys_sockaddr_t);
}
msg.msg_iov = iov;
msg.msg_iovlen = num_parts;
Index: runtime/src/qio/sys.c
===================================================================
--- runtime/src/qio/sys.c (revision 22391)
+++ runtime/src/qio/sys.c (working copy)
@@ -42,7 +42,6 @@
void sys_init_sys_sockaddr(sys_sockaddr_t* addr)
{
memset(addr, 0, sizeof(sys_sockaddr_t));
- addr->len = sizeof(sys_sockaddr_storage_t);
}
// ------------------- system call wrappers -----------------------------
@@ -1064,16 +1063,15 @@
{
int got;
err_t err_out;
- socklen_t addr_len = sizeof(sys_sockaddr_storage_t);
+ socklen_t addr_len = sizeof(sys_sockaddr_t);
STARTING_SLOW_SYSCALL;
- got = accept(sockfd, (struct sockaddr*) & addr_out->addr, &addr_len);
+ got = accept(sockfd, (struct sockaddr*) addr_out, &addr_len);
if( got != -1 ) {
- if( addr_len > sizeof(sys_sockaddr_storage_t) ) {
+ if( addr_len > sizeof(sys_sockaddr_t) ) {
fprintf(stderr, "Warning: address truncated in sys_accept\n");
}
- addr_out->len = addr_len;
err_out = 0;
*fd_out = got;
} else {
@@ -1091,11 +1089,7 @@
int got;
err_t err_out;
- if( addr->len == 0 ) {
- return EINVAL;
- }
-
- got = bind(sockfd, (const struct sockaddr*) & addr->addr, addr->len);
+ got = bind(sockfd, (struct sockaddr*) addr, sizeof(sys_sockaddr_t));
if( got != -1 ) {
err_out = 0;
} else {
@@ -1112,7 +1106,7 @@
STARTING_SLOW_SYSCALL;
- got = connect(sockfd, (const struct sockaddr*) & addr->addr, addr->len);
+ got = connect(sockfd, (struct sockaddr*) addr, sizeof(sys_sockaddr_t));
if( got != -1 ) {
err_out = 0;
} else {
@@ -1163,14 +1157,13 @@
int sys_getaddrinfo_socktype(sys_addrinfo_ptr_t a) {return a->ai_socktype;}
int sys_getaddrinfo_protocol(sys_addrinfo_ptr_t a) {return a->ai_protocol;}
sys_sockaddr_t sys_getaddrinfo_addr(sys_addrinfo_ptr_t a) {
- sys_sockaddr_t ret;
- memcpy(&ret.addr, a->ai_addr, a->ai_addrlen);
- ret.len = a->ai_addrlen;
+ sys_sockaddr_t ret = {0};
+ memcpy(&ret, a->ai_addr, a->ai_addrlen);
return ret;
}
sys_addrinfo_ptr_t sys_getaddrinfo_next(sys_addrinfo_ptr_t a) {return
a->ai_next;}
-void sys_freeaddr_info(sys_addrinfo_ptr_t *p)
+void sys_freeaddrinfo(sys_addrinfo_ptr_t *p)
{
freeaddrinfo(*p);
*p = NULL;
@@ -1201,7 +1194,7 @@
host_buf = new_host_buf;
serv_buf = new_serv_buf;
- got = getnameinfo((const struct sockaddr*) & addr->addr, addr->len,
+ got = getnameinfo((struct sockaddr*) addr, sizeof(sys_sockaddr_t),
host_buf, host_buf_sz,
serv_buf, serv_buf_sz,
flags);
@@ -1235,12 +1228,26 @@
#endif
+int sys_sockaddr_family(sys_sockaddr_t addr)
+{
+ return addr.ss_family;
+}
+
+int sys_sockaddr_port(sys_sockaddr_t addr)
+{
+ switch (addr.ss_family) {
+ case AF_INET: return ntohs(((struct sockaddr_in*) &addr)->sin_port);
+ case AF_INET6: return ntohs(((struct sockaddr_in6*) &addr)->sin6_port);
+ default: return 0;
+ }
+}
+
err_t sys_getpeername(fd_t sockfd, sys_sockaddr_t* addr)
{
- int got;
+ int got, addr_len = sizeof(sys_sockaddr_t);
err_t err_out;
- got = getpeername(sockfd, (struct sockaddr*) & addr->addr, & addr->len);
+ got = getpeername(sockfd, (struct sockaddr*) addr, & addr_len);
if( got != -1 ) {
err_out = 0;
} else {
@@ -1252,10 +1259,10 @@
err_t sys_getsockname(fd_t sockfd, sys_sockaddr_t* addr)
{
- int got;
+ int got, addr_len = sizeof(sys_sockaddr_t);
err_t err_out;
- got = getsockname(sockfd, (struct sockaddr*) & addr->addr, & addr->len);
+ got = getsockname(sockfd, (struct sockaddr*) addr, & addr_len);
if( got != -1 ) {
err_out = 0;
} else {
@@ -1333,11 +1340,11 @@
err_t sys_recvfrom(fd_t sockfd, void* buf, size_t len, int flags,
sys_sockaddr_t* src_addr_out, ssize_t* num_recvd_out)
{
- ssize_t got;
+ ssize_t got, addr_len = sizeof(sys_sockaddr_t);
err_t err_out;
STARTING_SLOW_SYSCALL;
- got = recvfrom(sockfd, buf, len, flags, (struct sockaddr*)
&src_addr_out->addr, & src_addr_out->len);
+ got = recvfrom(sockfd, buf, len, flags, (struct sockaddr*) src_addr_out, &
addr_len);
if( got != -1 ) {
*num_recvd_out = got;
err_out = 0;
@@ -1397,7 +1404,7 @@
err_t err_out;
STARTING_SLOW_SYSCALL;
- sent = sendto(sockfd, buf, len, flags, (const struct sockaddr*)
&dest_addr->addr, dest_addr->len);
+ sent = sendto(sockfd, buf, len, flags, (struct sockaddr*) dest_addr,
sizeof(sys_sockaddr_t));
if( sent != -1 ) {
*num_sent_out = sent;
err_out = 0;
@@ -1530,3 +1537,20 @@
*path_out = buf;
return err;
}
+
+err_t sys_inet_ntop(int family, sys_sockaddr_t *addr, const char **str_out)
+{
+ void *addr_ptr;
+
+ switch (family) {
+ case AF_INET : addr_ptr = &((struct sockaddr_in *) addr)->sin_addr;
break;
+ case AF_INET6: addr_ptr = &((struct sockaddr_in6 *) addr)->sin6_addr;
break;
+ default: return EAFNOSUPPORT;
+ }
+
+ char *buf = qio_malloc(INET6_ADDRSTRLEN);
+ if (!buf) return ENOMEM;
+ char *ret = inet_ntop(family, addr_ptr, buf, INET6_ADDRSTRLEN);
+ if (ret) *str_out = ret;
+ return errno;
+}
Index: test/net/bwross/url_parser.chpl
===================================================================
--- test/net/bwross/url_parser.chpl (revision 0)
+++ test/net/bwross/url_parser.chpl (working copy)
@@ -0,0 +1,43 @@
+use Net;
+
+proc tryUri(s:string) {
+ var u = new URI(s);
+ writeln(s," => ",new URI(s));
+ if u.scheme != "" then writeln(" scheme = ", u.scheme);
+ if u.user != "" then writeln(" user = ", u.user);
+ if u.pass != "" then writeln(" pass = ", u.pass);
+ if u.host != "" then writeln(" host = ", u.host);
+ if u.port != "" then writeln(" port = ", u.port);
+ if u.path != "" then writeln(" path = ", u.path);
+ if u.query != "" then writeln(" query = ", u.query);
+ if u.fragment!="" then writeln(" frag = ", u.fragment);
+}
+
+tryUri("http://chapel.cray.com/");
+tryUri("http://chapel.cray.com/some/page/index.html");
+tryUri("http://chapel.cray.com:8080/");
+tryUri("ftp://[feed:2::beef]:123/");
+tryUri("/some/path/");
+tryUri("http://example.com/?query=something");
+tryUri("http://example.com/#fragment-is-something");
+tryUri("http://example.com/?query#fragment");
+tryUri("http://example.com/#fragment-not?query");
+tryUri("//lan-host/thing");
+tryUri("mailto:[email protected]");
+tryUri("file:///some/path");
+tryUri("file:/some/path");
+tryUri("file://notdomain/path");
+tryUri("http://domain/path");
+tryUri("./relative/path");
+tryUri("http://[email protected]/");
+tryUri("http://user:[email protected]/");
+tryUri("http://user:pass:with:[email protected]/");
+tryUri("//user:pass@lan-host:456/");
+tryUri("proto:");
+tryUri("tcp://:80");
+tryUri("http://nopath.com");
+tryUri("http://yespath.com/");
+tryUri("http://multipath.com////////////////////");
+tryUri("http://coloninpath.com/C:/WINDOWS/");
+tryUri("//coloninpath.com/C:/WINDOWS/");
+tryUri("/C:/WINDOWS/");
Index: url_parser.good
===================================================================
--- url_parser.good (revision 0)
+++ url_parser.good (working copy)
@@ -0,0 +1,110 @@
+http://chapel.cray.com/ => http://chapel.cray.com/
+ scheme = http
+ host = chapel.cray.com
+ path = /
+http://chapel.cray.com/some/page/index.html =>
http://chapel.cray.com/some/page/index.html
+ scheme = http
+ host = chapel.cray.com
+ path = /some/page/index.html
+http://chapel.cray.com:8080/ => http://chapel.cray.com:8080/
+ scheme = http
+ host = chapel.cray.com
+ port = 8080
+ path = /
+ftp://[feed:2::beef]:123/ => ftp://[feed:2::beef]:123/
+ scheme = ftp
+ host = feed:2::beef
+ port = 123
+ path = /
+/some/path/ => /some/path/
+ path = /some/path/
+http://example.com/?query=something => http://example.com/?query=something
+ scheme = http
+ host = example.com
+ path = /
+ query = query=something
+http://example.com/#fragment-is-something =>
http://example.com/#fragment-is-something
+ scheme = http
+ host = example.com
+ path = /
+ frag = fragment-is-something
+http://example.com/?query#fragment => http://example.com/?query#fragment
+ scheme = http
+ host = example.com
+ path = /
+ query = query
+ frag = fragment
+http://example.com/#fragment-not?query =>
http://example.com/#fragment-not?query
+ scheme = http
+ host = example.com
+ path = /
+ frag = fragment-not?query
+//lan-host/thing => //lan-host/thing
+ host = lan-host
+ path = /thing
+mailto:[email protected] => mailto:[email protected]
+ scheme = mailto
+ path = [email protected]
+file:///some/path => file:///some/path
+ scheme = file
+ path = ///some/path
+file:/some/path => file:/some/path
+ scheme = file
+ path = /some/path
+file://notdomain/path => file://notdomain/path
+ scheme = file
+ path = //notdomain/path
+http://domain/path => http://domain/path
+ scheme = http
+ host = domain
+ path = /path
+./relative/path => ./relative/path
+ path = ./relative/path
+http://[email protected]/ => http://[email protected]/
+ scheme = http
+ user = user
+ host = ftp.example.com
+ path = /
+http://user:[email protected]/ => http://user:[email protected]/
+ scheme = http
+ user = user
+ pass = pass
+ host = ftp.example.com
+ path = /
+http://user:pass:with:[email protected]/ =>
http://user:pass:with:[email protected]/
+ scheme = http
+ user = user
+ pass = pass:with:colon
+ host = ftp.example.com
+ path = /
+//user:pass@lan-host:456/ => //user:pass@lan-host:456/
+ user = user
+ pass = pass
+ host = lan-host
+ port = 456
+ path = /
+proto: => proto:
+ scheme = proto
+tcp://:80 => tcp://:80
+ scheme = tcp
+ port = 80
+http://nopath.com => http://nopath.com
+ scheme = http
+ host = nopath.com
+http://yespath.com/ => http://yespath.com/
+ scheme = http
+ host = yespath.com
+ path = /
+http://multipath.com//////////////////// =>
http://multipath.com////////////////////
+ scheme = http
+ host = multipath.com
+ path = ////////////////////
+http://coloninpath.com/C:/WINDOWS/ => http://coloninpath.com/C:/WINDOWS/
+ scheme = http
+ host = coloninpath.com
+ path = /C:/WINDOWS/
+//coloninpath.com/C:/WINDOWS/ => //coloninpath.com/C:/WINDOWS/
+ host = coloninpath.com
+ path = /C:/WINDOWS/
+/C:/WINDOWS/ => /C:/WINDOWS/
+ path = /C:/WINDOWS/
Index: modules/standard/Net.chpl
===================================================================
--- modules/standard/Net.chpl (revision 0)
+++ modules/standard/Net.chpl (working copy)
@@ -0,0 +1,424 @@
+// Net Module
+//
+// Portable networking module for Chapel. This module provides an abstraction
+// (inspired by Go's net package) over low level system sockets to make
+// creating and using network connections easier compared to the typically
+// unwieldly socket API.
+//
+// This module also contains a number of additonal utilities commonly used in
+// network applications.
+//
+// Issues and Future Work
+// - Right now, this library is too "UNIX-y".
+// - Asynchronous DNS lookup.
+
+use IO, Sys, SysBasic, Error;
+use _InternalDecor;
+
+/*
+enum AddrFamily {
+ pipe = AF_LOCAL,
+ IPv4 = AF_INET,
+ IPv6 = AF_INET6,
+ any = AF_UNSPEC
+}
+
+enum SockType {
+ stream = SOCK_STREAM,
+ dgram = SOCK_DGRAM,
+ raw = SOCK_RAW,
+ rdm = SOCK_RDM,
+ seq = SOCK_SEQPACKET
+}
+*/
+
+// This is here because getaddrinfo must be dynamically linked. The normal way
+// would be to define a wrapper in the Chapel runtime library, but the runtime
+// library is statically linked and does not like getaddrinfo being referenced
+// there.
+extern proc getaddrinfo(node:c_string, service:c_string,
+ ref hints:sys_addrinfo_t,
+ ref res:sys_addrinfo_ptr_t):int;
+extern proc gai_strerror(errcode:int):c_string;
+
+// The listening side of a socket connection.
+class Listener {
+ var _fd:fd_t;
+ var closed:bool = false;
+
+ proc Listener(fd:fd_t) {
+ _fd = fd;
+ }
+
+ proc ~Listener() {
+ this.close();
+ }
+
+ // Close the connection.
+ proc close() {
+ sys_close(_fd);
+ closed = true;
+ }
+
+ // Iterate over incoming connections.
+ iter these() {
+ while !closed do yield accept();
+ }
+
+ // Blocks until a new connection is received. This returns a file instead of
+ // two channels, even though a socket does not perfectly match the file
+ // abstraction. This allows the caller to close the connection, since closing
+ // all channels to a file does not seem to close the connection.
+ proc accept() {
+ var err:syserr;
+ var (conn, ip) = accept(err);
+ return (conn, ip, err);
+ }
+ proc accept(out err:syserr) {
+ var afd:fd_t, raddr:sys_sockaddr_t;
+ var ip:Addr;
+ var conn:file;
+
+ if closed then {
+ err = EBADF:syserr;
+ return (conn, ip);
+ }
+
+ // This is where the blocking happens.
+ err = sys_accept(_fd, raddr, afd):syserr;
+ if err then
+ return (conn, ip);
+ ip = new Addr(raddr);
+
+ // There doesn't seem to be a safe way to create channels from a file
+ // descriptor without having an associated file, so let's make one.
+ conn = openfd(afd, QIO_HINT_OWNED);
+
+ err = ENOERR:syserr;
+ return (conn, ip);
+ }
+}
+
+// Create and return a server socket.
+proc listen(proto:string, addr:Addr):(Listener,syserr) {
+ var (family, ptype) = _protoToSockFlags(proto);
+ var err:err_t, fd:fd_t;
+
+ err = sys_socket(addr._sys_addr.family, ptype, 0, fd);
+ if err then return (nil:Listener, err:syserr);
+
+ var yes:int(32) = 1;
+ err = sys_setsockopt(fd, IPPROTO_IP, SO_REUSEADDR, yes, 4);
+ if err then return (nil:Listener, err:syserr);
+
+ err = sys_bind(fd, addr._sys_addr);
+ if err then return (nil:Listener, err:syserr);
+
+ err = sys_listen(fd, 30);
+ if err then return (nil:Listener, err:syserr);
+
+ return (new Listener(fd), ENOERR:syserr);
+}
+proc listen(uri:string) { return listen(uriToAddr(uri)); }
+proc listen(uri:URI) { return listen(uriToAddr(uri)); }
+proc listen(proto:string, hostport:string) {
+ var (host, port) = splitHostPort(hostport);
+ return listen(proto, new Addr(host, port));
+}
+proc listen(proto:string, host:string, port:?) {
+ return listen(proto, new Addr(host, port:string));
+}
+proc listen(tuple:(string, Addr)) { // sigh...
+ return listen(tuple(1), tuple(2));
+}
+
+// Connect to a remote server.
+proc connect(proto:string, addr:Addr) {
+ var (family, ptype) = _protoToSockFlags(proto);
+ var err:err_t, fd:fd_t;
+ var f:file;
+
+ err = sys_socket(addr._sys_addr.family, ptype, 0, fd);
+ if err then return (f, err:syserr);
+
+ err = sys_connect(fd, addr._sys_addr);
+ if err then return (f, err:syserr);
+
+ f = openfd(fd, QIO_HINT_OWNED);
+ return (f, ENOERR:syserr);
+}
+proc connect(uri:string) { return connect(uriToAddr(uri)); }
+proc connect(uri:URI) { return connect(uriToAddr(uri)); }
+proc connect(proto:string, hostport:string) {
+ var (host, port) = splitHostPort(hostport);
+ return connect(proto, host, port);
+}
+proc connect(proto:string, host:string, port:?) {
+ return connect(proto, lookupHost(host, port)(1));
+}
+proc connect(tuple:(string, Addr)) { // sigh...
+ return connect(tuple(1), tuple(2));
+}
+
+// A network address, including an optional port.
+record Addr {
+ var _sys_addr:sys_sockaddr_t;
+
+ // Create an address from a string representation.
+ proc Addr(host:string, port = "") {
+ // Just delegate to getaddrinfo for now. TODO: Error handling.
+ var err:string;
+ (_sys_addr, err) = _rawLookupHost(host, port, AI_NUMERICHOST);
+ if err != "" then writeln(err);
+ }
+
+ // Methods for checking address family. If there is demand, we can add
+ // support for other address families.
+ //inline proc family:AddrFamily { return _sys_addr.family:AddrFamily; }
+ inline proc isIPv4:bool { return _sys_addr.family == AF_INET; }
+ inline proc isIPv6:bool { return _sys_addr.family == AF_INET6; }
+ inline proc isIP:bool { return isIPv4 || isIPv6; }
+
+ // Get the port associated with the address, or 0 if there is none.
+ inline proc port:int { return _sys_addr.port:int; }
+
+ // FIXME: There is a memory leak here. The string returned by sys_inet_pton
+ // needs to be freed.
+ proc writeThis(w:Writer) {
+ var str:string;
+ var err = sys_inet_ntop(_sys_addr.family:c_int, _sys_addr, str);
+
+ if err {
+ w.write("<", errorToString(err:syserr), ">");
+ } else {
+ if port > 0 && isIPv6 then w.write("[");
+ w.write(str);
+ if port > 0 then w.write(if isIPv6 then "]:" else ":", port);
+ }
+ }
+}
+
+// Determine the service and address based on a URI.
+inline proc uriToAddr(uri:string) { return uriToAddr(new URI(uri)); }
+inline proc uriToAddr(uri:URI) {
+ return (uri.scheme, lookupHost(uri.host, uri.port)(1));
+}
+
+// Determine the address family and socket type from the protocol.
+inline proc _protoToSockFlags(proto:string) {
+ select proto {
+ when "tcp" do return (AF_UNSPEC, SOCK_STREAM);
+ when "tcp4" do return (AF_INET, SOCK_STREAM);
+ when "tcp6" do return (AF_INET6, SOCK_STREAM);
+ when "udp" do return (AF_UNSPEC, SOCK_DGRAM);
+ when "udp4" do return (AF_INET, SOCK_DGRAM);
+ when "udp6" do return (AF_INET6, SOCK_DGRAM);
+ when "raw" do return (AF_UNSPEC, SOCK_RAW);
+ when "raw4" do return (AF_INET, SOCK_RAW);
+ when "raw6" do return (AF_INET6, SOCK_RAW);
+ when "file" do return (AF_LOCAL, SOCK_STREAM);
+ otherwise return (AF_UNSPEC, SOCK_STREAM);
+ }
+}
+
+// This is used internally to get a sys_sockaddr_t for connect/listen and
+// blocks. Can optionally do numerical parsing of an IP only.
+proc _rawLookupHost(in host = "", port = "", flags = 0):(sys_sockaddr_t,
string) {
+ var res:sys_addrinfo_ptr_t;
+ var hint:sys_addrinfo_t;
+ var sa:sys_sockaddr_t;
+
+ hint.ai_flags = flags:c_int;
+ hint.ai_family = 0;
+ hint.ai_socktype = 0;
+ hint.ai_protocol = 0;
+
+ // We should really pass null to getaddrinfo, but that's annoying in Chapel.
+ // We can handle it later.
+ if host == "" then host = "127.0.0.1";
+
+ var rv = getaddrinfo(host.c_str(), port.c_str(), hint, res);
+
+ if rv == 0 then {
+ sa = res.addr;
+ sys_freeaddrinfo(res);
+ return (sa, "");
+ } else {
+ return (sa, toString(gai_strerror(rv)));
+ }
+}
+
+// Look up the IP for a host. Returns IP and error message. If the resolution
+// is successful, the IP will be returned and the error message will be empty.
+// If there is an error, the IP will be nil and the error message will be set.
+// TODO: Detect IP strings, additional DNS records, asynchronous alternative,
+// error handling, portability, iterate over addrinfos, time limit.
+proc lookupHost(host = "", port = "", nstype = "A"):(Addr, string) {
+ var ip:Addr, err:string;
+ var (sa, e) = _rawLookupHost(host, port);
+ if e == "" {
+ ip = new Addr(sa); err = "";
+ } else {
+ err = e;
+ }
+ return (ip, err);
+}
+
+// A utility for URI parsing and manipulation based on the parsing algorithm
+// used in URI.js <http://medialize.github.io/URI.js/>. It complies with RFC
+// 3986, though not strictly; it is more liberal with what it accepts while
+// also being simpler.
+//
+// TODO: (Un)escaping, handle particularly egregious formatting errors,
+// normalization.
+class URI {
+ var scheme:string;
+ var user:string;
+ var pass:string;
+ var host:string;
+ var port:string;
+ var path:string;
+ var query:string;
+ var fragment:string;
+
+ // Create a URI object from a string representation.
+ proc URI(uri:string = "") {
+ if uri != "" then parseURI(uri, this);
+ }
+
+ // Write a human-readable string representation of this URI (i.e., with
+ // minimal escaping).
+ proc writeThis(w:Writer) {
+ if scheme != "" then w.write(scheme, ":");
+
+ if host != "" || port != "" || user != "" || pass != "" {
+ w.write("//");
+ if user != "" || pass != "" {
+ if user != "" then w.write(user);
+ if pass != "" then w.write(":", pass);
+ w.write("@");
+ }
+ if host != "" {
+ if host.contains(":") then
+ w.write("[",host,"]");
+ else
+ w.write(host);
+ }
+ if port != "" then w.write(":", port);
+ if path != "" && path.substring(1) != "/" then w.write("/");
+ }
+
+ if path != "" then w.write(path);
+ if query != "" then w.write("?", query);
+ if fragment != "" then w.write("#", fragment);
+ }
+}
+
+// Parse a string representation of a URI into the given URI object.
+proc parseURI(in struri:string, ref uri:URI = new URI()) {
+ var auth:string, ui:string, hp:string;
+
+ // Get the easy ones first.
+ (struri, uri.fragment) = struri.split("#");
+ (struri, uri.query) = struri.split("?");
+
+ // Try to get the scheme, authority, and path parts.
+ if struri.startsWith("//") {
+ struri = struri.substring(3..);
+ (auth, uri.path) = struri.split("/");
+ if struri.indexOf("/") then uri.path = "/"+uri.path;
+ } else if struri.indexOf(":") < 1 {
+ uri.path = struri;
+ } else {
+ var rest:string;
+ (uri.scheme, rest) = struri.split(":");
+ if uri.scheme.indexOf("/") {
+ uri.scheme = "";
+ uri.path = struri;
+ } else if uri.scheme == "file" {
+ uri.path = rest;
+ } else if rest.startsWith("//") {
+ rest = rest.substring(3..);
+ (auth, uri.path) = rest.split("/");
+ if rest.contains("/") then uri.path = "/"+uri.path;
+ } else {
+ uri.path = rest;
+ }
+ }
+
+ // Parse the authority into its subcomponents.
+ if auth.contains("@") {
+ (ui, hp) = auth.split("@");
+ (uri.user, uri.pass) = ui.split(":");
+ } else {
+ hp = auth;
+ }
+
+ // Parse out the host and port. TODO: Service resolution.
+ (uri.host, uri.port) = splitHostPort(hp);
+
+ // TODO: Unescaping and other normalizations.
+ return uri;
+}
+
+// Split a colon-delimited host and port (e.g., 127.0.0.1:123) into individual
+// components. This function handles bracketed host representation for IPv6
+// and hostnames containing colons, per RFC 2732.
+proc splitHostPort(in hostport:string):(string,string) {
+ var host:string, port:string;
+ if hostport.startsWith("[") {
+ (host, hostport) = hostport.substring(2..).split("]");
+ (hostport, port) = hostport.split(":");
+ return (host, port);
+ } else {
+ return hostport.split(":");
+ }
+}
+
+// Join a separate host and port into a colon-delimited string representation.
+// Will take care of bracketing hostnames with colons.
+proc joinHostPort(in host:string, port:string):string {
+ if host.indexOf(":") then
+ host = "["+host+"]";
+ if port == "" then
+ return host;
+ return host+":"+port;
+} proc joinHostPort(in host:string, port:integral):string {
+ return joinHostPort(host, port:string);
+}
+
+// This is a small decorator module used internally to add useful methods to
+// various types without making them available to users.
+module _InternalDecor {
+ inline proc string.split(delim:string):(string,string) {
+ var i = indexOf(delim);
+ if i == 0 then return (this, "");
+ return (substring(..i-1), substring(i+delim.length..));
+ }
+ inline iter string.splitAll(delim:string, in max = 0) {
+ // AKA "spliterator". :)
+ if delim == "" then return;
+ var token:string, rest = this;
+ while rest != "" {
+ (token, rest) = rest.split(delim);
+ yield token;
+ if max == 1 then return;
+ else max = max-1;
+ }
+ }
+ inline proc string.join(parts:[]?t):string {
+ if parts.size == 0 then return "";
+ var str = parts[1]:string;
+ for i in 2..parts.size do str = str+this+parts[i]:string;
+ return str;
+ }
+ inline proc string.contains(token:string):bool {
+ return indexOf(token) != 0;
+ }
+ inline proc string.startsWith(prefix:string) {
+ return indexOf(prefix) == 1;
+ }
+ inline proc string.endsWith(suffix:string) {
+ return indexOf(suffix) == this.length-suffix.length+1;
+ }
+}
------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT
organizations don't have a clear picture of how application performance
affects their revenue. With AppDynamics, you get 100% visibility into your
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Chapel-developers mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/chapel-developers