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
endserver=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
enddef 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
endapp = 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
enddef 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
endserver=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