feat: parsing of headers

feat: return simple static html reponse
This commit is contained in:
Aymeric GUERACAGUE 2021-03-28 23:59:46 +02:00
parent ab7087581e
commit ed90eb566e
Signed by: Superkooka
GPG Key ID: F78F2B172E894865
5 changed files with 189 additions and 1 deletions

13
src/http/method.cr Normal file
View File

@ -0,0 +1,13 @@
enum Method
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
CONNECT
TRACE
NON_STANDARD
end

99
src/http/request.cr Normal file
View File

@ -0,0 +1,99 @@
require "./method"
require "uri"
class Request
@method : Method = Method::NON_STANDARD
@protocol_name : String | Nil
@protocol_version : String | Nil
@headers : Hash(String, String) = Hash(String, String).new()
@body : String = ""
@@status_line_regex = /(?'method'[A-Z]+) (?'uri'[^ ]+) (?'protocol'(?'protocol_name'[^ ]+)\/(?'protocol_version'[0-9]+.[0-9]+))/
@@header_line_regex = /^(?'name'[^:]+): (?'values'.+)/
def initialize(socket)
if socket.nil?
raise "Oooops"
end
plain_request = socket.receive[0]
current_header = ""
is_body = false
is_status_line = true
plain_request.chars.each do |char|
if is_body
@body += char
next
end
current_header += char
if current_header == "\r\n"
is_body = true
next
end
if current_header.ends_with?("\r\n") # End of header
if !is_status_line
regexified_header = @@header_line_regex.match(current_header)
if regexified_header.nil?
raise "Header regex is broken :aie:"
end
@headers[regexified_header["name"]] = regexified_header["values"].gsub({"\r": "", "\n": ""})
else
is_status_line = false
regexified_status_line = @@status_line_regex.match(current_header)
if regexified_status_line.nil?
raise "Status line regex is broken :aie:"
end
method = Method.parse?(regexified_status_line["method"])
if method.nil?
@method = Method::NON_STANDARD
else
@method = method
end
protocol_name = regexified_status_line["protocol_name"]
if protocol_name.nil?
raise "Status Line must have a valid protocol name"
else
@protocol_name = protocol_name
end
protocol_version = regexified_status_line["protocol_version"]
if protocol_version.nil?
raise "Status Line must have a valid protocol version"
else
@protocol_version = protocol_version
end
end
current_header = ""
end
end
if @protocol_name.nil?
raise "Request must have a protocol name in status line"
end
if @protocol_version.nil?
raise "Request must have a protocol version in status line"
end
puts "Method: " + @method.to_s
puts "Protocol name: " + @protocol_name.to_s
puts "Protocol version: " + @protocol_version.to_s
puts "Headers: " + @headers.to_s
puts "Body: "
puts @body
puts "\r\n"
end
def method : Method
return @method
end
end

22
src/http/response.cr Normal file
View File

@ -0,0 +1,22 @@
class Response
def initialize()
@headers = [
"HTTP/1.1 200 OK"
]
end
def to_s()
body = File.read("/vagrant/static/hellow.html")
@headers << "Content-Length: " + (body.bytesize + 1).to_s() # +1 for CRLF ?
request = ""
@headers.each() do |header|
request += header + "\r\n"
end
request += "\r\n"
request += body
return request
end
end

View File

@ -1 +1,42 @@
puts "Hellow World !"
require "socket"
require "option_parser"
require "./http/request"
require "./http/response"
def handle_request(client)
request = Request.new(client)
response = Response.new()
client.puts response.to_s()
client.close
end
tcp_port = 8000
hostname = "127.0.0.1"
OptionParser.parse do |parser|
parser.banner = "Usage: webcrystal [arguments]"
parser.on("-p PORT", "--port=PORT", "Specify the port to listen, default to 8000") do |port|
tcp_port = port.to_u32
end
parser.on("-n NAME", "--name NAME", "Hostname to listen, default to 127.0.0.1") do |name|
hostname = name
end
parser.on("-h", "--help", "Show this help") do
puts parser
exit
end
parser.invalid_option do |flag|
STDERR.puts "ERROR: #{flag} is not a valid option."
STDERR.puts parser
exit(1)
end
end
server = TCPServer.new(hostname, tcp_port)
while client = server.accept?
spawn handle_request(client)
end

13
static/hellow.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Wesh</title>
</head>
<body>
<h1>Hellow World !</h1>
<p>Cimer Nuker pour la découverte 😉</p>
</body>
</html>