Skip to content

Instantly share code, notes, and snippets.

@costajob
Last active November 24, 2022 09:09
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save costajob/a56dc00d81e46bd44feb0c2a65f3d1b5 to your computer and use it in GitHub Desktop.
Save costajob/a56dc00d81e46bd44feb0c2a65f3d1b5 to your computer and use it in GitHub Desktop.
Barebones HTTP server cluster in Crystal lang
require "http/server"
module HTTPCluster
struct Worker
def initialize(@pid : Int32, @master : Int32)
end
def call(port)
yield(port)
end
def kill
Process.kill(Signal::KILL, @pid)
end
end
class Cluster
def initialize(@n : Int32, @handler : HTTP::Handler)
@master = Process.pid
@workers = [] of Worker
end
def start
@n.times do |i|
fork do
worker = Worker.new(Process.pid, @master)
@workers << worker
worker.call(9292 + i) do |port|
puts "Listening on http://127.0.0.1:#{port}"
HTTP::Server.new(port, @handler).listen
end
end
end
loop {}
ensure
stop
end
private def stop
@workers.each(&.kill)
end
end
end
n = ARGV[0]? || 7
class HelloWorld < HTTP::Handler
def call(context)
context.response.content_type = "text/plain"
context.response.print "Hello World"
end
end
cluster = HTTPCluster::Cluster.new(n.to_i, HelloWorld.new)
cluster.start
require "socket"
struct Cluster
def initialize(@n : Int32, @host : String, @port : Int32, @message : String)
@server = TCPServer.new(@host, @port)
@pids = [] of Int32
end
def call
@n.times do
fork do
pid = Process.pid
@pids << pid
puts "child #{pid} accepting on shared socket (#{@host}:#{@port})"
loop {
@server.accept do |socket|
socket.puts response
socket.close
end
}
exit
end
end
loop {}
ensure
cleanup
end
private def response
String.build do |str|
str << "HTTP/1.1 200 OK\r\n"
str << "Content-Type: text/plain\r\n"
str << "Content-Length: #{@message.bytesize}\r\n"
str << "Connection: keep-alive\r\n"
str << "\r\n"
str << @message
end
end
private def cleanup
@pids.each do |pid|
Process.kill(Signal::KILL, pid)
end
end
end
n = ARGV[0]? || 3
host = ARGV[1]? || "localhost"
port = ARGV[2]? || 9292
message = ARGV[3]? || "Hello World"
Cluster.new(n.to_i, host, port.to_i, message).call
@mrfarhadir
Copy link

that's great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment