kemal/src/kemal/param_parser.cr

116 lines
3.2 KiB
Crystal
Raw Normal View History

2016-07-17 11:43:13 +00:00
module Kemal
2017-10-06 11:53:53 +00:00
# Parses the request contents including query_params and body
# and converts them into a params hash which you can use within
# the environment context.
2016-07-17 11:43:13 +00:00
class ParamParser
URL_ENCODED_FORM = "application/x-www-form-urlencoded"
APPLICATION_JSON = "application/json"
2017-02-11 13:33:42 +00:00
MULTIPART_FORM = "multipart/form-data"
2018-01-26 15:32:24 +00:00
PARTS = %w(url query body json)
2016-11-10 13:38:29 +00:00
# :nodoc:
alias AllParamTypes = Nil | String | Int64 | Float64 | Bool | Hash(String, JSON::Type) | Array(JSON::Type)
2017-02-11 13:33:42 +00:00
getter files
2016-07-17 11:43:13 +00:00
def initialize(@request : HTTP::Request)
@url = {} of String => String
@query = HTTP::Params.new({} of String => Array(String))
@body = HTTP::Params.new({} of String => Array(String))
@json = {} of String => AllParamTypes
2017-02-11 13:33:42 +00:00
@files = {} of String => FileUpload
2016-07-17 11:43:13 +00:00
@url_parsed = false
@query_parsed = false
@body_parsed = false
@json_parsed = false
end
2015-10-28 18:30:27 +00:00
private def unescape_url_param(value : String)
value.size == 0 ? value : URI.unescape(value)
rescue
value
end
2017-12-08 18:35:02 +00:00
{% for method in PARTS %}
2017-10-06 11:55:37 +00:00
def {{method.id}}
# check memoization
return @{{method.id}} if @{{method.id}}_parsed
2016-03-06 20:39:10 +00:00
2017-10-06 11:55:37 +00:00
parse_{{method.id}}
# memoize
@{{method.id}}_parsed = true
@{{method.id}}
end
2016-07-17 11:43:13 +00:00
{% end %}
2015-10-28 18:30:27 +00:00
2017-02-13 20:39:40 +00:00
private def parse_body
2017-09-04 15:54:06 +00:00
content_type = @request.content_type
2017-02-11 13:33:42 +00:00
return unless content_type
if content_type.try(&.starts_with?(URL_ENCODED_FORM))
@body = parse_part(@request.body)
return
end
if content_type.try(&.starts_with?(MULTIPART_FORM))
parse_file_upload
return
end
2016-07-17 11:43:13 +00:00
end
2017-02-13 20:39:40 +00:00
private def parse_query
2016-07-17 11:43:13 +00:00
@query = parse_part(@request.query)
end
2017-02-13 20:39:40 +00:00
private def parse_url
2016-07-17 11:43:13 +00:00
if params = @request.url_params
params.each do |key, value|
2017-09-05 09:03:37 +00:00
@url[key] = unescape_url_param(value)
2016-07-17 11:43:13 +00:00
end
2017-02-11 13:33:42 +00:00
end
end
2017-02-13 20:39:40 +00:00
private def parse_file_upload
HTTP::FormData.parse(@request) do |upload|
next unless upload
filename = upload.filename
2017-02-11 13:33:42 +00:00
if !filename.nil?
@files[upload.name] = FileUpload.new(upload: upload)
2017-02-11 13:33:42 +00:00
else
@body.add(upload.name, upload.body.gets_to_end)
2017-02-11 13:33:42 +00:00
end
2016-01-12 22:06:19 +00:00
end
end
2016-01-12 19:37:12 +00:00
2016-07-17 11:43:13 +00:00
# Parses JSON request body if Content-Type is `application/json`.
2017-10-06 11:53:53 +00:00
#
# - If request body is a JSON `Hash` then all the params are parsed and added into `params`.
# - If request body is a JSON `Array` it's added into `params` as `_json` and can be accessed like `params["_json"]`.
2017-02-13 20:39:40 +00:00
private def parse_json
return unless @request.body && @request.headers["Content-Type"]?.try(&.starts_with?(APPLICATION_JSON))
2015-11-10 11:30:16 +00:00
body = @request.body.not_nil!.gets_to_end
2016-07-17 11:43:13 +00:00
case json = JSON.parse(body).raw
when Hash
json.each do |key, value|
@json[key] = value.as(AllParamTypes)
2016-07-17 11:43:13 +00:00
end
when Array
@json["_json"] = json
2015-11-10 11:30:16 +00:00
end
2015-11-06 18:24:38 +00:00
end
2017-02-13 20:39:40 +00:00
private def parse_part(part : IO?)
if part
HTTP::Params.parse(part.gets_to_end)
else
HTTP::Params.parse("")
end
end
2017-02-13 20:39:40 +00:00
private def parse_part(part : String?)
if part
HTTP::Params.parse(part.to_s)
else
HTTP::Params.parse("")
end
2016-07-17 11:43:13 +00:00
end
end
2015-10-28 18:30:27 +00:00
end