moo/moo/kernel/Http.moo

498 lines
9.0 KiB
Smalltalk
Raw Normal View History

###include 'Moo.moo'.
#include 'Socket.moo'.
2018-05-13 16:28:22 +00:00
class HttpConnReg(Object)
{
var connections.
var first_free_slot.
var last_free_slot.
method initialize
{
| i size |
self.connections := Array new: 32. ## TODO: make it dynamic
i := self.connections size.
if (i <= 0)
{
self.first_free_slot := -1.
self.last_free_slot := -1.
}
else
{
i := (self.first_free_slot := i - 1).
while (i >= 0)
{
self.connections at: i put: (i - 1).
i := i - 1.
}.
self.last_free_slot := 0.
}.
}
method add: elem
{
| index |
2018-05-22 05:15:49 +00:00
if (self.first_free_slot < 0) { Exception signal: 'no free slot' }.
2018-05-13 16:28:22 +00:00
index := self.first_free_slot.
self.first_free_slot := (self.connections at: index).
self.connections at: index put: elem.
if (self.first_free_slot < 0) { self.last_free_slot = -1 }.
^index.
}
method remove: index
{
if (self.last_free_slot >= 0)
{
self.connections at: self.last_free_slot put: index.
}
else
{
self.first_free_slot := self.last_free_slot.
}.
self.connections at: index put: -1.
self.last_free_slot := index.
}
method do: block
{
| index size conn |
2018-05-22 16:22:32 +00:00
## the following loop won't evaluate the given block for an element added after
## resizing of self.connections at present, there is no self.connections resizing
## impelemented. so no worry on this.
size := self.connections size.
index := 0.
while (index < size)
{
conn := self.connections at: index.
if ((conn isKindOf: Integer) not)
{
block value: (self.connections at: index).
}.
index := index + 1.
}.
}
2018-05-13 16:28:22 +00:00
}
2018-05-22 16:22:32 +00:00
class HttpBuffer(Object)
{
2018-05-22 16:22:32 +00:00
}
2018-05-16 10:25:20 +00:00
class HttpSocket(SyncSocket)
{
2018-05-13 16:28:22 +00:00
var(#get) server := nil.
var(#get) rid := -1.
2018-05-13 16:28:22 +00:00
method close
{
('Http Connection closing.......... handle ' & self.handle asString) dump.
if (self.server notNil)
{
('Http Connection ' & self.rid asString & ' removing from server ..........') dump.
2018-05-13 16:28:22 +00:00
self.server removeConnection: self.
}.
^super close.
}
method server: server rid: rid
2018-05-13 16:28:22 +00:00
{
self.server := server.
self.rid := rid.
2018-05-13 16:28:22 +00:00
}
2018-05-16 10:25:20 +00:00
2018-05-22 16:22:32 +00:00
method getLine
{
}
method readRequest
{
}
2018-05-16 10:25:20 +00:00
method _run_service
{
| buf |
2018-05-22 16:22:32 +00:00
self timeout: 10.
(*while (true)
{
req := self readRequest.
}. *)
2018-05-16 10:25:20 +00:00
buf := ByteArray new: 128.
2018-05-22 05:15:49 +00:00
'IM RUNNING SERVICE...............' dump.
2018-05-22 16:22:32 +00:00
2018-05-16 10:25:20 +00:00
self readBytes: buf.
buf dump.
self readBytes: buf.
buf dump.
self close.
}
method runService
{
[ self _run_service ] on: Exception do: [:ex |
self close.
('EXCEPTION IN HttpSocket ' & ex messageText) dump
].
}
}
class HttpListener(AsyncServerSocket)
{
var(#get) server := nil.
2018-05-22 05:15:49 +00:00
var(#get) rid := nil.
2018-05-10 16:16:03 +00:00
method initialize
{
super initialize.
}
2018-05-22 16:22:32 +00:00
method close
{
if (self.server notNil) { self.server removeListener: self }.
^super close.
}
2018-05-22 05:15:49 +00:00
method onSocketAccepted: clisck from: cliaddr
{
| rid |
2018-05-13 16:28:22 +00:00
2018-05-22 16:22:32 +00:00
'CLIENT accepted ..............' dump.
clisck dump.
2018-05-22 16:22:32 +00:00
cliaddr dump.
2018-05-13 16:28:22 +00:00
if (self.server notNil)
{
2018-05-22 05:15:49 +00:00
[
server addConnection: clisck.
if (clisck isKindOf: SyncSocket)
{
'SERVICE READLLY STARTING' dump.
2018-05-22 05:15:49 +00:00
[clisck runService] fork.
}
]
on: Exception do: [:ex |
2018-05-22 16:22:32 +00:00
clisck close.
2018-05-22 05:15:49 +00:00
Exception signal: ('unable to handle a new connection - ' & ex messageText).
].
2018-05-13 16:28:22 +00:00
}.
}
method acceptedSocketClass
2018-05-09 16:43:58 +00:00
{
2018-05-10 16:16:03 +00:00
##^if (self currentAddress port == 80) { HttpSocket } else { HttpSocket }.
^HttpSocket.
2018-05-09 16:43:58 +00:00
}
method server: server rid: rid
{
self.server := server.
self.rid := rid.
}
}
class HttpServer(Object)
{
2018-05-13 16:28:22 +00:00
var listeners.
var connreg.
method initialize
{
super initialize.
self.listeners := HttpConnReg new.
2018-05-13 16:28:22 +00:00
self.connreg := HttpConnReg new.
}
2018-05-22 05:15:49 +00:00
method __add_listener: listener
{
| rid |
rid := self.listeners add: listener.
('ADD NEW LISTENER ' & rid asString) dump.
listener server: self rid: rid.
}
2018-05-22 16:22:32 +00:00
method removeListener: listener
2018-05-22 05:15:49 +00:00
{
| rid |
2018-05-22 16:22:32 +00:00
rid := listener rid.
2018-05-22 05:15:49 +00:00
if (rid notNil)
{
2018-05-22 16:22:32 +00:00
('REALLY REMOVE LISTENER ' & rid asString) dump.
2018-05-22 05:15:49 +00:00
self.listeners remove: (listener rid).
listener server: nil rid: nil.
}.
}
2018-05-22 16:22:32 +00:00
method __add_new_listener: addr
{
2018-05-13 16:28:22 +00:00
| listener |
2018-05-22 05:15:49 +00:00
listener := HttpListener family: (addr family) type: Socket.Type.STREAM.
[
2018-05-22 16:22:32 +00:00
self __add_listener: listener.
listener bind: addr.
listener listen: 128.
2018-05-22 05:15:49 +00:00
] on: Exception do: [:ex |
listener close.
## ex pass.
Exception signal: ('unable to add new listener - ' & ex messageText).
].
}
2018-05-13 16:28:22 +00:00
method addConnection: conn
{
| rid |
rid := self.connreg add: conn.
('ADD NEW CONNECTION ' & rid asString) dump.
conn server: self rid: rid.
2018-05-13 16:28:22 +00:00
}
method removeConnection: conn
{
| rid |
2018-05-22 05:15:49 +00:00
rid := conn rid.
if (rid notNil)
{
2018-05-22 16:22:32 +00:00
('REMOVE CONNECTION ' & rid asString) dump.
2018-05-22 05:15:49 +00:00
self.connreg remove: (conn rid).
2018-05-22 16:22:32 +00:00
conn server: nil rid: nil.
}.
2018-05-13 16:28:22 +00:00
}
2018-05-21 17:14:16 +00:00
2018-05-22 05:15:49 +00:00
method start: laddr
2018-05-21 17:14:16 +00:00
{
2018-05-22 16:22:32 +00:00
| listener |
2018-05-21 17:14:16 +00:00
if (laddr class == Array)
2018-05-22 16:22:32 +00:00
##if (laddr respondsTo: #do:) ## can i check if the message receives a block and the block accepts 1 argument?
2018-05-21 17:14:16 +00:00
{
2018-05-22 16:22:32 +00:00
laddr do: [:addr | self __add_new_listener: addr ].
2018-05-21 17:14:16 +00:00
}
else
{
2018-05-22 16:22:32 +00:00
self __add_new_listener: laddr.
2018-05-21 17:14:16 +00:00
}.
}
method close
{
self.listeners do: [:listener |
listener close.
].
self.connreg do: [:conn |
conn close.
].
}
}
class MyObject(Object)
{
method(#class) start_server_socket
{
| s2 buf |
s2 := AsyncServerSocket 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 := AsyncClientSocket 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)
{
2018-05-15 16:38:37 +00:00
ss := thisProcess handleAsyncEvent.
if (ss isError) { break }.
2018-05-15 16:38:37 +00:00
###if (ss == st) { thisProcess 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) another_proc: base_port
2018-05-15 16:38:37 +00:00
{
| httpd |
[
thisProcess initAsync.
httpd := HttpServer new.
[
| ss |
httpd start: %(
SocketAddress fromString: ('[::]:' & base_port asString),
SocketAddress fromString: ('0.0.0.0:' & (base_port + 1) asString)
2018-05-15 16:38:37 +00:00
).
while (true)
{
ss := thisProcess handleAsyncEvent.
if (ss isError) { break }.
}.
] ensure: [
if (httpd notNil) { httpd close }
]
] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump].
'----- END OF ANOTHER PROC ------' dump.
}
2018-05-22 05:15:49 +00:00
method(#class) main
{
| httpd addr |
(*
[
addr := SocketAddress fromString: '1.2.3.4:5555'.
##addr := SocketAddress fromString: '127.0.0.1:22'.
httpd := SyncSocket family: (addr family) type: Socket.Type.STREAM.
httpd timeout: 5.
httpd connect: addr.
] on: Exception do: [:ex | ].
*)
[ self another_proc: 5000 ] fork.
[ self another_proc: 5100 ] fork.
[ self another_proc: 5200 ] fork.
2018-05-15 16:38:37 +00:00
[
2018-05-15 16:38:37 +00:00
thisProcess initAsync.
httpd := HttpServer new.
[
| ss |
httpd start: %(
SocketAddress fromString: '[::]:7777',
SocketAddress fromString: '0.0.0.0:7776'
).
2018-05-15 16:38:37 +00:00
while (true)
{
ss := thisProcess handleAsyncEvent.
if (ss isError) { break }.
}.
] ensure: [
if (httpd notNil) { httpd close }
]
] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump].
'----- END OF MAIN ------' dump.
}
2018-05-21 17:14:16 +00:00
2018-05-22 05:15:49 +00:00
method(#class) mainqq
2018-05-21 17:14:16 +00:00
{
2018-05-22 05:15:49 +00:00
| httpd addr sg ss |
sg := SemaphoreGroup new.
2018-05-21 17:14:16 +00:00
[
2018-05-22 16:22:32 +00:00
httpd := HttpServer new.
2018-05-21 17:14:16 +00:00
[
2018-05-22 05:15:49 +00:00
httpd start: %(
2018-05-21 17:14:16 +00:00
SocketAddress fromString: '[::]:7777',
SocketAddress fromString: '0.0.0.0:7776'
).
2018-05-22 05:15:49 +00:00
while (true)
{
ss := sg wait.
if (ss isError) { break }.
}
2018-05-21 17:14:16 +00:00
] ensure: [
if (httpd notNil) { httpd close }
]
] on: Exception do: [:ex | ('Exception - ' & ex messageText) dump].
'----- END OF MAIN ------' dump.
}
}