143 lines
3.6 KiB
Crystal
143 lines
3.6 KiB
Crystal
require "./message"
|
|
require "./method"
|
|
require "uri"
|
|
require "json"
|
|
|
|
class Message
|
|
|
|
@@status_line_regex = /(?'method'[A-Z]+) (?'uri'[^ ]+) (?'protocol'(?'protocol_name'[^ ]+)\/(?'protocol_version'[0-9]+.[0-9]+))/
|
|
@@header_line_regex = /^(?'name'[^:]+): (?'values'.+)/
|
|
|
|
@protocol_name = "HTTP"
|
|
@protocol_version = "1.0"
|
|
@method : Method = Method::NON_STANDARD
|
|
@uri : String = "/"
|
|
|
|
@headers : Hash(String, String) = {} of String => String
|
|
@body : String = ""
|
|
|
|
def parse_message(request : String)
|
|
protocol_name = Nil
|
|
protocol_version = Nil
|
|
method = Method::NON_STANDARD
|
|
uri = "/"
|
|
headers = {} of String => String
|
|
body = ""
|
|
|
|
current_header = ""
|
|
is_body = false
|
|
is_status_line = true
|
|
|
|
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"])
|
|
uri = regexified_status_line["uri"]
|
|
protocol_name = regexified_status_line["protocol_name"]
|
|
protocol_version = regexified_status_line["protocol_version"]
|
|
end
|
|
current_header = ""
|
|
end
|
|
end
|
|
|
|
return {
|
|
"protocol_name" => protocol_name,
|
|
"protocol_version" => protocol_version,
|
|
"method" => method,
|
|
"uri" => uri,
|
|
"headers" => headers,
|
|
"body" => body
|
|
}
|
|
end
|
|
|
|
def parse_form_body(body : String)
|
|
form_body = Hash(String, String).new()
|
|
|
|
key = ""
|
|
value = ""
|
|
is_key = true
|
|
body.chars.each do |char|
|
|
if char == '='
|
|
is_key = false
|
|
next
|
|
end
|
|
|
|
if char == '&'
|
|
form_body[key] = value[0, value.size - 1]
|
|
key = ""
|
|
value = ""
|
|
next
|
|
end
|
|
|
|
if is_key
|
|
key += char
|
|
else
|
|
value += char
|
|
end
|
|
end
|
|
form_body[key] = value
|
|
return form_body
|
|
end
|
|
|
|
def is_form_body
|
|
return "application/x-www-form-urlencoded" == @headers["Content-Type"]?
|
|
end
|
|
|
|
def is_json_body
|
|
return "application/json" == @headers["Content-Type"]?
|
|
end
|
|
|
|
def get_body
|
|
if is_json_body
|
|
return Hash(String, JSON::Any).from_json @body
|
|
elsif is_form_body
|
|
return parse_form_body @body
|
|
end
|
|
end
|
|
|
|
def header(header : String)
|
|
return @headers[header]?
|
|
end
|
|
|
|
def headers
|
|
return @headers
|
|
end
|
|
|
|
def body : String
|
|
return @body
|
|
end
|
|
|
|
def method : Method
|
|
return @method
|
|
end
|
|
|
|
def uri: String
|
|
return @uri
|
|
end
|
|
end
|