From ed90eb566ec22098b770771c0634af8d5d07740c Mon Sep 17 00:00:00 2001 From: Superkooka Date: Sun, 28 Mar 2021 23:59:46 +0200 Subject: [PATCH] feat: parsing of headers feat: return simple static html reponse --- src/http/method.cr | 13 ++++++ src/http/request.cr | 99 ++++++++++++++++++++++++++++++++++++++++++++ src/http/response.cr | 22 ++++++++++ src/main.cr | 43 ++++++++++++++++++- static/hellow.html | 13 ++++++ 5 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/http/method.cr create mode 100644 src/http/request.cr create mode 100644 src/http/response.cr create mode 100644 static/hellow.html diff --git a/src/http/method.cr b/src/http/method.cr new file mode 100644 index 0000000..a96a70a --- /dev/null +++ b/src/http/method.cr @@ -0,0 +1,13 @@ +enum Method + GET + POST + PUT + PATCH + DELETE + HEAD + OPTIONS + CONNECT + TRACE + + NON_STANDARD +end \ No newline at end of file diff --git a/src/http/request.cr b/src/http/request.cr new file mode 100644 index 0000000..4f57d73 --- /dev/null +++ b/src/http/request.cr @@ -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 diff --git a/src/http/response.cr b/src/http/response.cr new file mode 100644 index 0000000..b66f143 --- /dev/null +++ b/src/http/response.cr @@ -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 diff --git a/src/main.cr b/src/main.cr index 8a035e9..f6cc909 100644 --- a/src/main.cr +++ b/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 diff --git a/static/hellow.html b/static/hellow.html new file mode 100644 index 0000000..435fda5 --- /dev/null +++ b/static/hellow.html @@ -0,0 +1,13 @@ + + + + + + + Wesh + + +

Hellow World !

+

Cimer Nuker pour la découverte 😉

+ + \ No newline at end of file