|
require "socket" |
|
|
|
class ChatServer |
|
def initialize(port) |
|
@port = port |
|
@connections = [] |
|
end |
|
|
|
def start() |
|
socket = TCPServer.open(@port) |
|
addr = socket.addr() |
|
addr.shift() |
|
puts "server is on #{addr.join(':')}" |
|
|
|
acceptor = create_acceptor() |
|
puts "acceptor created: #{acceptor.inspect}" |
|
acceptor.send(socket, move: true) |
|
|
|
periodic_timer = create_periodic_timer() |
|
puts "periodic_timer created: #{periodic_timer.inspect}" |
|
|
|
loop do |
|
r, obj = Ractor.select(periodic_timer, acceptor) |
|
case r |
|
when acceptor |
|
puts "Acceptor: #{obj}" |
|
case obj |
|
when Ractor |
|
@connections << obj |
|
when String |
|
@connections.select! { |c| c.send(obj) rescue nil } |
|
else |
|
puts "warn: ignored event r=#{r}, obj=#{obj}" |
|
end |
|
when periodic_timer |
|
puts "PeriodicTimer" |
|
time = Time.now |
|
@connections.select! { |c| c.send(time) rescue nil} |
|
puts time |
|
puts `vmstat` |
|
puts "connections count: #{@connections.count}" |
|
end |
|
end |
|
end |
|
|
|
def create_periodic_timer() |
|
Ractor.new do |
|
loop do |
|
Ractor.yield(nil) |
|
sleep(60) |
|
end |
|
end |
|
end |
|
|
|
def create_acceptor() |
|
Ractor.new do |
|
tcp_server = Ractor.recv() |
|
|
|
loop do |
|
accept = tcp_server.accept |
|
request = accept.readpartial(4096) |
|
verb, path, _ = *request.split("\r\n").first.split(" ") |
|
case verb |
|
when "GET" |
|
if(path == "/") |
|
accept.print("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n") |
|
accept.print(<<__HTML__) |
|
<!DOCTYPE html> |
|
<html> |
|
<head><meta charset="utf-8"></head> |
|
<body> |
|
<form> |
|
<input type="text" name="data"> |
|
<input type="submit" value="send"> |
|
</form> |
|
<div id="messages"></div> |
|
<script> |
|
var source = new EventSource("/rooms/lobby"); // URL は / 以外ならなんでもいいけど将来にそなえてこうしてる |
|
source.onmessage = function(event) { |
|
document.getElementById("messages").innerHTML += event.data + "<br>"; |
|
}; |
|
var formElement = document.querySelector("form"); |
|
formElement.addEventListener('submit', function(event) { |
|
event.preventDefault(); |
|
fetch("/rooms/lobby", { |
|
method: "POST", |
|
body: new FormData(formElement) |
|
}); |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
__HTML__ |
|
accept.flush() |
|
accept.close() |
|
else |
|
r = Ractor.new do |
|
c = Ractor.recv() |
|
c.print("HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nContent-Type: text/event-stream\r\n\r\n") |
|
c.flush() |
|
|
|
loop do |
|
data = Ractor.recv() |
|
c.print("data: #{data}\r\n\r\n") |
|
c.flush() |
|
end |
|
end |
|
Ractor.yield(r) |
|
r.send(accept, move: true) |
|
end |
|
when "POST" |
|
accept.print("HTTP/1.1 202 Accepted\r\n") |
|
accept.close() |
|
body = request.split("\r\n\r\n").last.split("\r\n").first |
|
Ractor.yield(body) |
|
else |
|
puts "warn: ignored request verb=#{verb}, path=#{path}" |
|
end |
|
end |
|
end |
|
end |
|
end |
|
|
|
ChatServer.new(8080).start() |
This comment has been minimized.
typo in the
pwd
command, (was missing$
). Worked with:curl -O -L https://gist.githubusercontent.com/niku/c19da11edf0b97470af27844b44d12fa/raw/91afc1b01dd5d7083965d7a718e3f1c09cafb7b0/chat_server.rb docker run -v $(pwd):/app -p 8080:8080 -it wakaba260/ruby-ractor-dev ruby /app/chat_server.rb