added SocketAddress>>family
added Socket>>readBytes:offset:length: started writing Http.moo
This commit is contained in:
		
							
								
								
									
										190
									
								
								moo/kernel/Http.moo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								moo/kernel/Http.moo
									
									
									
									
									
										Normal file
									
								
							| @ -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. | ||||
| 	} | ||||
| } | ||||
| @ -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. | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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  }  } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user