Last active
November 24, 2022 09:09
-
-
Save costajob/a56dc00d81e46bd44feb0c2a65f3d1b5 to your computer and use it in GitHub Desktop.
Barebones HTTP server cluster in Crystal lang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
that's great