Building a simple web server with Ruby socket. This web server is in its simplest form. Listening at port 4567, accept GET request, response with “Hello World”, reject all other request with “Forbidden”.

require 'socket'def response(client, status, headers, body)
client.print "HTTP/1.1 #{status}\r\n"
headers.each do |key, value|
client.print "#{key}: #{value}\r\n"
end
client.print "\r\n"
body.each do |part|
client.print part
end
end
server=TCPServer.new 4567
puts "server started"
loop do
Thread.start(server.accept) do |client|
begin
req=client.gets
request_headers, request_body=client.readpartial(2048).split("\r\n\r\n",2)
method, url=request.split(' ')
if method=='GET'
response(client,200,{'Content-Type'=>'text/plain'},['Hello World'])
else
response(client,403,{'Content-Type'=>'text/plain'},['Forbidden'])
end
rescue => e
puts e
ensure
client.close
end
end
end

In its simplest form may not be the most best or efficient. Codes below are enhancement to the simplest form. Enhancements are

- Multi processes, so that slow connection does not hog up the web server.
- Ability to access the request header and query strings in a hash.
- Ability to detect URL path and process accordingly.
- Ability to get the request IP address.

require 'socket'def str_query_strings_to_hash(str)
hash=Hash.new
unless str.nil?
params=str.split('&')
params.each do|param|
pair=param.split('=',2)
hash[pair[0].downcase]=pair[1]
end
end
return hash
end
def str_headers_to_hash(str)
hash=Hash.new
lines=str.split("\r\n")
lines.each do|line|
pair=line.split(':')
hash[pair[0].to_s.strip.downcase]=pair[1].to_s.strip
end
return hash
end
app = Proc.new do|param|
headers=str_headers_to_hash(param['request_headers'])
method=param["request_method"]
path=param["path"]
query_strings=str_query_strings_to_hash(param['query_strings'])
ip=param["ip"]
if method=='GET' and path == '/'
['200', {'Content-Type' => 'text/plain'}, ["Hello World"]]
else
['403', {'Content-Type' => 'text/plain'}, ["Forbidden"]]
end
end
def response(client, status, headers, body)
client.print "HTTP/1.1 #{status}\r\n"
headers.each do |key, value|
client.print "#{key}: #{value}\r\n"
end
client.print "\r\n"
body.each do |part|
client.print part
end
end
server=TCPServer.new 4567
puts "server started"
loop do
Thread.start(server.accept) do |client|
begin
request=client.gets
request_headers, request_body=client.readpartial(2048).split("\r\n\r\n",2)
method, remainder=request.split(' ',2)
remainder=remainder.split(' ')
remainder.pop
url=remainder.join(' ')
path, query_strings=url.split('?',2)
status, headers,body=app.call({
'request_headers'=>request_headers,
'request_method'=>method,
'path'=>path,
'query_strings'=>query_strings,
'ip'=>client.peeraddr[3]
})
response(client, status, headers, body)
rescue => e
puts e
ensure
client.close
end
end
end

< Back to all the stories I had written

Written by

A software engineer, a rock climbing, diving & rollerblading enthusiast, a husband, a father.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store