feat: parsing of headers
feat: return simple static html reponse
This commit is contained in:
parent
ab7087581e
commit
ed90eb566e
|
|
@ -0,0 +1,13 @@
|
|||
enum Method
|
||||
GET
|
||||
POST
|
||||
PUT
|
||||
PATCH
|
||||
DELETE
|
||||
HEAD
|
||||
OPTIONS
|
||||
CONNECT
|
||||
TRACE
|
||||
|
||||
NON_STANDARD
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
43
src/main.cr
43
src/main.cr
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue