1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
require 'rack/utils'
module Rack
# Rack::Request provides a convenient interface to a Rack
# environment. It is stateless, the environment +env+ passed to the
# constructor will be directly modified.
#
# req = Rack::Request.new(env)
# req.post?
# req.params["data"]
class Request
# The environment of the request.
attr_reader :env
def initialize(env)
@env = env
end
def body; @env["rack.input"] end
def scheme; @env["rack.url_scheme"] end
def script_name; @env["SCRIPT_NAME"].to_s end
def path_info; @env["PATH_INFO"].to_s end
def port; @env["SERVER_PORT"].to_i end
def request_method; @env["REQUEST_METHOD"] end
def query_string; @env["QUERY_STRING"].to_s end
def content_length; @env['CONTENT_LENGTH'] end
def content_type; @env['CONTENT_TYPE'] end
# The media type (type/subtype) portion of the CONTENT_TYPE header
# without any media type parameters. e.g., when CONTENT_TYPE is
# "text/plain;charset=utf-8", the media-type is "text/plain".
#
# For more information on the use of media types in HTTP, see:
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
def media_type
content_type && content_type.split(/\s*[;,]\s*/, 2)[0].downcase
end
# The media type parameters provided in CONTENT_TYPE as a Hash, or
# an empty Hash if no CONTENT_TYPE or media-type parameters were
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
# this method responds with the following Hash:
# { 'charset' => 'utf-8' }
def media_type_params
return {} if content_type.nil?
content_type.split(/\s*[;,]\s*/)[1..-1].
collect { |s| s.split('=', 2) }.
inject({}) { |hash,(k,v)| hash[k.downcase] = v ; hash }
end
# The character set of the request body if a "charset" media type
# parameter was given, or nil if no "charset" was specified. Note
# that, per RFC2616, text/* media types that specify no explicit
# charset are to be considered ISO-8859-1.
def content_charset
media_type_params['charset']
end
def host
# Remove port number.
(@env["HTTP_HOST"] || @env["SERVER_NAME"]).gsub(/:\d+\z/, '')
end
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
def path_info=(s); @env["PATH_INFO"] = s.to_s end
def get?; request_method == "GET" end
def post?; request_method == "POST" end
def put?; request_method == "PUT" end
def delete?; request_method == "DELETE" end
def head?; request_method == "HEAD" end
# The set of form-data media-types. Requests that do not indicate
# one of the media types presents in this list will not be eligible
# for form-data / param parsing.
FORM_DATA_MEDIA_TYPES = [
nil,
'application/x-www-form-urlencoded',
'multipart/form-data'
]
# Determine whether the request body contains form-data by checking
# the request media_type against registered form-data media-types:
# "application/x-www-form-urlencoded" and "multipart/form-data". The
# list of form-data media types can be modified through the
# +FORM_DATA_MEDIA_TYPES+ array.
def form_data?
FORM_DATA_MEDIA_TYPES.include?(media_type)
end
# Returns the data recieved in the query string.
def GET
if @env["rack.request.query_string"] == query_string
@env["rack.request.query_hash"]
else
@env["rack.request.query_string"] = query_string
@env["rack.request.query_hash"] =
Utils.parse_query(query_string)
end
end
# Returns the data recieved in the request body.
#
# This method support both application/x-www-form-urlencoded and
# multipart/form-data.
def POST
if @env["rack.request.form_input"].eql? @env["rack.input"]
@env["rack.request.form_hash"]
elsif form_data?
@env["rack.request.form_input"] = @env["rack.input"]
unless @env["rack.request.form_hash"] =
Utils::Multipart.parse_multipart(env)
@env["rack.request.form_vars"] = @env["rack.input"].read
@env["rack.request.form_hash"] = Utils.parse_query(@env["rack.request.form_vars"])
end
@env["rack.request.form_hash"]
else
{}
end
end
# The union of GET and POST data.
def params
self.GET.update(self.POST)
rescue EOFError => e
self.GET
end
# shortcut for request.params[key]
def [](key)
params[key.to_s]
end
# shortcut for request.params[key] = value
def []=(key, value)
params[key.to_s] = value
end
# like Hash#values_at
def values_at(*keys)
keys.map{|key| params[key] }
end
# the referer of the client or '/'
def referer
@env['HTTP_REFERER'] || '/'
end
alias referrer referer
def cookies
return {} unless @env["HTTP_COOKIE"]
if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
@env["rack.request.cookie_hash"]
else
@env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
# According to RFC 2109:
# If multiple cookies satisfy the criteria above, they are ordered in
# the Cookie header such that those with more specific Path attributes
# precede those with less specific. Ordering with respect to other
# attributes (e.g., Domain) is unspecified.
@env["rack.request.cookie_hash"] =
Utils.parse_query(@env["rack.request.cookie_string"], ';,').inject({}) {|h,(k,v)|
h[k] = Array === v ? v.first : v
h
}
end
end
def xhr?
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
end
# Tries to return a remake of the original request URL as a string.
def url
url = scheme + "://"
url << host
if scheme == "https" && port != 443 ||
scheme == "http" && port != 80
url << ":#{port}"
end
url << fullpath
url
end
def fullpath
path = script_name + path_info
path << "?" << query_string unless query_string.empty?
path
end
def accept_encoding
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/,\s*/).map do |part|
m = /^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$/.match(part) # From WEBrick
if m
[m[1], (m[2] || 1.0).to_f]
else
raise "Invalid value for Accept-Encoding: #{part.inspect}"
end
end
end
end
end
|