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