diff --git a/moo/kernel/Http.moo b/moo/kernel/Http.moo new file mode 100644 index 0000000..b337720 --- /dev/null +++ b/moo/kernel/Http.moo @@ -0,0 +1,190 @@ +###include 'Moo.moo'. +#include 'Socket.moo'. + + +class HttpServerSocket(ServerSocket) +{ +} + +class HttpServer(Object) +{ + var server_sockets. + + method initialize + { + server_sockets := LinkedList new. + } + + method start: laddr + { + | sck | + + if (laddr class == Array) + { + laddr do: [:addr | + sck := ServerSocket family: (addr family) type: Socket.Type.STREAM. + self.server_sockets addLast: sck. + sck bind: addr. + ]. + } + else + { + sck := ServerSocket family: (laddr family) type: Socket.Type.STREAM. + self.server_sockets addLast: sck. + sck bind: laddr. + }. + + self.server_sockets do: [:ssck | + ssck listen: 128. + ]. + } + + method close + { + self.server_sockets do: [:sck | + sck close. + ]. + + while (self.server_sockets size > 0) + { + self.server_sockets removeLastLink. + }. + } +} + + + +class MyObject(Object) +{ + method(#class) start_server_socket + { + | s2 buf | + s2 := ServerSocket family: Socket.Family.INET type: Socket.Type.STREAM. + buf := ByteArray new: 128. + + s2 onEvent: #accepted do: [ :sck :clisck :cliaddr | +'SERVER ACCEPTED new client' dump. + clisck onEvent: #data_in do: [ :csck | + | nbytes | + while (true) + { + nbytes := csck readBytes: buf. + if (nbytes <= 0) + { + if (nbytes == 0) { csck close }. + ('Got ' & (nbytes asString)) dump. + break. + }. + + buf dump. + csck writeBytes: buf offset: 0 length: nbytes. + }. + ]. + clisck onEvent: #data_out do: [ :csck | + ##csck writeBytes: #[ $a, $b, C'\n' ]. + ]. + clisck onEvent: #closed do: [ :csck | + 'Socket CLOSED....' dump. + ]. + ]. + + s2 bind: (SocketAddress fromString: '0.0.0.0:7777'). + s2 listen: 10. + ^s2. + } + + method(#class) start_client_socket + { + | s buf count | + s := ClientSocket family: Socket.Family.INET type: Socket.Type.STREAM. + buf := ByteArray new: 128. + + count := 0. + + s onEvent: #connected do: [ :sck :state | + if (state) + { + s writeBytes: #[ $a, $b, $c ]. + s writeBytes: #[ $d, $e, $f ]. + } + else + { + 'FAILED TO CONNECT' dump. + }. + ]. + + s onEvent: #data_in do: [ :sck | + | nbytes | + while (true) + { + nbytes := sck readBytes: buf. + if (nbytes <= 0) + { + if (nbytes == 0) { sck close }. + break. + }. + ('Got ' & (nbytes asString)) dump. + buf dump. + }. + ]. + s onEvent: #data_out do: [ :sck | + if (count < 10) { sck writeBytes: #[ $a, $b, C'\n' ]. count := count + 1. }. + ]. + + s connect: (SocketAddress fromString: '127.0.0.1:9999'). + } + + method(#class) mainxx + { + [ + | s s2 ss | + + [ + s := self start_client_socket. + s2 := self start_server_socket. + + while (true) + { + ss := System handleAsyncEvent. + if (ss isError) { break }. + ###if (ss == st) { System removeAsyncSemaphore: st }. + }. + ] + ensure: + [ + if (s notNil) { s close }. + if (s2 notNil) { s2 close }. + ] + + ] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump ]. + + '----- END OF MAIN ------' dump. + } + + + method(#class) main + { + | httpd | + + [ + httpd := HttpServer new. + [ + | ss | + httpd start: %( + SocketAddress fromString: '[::]:7777', + SocketAddress fromString: '0.0.0.0:7776' + ). + + while (true) { + ss := System handleAsyncEvent. + if (ss isError) { break }. + }. + ] ensure: [ + if (httpd notNil) { httpd close } + ] + + ] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump]. + + '----- END OF MAIN ------' dump. + } +} diff --git a/moo/kernel/Socket.moo b/moo/kernel/Socket.moo index 5a7ba88..6215588 100644 --- a/moo/kernel/Socket.moo +++ b/moo/kernel/Socket.moo @@ -216,9 +216,9 @@ class(#byte(16)) IP6Address(IP4Address) class(#byte) SocketAddress(Object) from 'sck.addr' { - ##method(#primitive) family. + method(#primitive) family. method(#primitive) fromString: str. - + method(#class) fromString: str { ^self new fromString: str @@ -230,6 +230,7 @@ class AsyncHandle(Object) ## the handle must be the first field in this object to match ## the internal representation used by various modules. (e.g. sck) var(#get) handle := -1. + var(#get) closedEventAction := nil. ##method initialize ##{ @@ -242,9 +243,25 @@ class AsyncHandle(Object) { self _close. self.handle := -1. + + if (self.closedEventAction notNil) + { + self.closedEventAction value: self. + }. } } + method onEvent: event_type do: action_block + { + if (event_type == #closed) + { + self.closedEventAction := action_block. + ^self. + }. + + Exception signal: 'unknown event type ' & event_type asString. + } + method writeBytes: bytes offset: offset length: length { ^self writeBytes: bytes offset: offset length: length. @@ -264,7 +281,7 @@ class Socket(AsyncHandle) from 'sck' var pending_bytes, pending_offset, pending_length. var outreadysem, outdonesem, inreadysem. - method(#primitive) open(domain, type, proto). + method(#primitive) open(family, type, proto). method(#primitive) _close. method(#primitive) bind: addr. method(#primitive) _listen: backlog. @@ -273,12 +290,13 @@ class Socket(AsyncHandle) from 'sck' method(#primitive) _socketError. method(#primitive) readBytes: bytes. + method(#primitive) readBytes: bytes offset: offset length: length. method(#primitive) _writeBytes: bytes. method(#primitive) _writeBytes: bytes offset: offset length: length. } -(* TODO: generate these domain and type from the C header *) -pooldic Socket.Domain +(* TODO: generate these family and type from the C header *) +pooldic Socket.Family { INET := 2. INET6 := 10. @@ -295,9 +313,9 @@ extend Socket method(#class) new { self messageProhibited: #new } method(#class) new: size { self messageProhibited: #new: } - method(#class) domain: domain type: type + method(#class) family: family type: type { - ^(super new) open(domain, type, 0) + ^(super new) open(family, type, 0) } method initialize @@ -351,8 +369,6 @@ extend Socket method close { -'Socket close' dump. - if (self.outdonesem notNil) { System unsignal: self.outdonesem. @@ -390,7 +406,7 @@ extend Socket } else { - Exception signal: 'unknown event type ' & event_type asString. + ^super onEvent: event_type do: action_block. } } @@ -569,109 +585,3 @@ class ServerSocket(Socket) ^self _listen: backlog. } } - -class MyObject(Object) -{ - method(#class) start_server_socket - { - | s2 buf | - s2 := ServerSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. - buf := ByteArray new: 128. - - s2 onEvent: #accepted do: [ :sck :clisck :cliaddr | -'SERVER ACCEPTED new client' dump. - clisck onEvent: #data_in do: [ :csck | - | nbytes | - while (true) - { - nbytes := csck readBytes: buf. - if (nbytes <= 0) - { - if (nbytes == 0) { csck close }. - ('Got ' & (nbytes asString)) dump. - break. - }. - - buf dump. - csck writeBytes: buf offset: 0 length: nbytes. - }. - ]. - clisck onEvent: #data_out do: [ :csck | - ##csck writeBytes: #[ $a, $b, C'\n' ]. - ]. - ]. - - s2 bind: (SocketAddress fromString: '0.0.0.0:7777'). - s2 listen: 10. - ^s2. - } - - method(#class) start_client_socket - { - | s buf count | - s := ClientSocket domain: Socket.Domain.INET type: Socket.Type.STREAM. - buf := ByteArray new: 128. - - count := 0. - - s onEvent: #connected do: [ :sck :state | - if (state) - { - s writeBytes: #[ $a, $b, $c ]. - s writeBytes: #[ $d, $e, $f ]. - } - else - { - 'FAILED TO CONNECT' dump. - }. - ]. - - s onEvent: #data_in do: [ :sck | - | nbytes | - while (true) - { - nbytes := sck readBytes: buf. - if (nbytes <= 0) - { - if (nbytes == 0) { sck close }. - break. - }. - ('Got ' & (nbytes asString)) dump. - buf dump. - }. - ]. - s onEvent: #data_out do: [ :sck | - if (count < 10) { sck writeBytes: #[ $a, $b, C'\n' ]. count := count + 1. }. - ]. - - s connect: (SocketAddress fromString: '127.0.0.1:9999'). - } - - method(#class) main - { - [ - | s s2 ss | - - [ - s := self start_client_socket. - s2 := self start_server_socket. - - while (true) - { - ss := System handleAsyncEvent. - if (ss isError) { break }. - ###if (ss == st) { System removeAsyncSemaphore: st }. - }. - ] - ensure: - [ - if (s notNil) { s close }. - if (s2 notNil) { s2 close }. - ] - - ] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump ]. - - '----- END OF MAIN ------' dump. - } -} - diff --git a/moo/mod/sck-addr.c b/moo/mod/sck-addr.c index a0f86d1..1ac49ca 100644 --- a/moo/mod/sck-addr.c +++ b/moo/mod/sck-addr.c @@ -55,36 +55,9 @@ #define MA MOO_TYPE_MAX(moo_oow_t) -typedef struct sck_addr_trailer_t sck_addr_trailer_t; - -struct sck_addr_trailer_t -{ - int family; - union - { - struct - { - moo_uint16_t port; - moo_uint32_t addr; - } in4; - struct - { - moo_uint16_t port; - moo_uint8_t addr[16]; - moo_uint32_t scope; - } in6; - - /* - struct - { - moo_ooch_t path[100]; - } local; */ - } u; -}; - - union sockaddr_t { + struct sockaddr sa; #if (MOO_SIZEOF_STRUCT_SOCKADDR_IN > 0) struct sockaddr_in in4; #endif @@ -104,7 +77,7 @@ static int str_to_ipv4 (const moo_ooch_t* str, moo_oow_t len, struct in_addr* in { const moo_ooch_t* end; int dots = 0, digits = 0; - moo_uint32_t acc = 0, addr = 0; + moo_uint32_t acc = 0, addr = 0; moo_ooch_t c; end = str + len; @@ -482,6 +455,21 @@ no_rbrack: return -1; } +static moo_pfrc_t pf_get_family (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) +{ + moo_oop_t rcv; + moo_oop_t v; + struct sockaddr* sa; + + rcv = (moo_oop_t)MOO_STACK_GETRCV(moo, nargs); + MOO_PF_CHECK_RCV (moo, MOO_OBJ_IS_BYTE_POINTER(rcv) && MOO_OBJ_GET_SIZE(rcv) >= MOO_SIZEOF(sockaddr_t)); + + sa = (struct sockaddr*)MOO_OBJ_GET_BYTE_SLOT(rcv); + v = MOO_SMOOI_TO_OOP(sa->sa_family); + + MOO_STACK_SETRET (moo, nargs, v); + return MOO_PF_SUCCESS; +} static moo_pfrc_t pf_from_string (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { @@ -506,7 +494,8 @@ static moo_pfrc_t pf_from_string (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) static moo_pfinfo_t pfinfos[] = { - { I, { 'f','r','o','m','S','t','r','i','n','g',':','\0' }, 0, { pf_from_string, 1, 1 } }, + { I, { 'f','a','m','i','l','y' }, 0, { pf_get_family, 0, 0 } }, + { I, { 'f','r','o','m','S','t','r','i','n','g',':','\0' }, 0, { pf_from_string, 1, 1 } } }; /* ------------------------------------------------------------------------ */ @@ -514,7 +503,6 @@ static moo_pfinfo_t pfinfos[] = static int import (moo_t* moo, moo_mod_t* mod, moo_oop_class_t _class) { moo_ooi_t spec; - /*if (moo_setclasstrsize (moo, _class, MOO_SIZEOF(sck_addr_trailer_t), MOO_NULL) <= -1) return -1;*/ spec = MOO_OOP_TO_SMOOI(_class->spec); if (!MOO_CLASS_SPEC_IS_INDEXED(spec) || MOO_CLASS_SPEC_INDEXED_TYPE(spec) != MOO_OBJ_TYPE_BYTE || MOO_CLASS_SPEC_NAMED_INSTVARS(spec) != 0) diff --git a/moo/mod/sck.c b/moo/mod/sck.c index 87d8f88..d18af8f 100644 --- a/moo/mod/sck.c +++ b/moo/mod/sck.c @@ -404,6 +404,7 @@ static moo_pfrc_t pf_read_socket (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) { oop_sck_t sck; moo_oop_byte_t buf; + moo_oow_t offset, length, maxlen; int fd; ssize_t n; @@ -428,7 +429,36 @@ static moo_pfrc_t pf_read_socket (moo_t* moo, moo_mod_t* mod, moo_ooi_t nargs) return MOO_PF_FAILURE; } - n = recv(fd, MOO_OBJ_GET_BYTE_SLOT(buf), MOO_OBJ_GET_SIZE(buf), 0); + offset = 0; + maxlen = MOO_OBJ_GET_SIZE(buf); + length = maxlen; + + if (nargs >= 2) + { + moo_oop_t tmp; + + tmp = MOO_STACK_GETARG(moo, nargs, 1); + if (moo_inttooow (moo, tmp, &offset) <= 0) + { + moo_seterrbfmt (moo, MOO_EINVAL, "invalid offset - %O", tmp); + return MOO_PF_FAILURE; + } + + if (nargs >= 3) + { + tmp = MOO_STACK_GETARG(moo, nargs, 2); + if (moo_inttooow(moo, tmp, &length) <= 0) + { + moo_seterrbfmt (moo, MOO_EINVAL, "invalid length - %O", tmp); + return MOO_PF_FAILURE; + } + } + + if (offset >= maxlen) offset = maxlen - 1; + if (length > maxlen - offset) length = maxlen - offset; + } + + n = recv(fd, &MOO_OBJ_GET_BYTE_SLOT(buf)[offset], length, 0); if (n <= -1 && errno != EWOULDBLOCK && errno != EAGAIN) { moo_seterrwithsyserr (moo, errno); @@ -540,6 +570,7 @@ static moo_pfinfo_t pfinfos[] = { I, { 'l','i','s','t','e','n',':','\0' }, 0, { pf_listen_socket, 1, 1 } }, { I, { 'o','p','e','n','\0' }, 0, { pf_open_socket, 3, 3 } }, { I, { 'r','e','a','d','B','y','t','e','s',':','\0' }, 0, { pf_read_socket, 1, 1 } }, + { I, { 'r','e','a','d','B','y','t','e','s',':','o','f','f','s','e','t',':','l','e','n','g','t','h',':','\0' }, 0, { pf_read_socket, 3, 3 } }, { I, { 's','o','c','k','e','t','E','r','r','o','r','\0' }, 0, { pf_get_socket_error, 0, 0 } }, { I, { 'w','r','i','t','e','B','y','t','e','s',':','\0' }, 0, { pf_write_socket, 1, 1 } }, { I, { 'w','r','i','t','e','B','y','t','e','s',':','o','f','f','s','e','t',':','l','e','n','g','t','h',':','\0' }, 0, { pf_write_socket, 3, 3 } }