webmachine 1.6.0 → 2.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +2 -5
- data/documentation/adapters.md +2 -11
- data/examples/debugger.rb +5 -3
- data/examples/logging.rb +2 -2
- data/examples/webrick.rb +2 -2
- data/lib/webmachine/adapter.rb +0 -3
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -1
- data/lib/webmachine/adapters/rack.rb +38 -34
- data/lib/webmachine/adapters/rack_mapped.rb +1 -2
- data/lib/webmachine/adapters/webrick.rb +11 -12
- data/lib/webmachine/adapters.rb +0 -2
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +1 -1
- data/lib/webmachine/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +12 -12
- data/lib/webmachine/cookie.rb +20 -18
- data/lib/webmachine/decision/conneg.rb +24 -24
- data/lib/webmachine/decision/falsey.rb +0 -1
- data/lib/webmachine/decision/flow.rb +19 -20
- data/lib/webmachine/decision/fsm.rb +4 -4
- data/lib/webmachine/decision/helpers.rb +21 -21
- data/lib/webmachine/dispatcher/route.rb +28 -28
- data/lib/webmachine/dispatcher.rb +3 -2
- data/lib/webmachine/errors.rb +7 -8
- data/lib/webmachine/etags.rb +2 -1
- data/lib/webmachine/header_negotiation.rb +5 -6
- data/lib/webmachine/headers.rb +5 -5
- data/lib/webmachine/media_type.rb +5 -5
- data/lib/webmachine/quoted_string.rb +3 -3
- data/lib/webmachine/request.rb +7 -10
- data/lib/webmachine/rescueable_exception.rb +3 -3
- data/lib/webmachine/resource/authentication.rb +3 -4
- data/lib/webmachine/resource/callbacks.rb +3 -3
- data/lib/webmachine/resource/encodings.rb +3 -9
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/response.rb +7 -9
- data/lib/webmachine/spec/adapter_lint.rb +67 -69
- data/lib/webmachine/spec/test_resource.rb +22 -22
- data/lib/webmachine/streaming/encoder.rb +3 -2
- data/lib/webmachine/streaming/io_encoder.rb +4 -3
- data/lib/webmachine/trace/fsm.rb +25 -18
- data/lib/webmachine/trace/resource_proxy.rb +10 -9
- data/lib/webmachine/trace/static/http-headers-status-v3.png +0 -0
- data/lib/webmachine/trace/trace_resource.rb +22 -24
- data/lib/webmachine/trace.rb +7 -6
- data/lib/webmachine/translation.rb +3 -3
- data/lib/webmachine/version.rb +1 -1
- metadata +52 -86
- data/.gitignore +0 -31
- data/Gemfile +0 -46
- data/Guardfile +0 -11
- data/RELEASING.md +0 -21
- data/Rakefile +0 -44
- data/lib/webmachine/adapters/httpkit.rb +0 -74
- data/lib/webmachine/adapters/reel.rb +0 -113
- data/memory_test.rb +0 -37
- data/spec/spec_helper.rb +0 -56
- data/spec/webmachine/adapter_spec.rb +0 -39
- data/spec/webmachine/adapters/httpkit_spec.rb +0 -10
- data/spec/webmachine/adapters/rack_mapped_spec.rb +0 -71
- data/spec/webmachine/adapters/rack_spec.rb +0 -62
- data/spec/webmachine/adapters/reel_spec.rb +0 -76
- data/spec/webmachine/adapters/webrick_spec.rb +0 -12
- data/spec/webmachine/application_spec.rb +0 -74
- data/spec/webmachine/chunked_body_spec.rb +0 -30
- data/spec/webmachine/configuration_spec.rb +0 -27
- data/spec/webmachine/cookie_spec.rb +0 -99
- data/spec/webmachine/decision/conneg_spec.rb +0 -166
- data/spec/webmachine/decision/falsey_spec.rb +0 -8
- data/spec/webmachine/decision/flow_spec.rb +0 -1148
- data/spec/webmachine/decision/fsm_spec.rb +0 -163
- data/spec/webmachine/decision/helpers_spec.rb +0 -216
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +0 -22
- data/spec/webmachine/dispatcher/route_spec.rb +0 -248
- data/spec/webmachine/dispatcher_spec.rb +0 -104
- data/spec/webmachine/errors_spec.rb +0 -13
- data/spec/webmachine/etags_spec.rb +0 -75
- data/spec/webmachine/events_spec.rb +0 -58
- data/spec/webmachine/headers_spec.rb +0 -99
- data/spec/webmachine/media_type_spec.rb +0 -85
- data/spec/webmachine/request_spec.rb +0 -273
- data/spec/webmachine/rescueable_exception_spec.rb +0 -15
- data/spec/webmachine/resource/authentication_spec.rb +0 -68
- data/spec/webmachine/response_spec.rb +0 -51
- data/spec/webmachine/trace/fsm_spec.rb +0 -37
- data/spec/webmachine/trace/resource_proxy_spec.rb +0 -34
- data/spec/webmachine/trace/trace_store_spec.rb +0 -29
- data/spec/webmachine/trace_spec.rb +0 -17
- data/webmachine.gemspec +0 -25
@@ -14,7 +14,7 @@ module Webmachine
|
|
14
14
|
# appropriate media type.
|
15
15
|
# @api private
|
16
16
|
def choose_media_type(provided, header)
|
17
|
-
types = Array(header).map{|h| h.split(SPLIT_COMMA) }.flatten
|
17
|
+
types = Array(header).map { |h| h.split(SPLIT_COMMA) }.flatten
|
18
18
|
requested = MediaTypeList.build(types)
|
19
19
|
provided = provided.map do |p| # normalize_provided
|
20
20
|
MediaType.parse(p)
|
@@ -43,7 +43,7 @@ module Webmachine
|
|
43
43
|
# @api private
|
44
44
|
def choose_charset(provided, header)
|
45
45
|
if provided && !provided.empty?
|
46
|
-
charsets = provided.map {|c| c.first }
|
46
|
+
charsets = provided.map { |c| c.first }
|
47
47
|
if charset = do_choose(charsets, header, HAS_ENCODING ? Encoding.default_external.name : kcode_charset)
|
48
48
|
metadata[CHARSET] = charset
|
49
49
|
end
|
@@ -62,17 +62,17 @@ module Webmachine
|
|
62
62
|
any_ok = star_priority && star_priority > 0.0
|
63
63
|
accepted = requested.find do |priority, range|
|
64
64
|
if priority == 0.0
|
65
|
-
provided.delete_if {|tag| language_match(range, tag) }
|
65
|
+
provided.delete_if { |tag| language_match(range, tag) }
|
66
66
|
false
|
67
67
|
else
|
68
|
-
provided.any? {|tag| language_match(range, tag) }
|
68
|
+
provided.any? { |tag| language_match(range, tag) }
|
69
69
|
end
|
70
70
|
end
|
71
71
|
chosen = if accepted
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
provided.find { |tag| language_match(accepted.last, tag) }
|
73
|
+
elsif any_ok
|
74
|
+
provided.first
|
75
|
+
end
|
76
76
|
if chosen
|
77
77
|
metadata['Language'] = chosen
|
78
78
|
response.headers['Content-Language'] = chosen
|
@@ -91,14 +91,14 @@ module Webmachine
|
|
91
91
|
# is "-".
|
92
92
|
# @api private
|
93
93
|
def language_match(range, tag)
|
94
|
-
range.downcase == tag.downcase || tag =~ /^#{Regexp.escape(range)}
|
94
|
+
range.downcase == tag.downcase || tag =~ /^#{Regexp.escape(range)}-/i
|
95
95
|
end
|
96
96
|
|
97
97
|
# Makes an conneg choice based what is accepted and what is
|
98
98
|
# provided.
|
99
99
|
# @api private
|
100
100
|
def do_choose(choices, header, default)
|
101
|
-
choices = choices.dup.map {|s| s.downcase }
|
101
|
+
choices = choices.dup.map { |s| s.downcase }
|
102
102
|
accepted = PriorityList.build(header.split(SPLIT_COMMA))
|
103
103
|
default_priority = accepted.priority_of(default)
|
104
104
|
star_priority = accepted.priority_of(STAR)
|
@@ -112,12 +112,13 @@ module Webmachine
|
|
112
112
|
choices.include?(acceptable.downcase)
|
113
113
|
end
|
114
114
|
end
|
115
|
-
|
115
|
+
chosen&.last || # Use the matching one
|
116
116
|
(any_ok && choices.first) || # Or first if "*"
|
117
117
|
(default_ok && choices.include?(default) && default) # Or default
|
118
118
|
end
|
119
119
|
|
120
120
|
private
|
121
|
+
|
121
122
|
# Matches acceptable items that include 'q' values
|
122
123
|
CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s*$/.freeze
|
123
124
|
|
@@ -138,13 +139,13 @@ module Webmachine
|
|
138
139
|
def kcode_charset
|
139
140
|
case $KCODE
|
140
141
|
when /^U/i
|
141
|
-
|
142
|
+
'UTF-8'
|
142
143
|
when /^S/i
|
143
|
-
|
144
|
+
'Shift-JIS'
|
144
145
|
when /^B/i
|
145
|
-
|
146
|
-
else #when /^A/i, nil
|
147
|
-
|
146
|
+
'Big5'
|
147
|
+
else # when /^A/i, nil
|
148
|
+
'ASCII'
|
148
149
|
end
|
149
150
|
end
|
150
151
|
|
@@ -157,7 +158,7 @@ module Webmachine
|
|
157
158
|
# Given an acceptance list, create a PriorityList from them.
|
158
159
|
def self.build(list)
|
159
160
|
new.tap do |plist|
|
160
|
-
list.each {|item| plist.add_header_val(item) }
|
161
|
+
list.each { |item| plist.add_header_val(item) }
|
161
162
|
end
|
162
163
|
end
|
163
164
|
|
@@ -166,7 +167,7 @@ module Webmachine
|
|
166
167
|
# Creates a {PriorityList}.
|
167
168
|
# @see PriorityList::build
|
168
169
|
def initialize
|
169
|
-
@hash = Hash.new {|h,k| h[k] = [] }
|
170
|
+
@hash = Hash.new { |h, k| h[k] = [] }
|
170
171
|
@index = {}
|
171
172
|
end
|
172
173
|
|
@@ -185,8 +186,8 @@ module Webmachine
|
|
185
186
|
def add_header_val(c)
|
186
187
|
if c =~ CONNEG_REGEX
|
187
188
|
choice, q = $1, $2
|
188
|
-
q =
|
189
|
-
add(q.to_f,choice)
|
189
|
+
q = '0' << q if /^\./.match?(q) # handle strange FeedBurner Accept
|
190
|
+
add(q.to_f, choice)
|
190
191
|
else
|
191
192
|
add(1.0, c)
|
192
193
|
end
|
@@ -212,8 +213,8 @@ module Webmachine
|
|
212
213
|
# @yieldparam [Float] q the acceptable item's priority
|
213
214
|
# @yieldparam [String] v the acceptable item
|
214
215
|
def each
|
215
|
-
@hash.to_a.sort.reverse_each do |q,l|
|
216
|
-
l.each {|v| yield q, v }
|
216
|
+
@hash.to_a.sort.reverse_each do |q, l|
|
217
|
+
l.each { |v| yield q, v }
|
217
218
|
end
|
218
219
|
end
|
219
220
|
end
|
@@ -232,10 +233,9 @@ module Webmachine
|
|
232
233
|
q = mt.params.delete('q') || 1.0
|
233
234
|
add(q.to_f, mt)
|
234
235
|
rescue ArgumentError
|
235
|
-
raise MalformedRequest, t('invalid_media_type', :
|
236
|
+
raise MalformedRequest, t('invalid_media_type', type: c)
|
236
237
|
end
|
237
238
|
end
|
238
|
-
|
239
239
|
end # module Conneg
|
240
240
|
end # module Decision
|
241
241
|
end # module Webmachine
|
@@ -64,7 +64,7 @@ module Webmachine
|
|
64
64
|
if resource.allowed_methods.include?(request.method)
|
65
65
|
:b9
|
66
66
|
else
|
67
|
-
response.headers[
|
67
|
+
response.headers['Allow'] = resource.allowed_methods.join(', ')
|
68
68
|
405
|
69
69
|
end
|
70
70
|
end
|
@@ -82,13 +82,13 @@ module Webmachine
|
|
82
82
|
when true
|
83
83
|
:b9b
|
84
84
|
when false
|
85
|
-
response.body =
|
85
|
+
response.body = 'Content-MD5 header does not match request body.'
|
86
86
|
400
|
87
87
|
else # not_validated
|
88
88
|
if decode64(request.content_md5) == Digest::MD5.hexdigest(request.body)
|
89
89
|
:b9b
|
90
90
|
else
|
91
|
-
response.body =
|
91
|
+
response.body = 'Content-MD5 header does not match request body.'
|
92
92
|
400
|
93
93
|
end
|
94
94
|
end
|
@@ -123,7 +123,7 @@ module Webmachine
|
|
123
123
|
CONTENT = /content-/.freeze
|
124
124
|
# Okay Content-* Headers?
|
125
125
|
def b6
|
126
|
-
decision_test(resource.valid_content_headers?(request.headers.grep(CONTENT)), :b5,
|
126
|
+
decision_test(resource.valid_content_headers?(request.headers.grep(CONTENT)), :b5, 501)
|
127
127
|
end
|
128
128
|
|
129
129
|
# Known Content-Type?
|
@@ -158,7 +158,7 @@ module Webmachine
|
|
158
158
|
|
159
159
|
# Acceptable media type available?
|
160
160
|
def c4
|
161
|
-
types = resource.content_types_provided.map {|pair| pair.first }
|
161
|
+
types = resource.content_types_provided.map { |pair| pair.first }
|
162
162
|
chosen_type = choose_media_type(types, request.accept)
|
163
163
|
if !chosen_type
|
164
164
|
406
|
@@ -215,7 +215,7 @@ module Webmachine
|
|
215
215
|
end
|
216
216
|
response.headers[CONTENT_TYPE] = chosen_type.to_s
|
217
217
|
if !request.accept_encoding
|
218
|
-
choose_encoding(resource.encodings_provided,
|
218
|
+
choose_encoding(resource.encodings_provided, 'identity;q=1.0,*;q=0.5') ? :g7 : 406
|
219
219
|
else
|
220
220
|
:f7
|
221
221
|
end
|
@@ -229,7 +229,7 @@ module Webmachine
|
|
229
229
|
# Resource exists?
|
230
230
|
def g7
|
231
231
|
# This is the first place after all conneg, so set Vary here
|
232
|
-
response.headers['Vary'] =
|
232
|
+
response.headers['Vary'] = variances.join(', ') if variances.any?
|
233
233
|
decision_test(resource.resource_exists?, :g8, :h7)
|
234
234
|
end
|
235
235
|
|
@@ -240,12 +240,12 @@ module Webmachine
|
|
240
240
|
|
241
241
|
# If-Match: * exists?
|
242
242
|
def g9
|
243
|
-
quote(request.if_match) == '"*"' ? :h10 : :g11
|
243
|
+
(quote(request.if_match) == '"*"') ? :h10 : :g11
|
244
244
|
end
|
245
245
|
|
246
246
|
# ETag in If-Match
|
247
247
|
def g11
|
248
|
-
request_etags = request.if_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
|
248
|
+
request_etags = request.if_match.split(SPLIT_COMMA).map { |etag| ETag.new(etag) }
|
249
249
|
request_etags.include?(ETag.new(resource.generate_etag)) ? :h10 : 412
|
250
250
|
end
|
251
251
|
|
@@ -271,7 +271,7 @@ module Webmachine
|
|
271
271
|
|
272
272
|
# Last-Modified > I-UM-S?
|
273
273
|
def h12
|
274
|
-
resource.last_modified > metadata['If-Unmodified-Since'] ? 412 : :i12
|
274
|
+
(resource.last_modified > metadata['If-Unmodified-Since']) ? 412 : :i12
|
275
275
|
end
|
276
276
|
|
277
277
|
# Moved permanently? (apply PUT to different URI)
|
@@ -299,7 +299,7 @@ module Webmachine
|
|
299
299
|
|
300
300
|
# If-none-match: * exists?
|
301
301
|
def i13
|
302
|
-
quote(request.if_none_match) == '"*"' ? :j18 : :k13
|
302
|
+
(quote(request.if_none_match) == '"*"') ? :j18 : :k13
|
303
303
|
end
|
304
304
|
|
305
305
|
# GET or HEAD?
|
@@ -327,10 +327,10 @@ module Webmachine
|
|
327
327
|
|
328
328
|
# Etag in if-none-match?
|
329
329
|
def k13
|
330
|
-
request_etags = request.if_none_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
|
330
|
+
request_etags = request.if_none_match.split(SPLIT_COMMA).map { |etag| ETag.new(etag) }
|
331
331
|
resource_etag = resource.generate_etag
|
332
332
|
if resource_etag && request_etags.include?(ETag.new(resource_etag))
|
333
|
-
|
333
|
+
:j18
|
334
334
|
else
|
335
335
|
:l13
|
336
336
|
end
|
@@ -371,12 +371,12 @@ module Webmachine
|
|
371
371
|
|
372
372
|
# IMS > Now?
|
373
373
|
def l15
|
374
|
-
metadata['If-Modified-Since'] > Time.now ? :m16 : :l17
|
374
|
+
(metadata['If-Modified-Since'] > Time.now) ? :m16 : :l17
|
375
375
|
end
|
376
376
|
|
377
377
|
# Last-Modified > IMS?
|
378
378
|
def l17
|
379
|
-
resource.last_modified.nil? || resource.last_modified > metadata['If-Modified-Since'] ? :m16 : 304
|
379
|
+
(resource.last_modified.nil? || resource.last_modified > metadata['If-Modified-Since']) ? :m16 : 304
|
380
380
|
end
|
381
381
|
|
382
382
|
# POST?
|
@@ -415,7 +415,7 @@ module Webmachine
|
|
415
415
|
if resource.post_is_create?
|
416
416
|
case uri = resource.create_path
|
417
417
|
when nil
|
418
|
-
raise InvalidResource, t('create_path_nil', :
|
418
|
+
raise InvalidResource, t('create_path_nil', class: resource.class)
|
419
419
|
when URI, String
|
420
420
|
base_uri = resource.base_uri || request.base_uri
|
421
421
|
new_uri = URI.join(base_uri.to_s, uri)
|
@@ -431,7 +431,7 @@ module Webmachine
|
|
431
431
|
when Integer
|
432
432
|
return result
|
433
433
|
else
|
434
|
-
raise InvalidResource, t('process_post_invalid', :
|
434
|
+
raise InvalidResource, t('process_post_invalid', result: result.inspect)
|
435
435
|
end
|
436
436
|
end
|
437
437
|
if response.is_redirect?
|
@@ -471,7 +471,7 @@ module Webmachine
|
|
471
471
|
if request.get? || request.head?
|
472
472
|
add_caching_headers
|
473
473
|
content_type = metadata[CONTENT_TYPE]
|
474
|
-
handler = resource.content_types_provided.find {|ct, _| content_type.type_matches?(MediaType.parse(ct)) }.last
|
474
|
+
handler = resource.content_types_provided.find { |ct, _| content_type.type_matches?(MediaType.parse(ct)) }.last
|
475
475
|
result = resource.send(handler)
|
476
476
|
if Integer === result
|
477
477
|
result
|
@@ -507,9 +507,8 @@ module Webmachine
|
|
507
507
|
|
508
508
|
# New resource?
|
509
509
|
def p11
|
510
|
-
!response.headers[LOCATION] ? :o20 : 201
|
510
|
+
(!response.headers[LOCATION]) ? :o20 : 201
|
511
511
|
end
|
512
|
-
|
513
512
|
end # module Flow
|
514
513
|
end # module Decision
|
515
514
|
end # module Webmachine
|
@@ -36,11 +36,11 @@ module Webmachine
|
|
36
36
|
when Symbol # Next state
|
37
37
|
state = result
|
38
38
|
else # You bwoke it
|
39
|
-
raise InvalidResource, t('fsm_broke', :
|
39
|
+
raise InvalidResource, t('fsm_broke', state: state, result: result.inspect)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
rescue => e
|
43
|
-
Webmachine.render_error(500, request, response, :
|
43
|
+
Webmachine.render_error(500, request, response, message: e.message)
|
44
44
|
ensure
|
45
45
|
trace_response(response)
|
46
46
|
end
|
@@ -53,11 +53,11 @@ module Webmachine
|
|
53
53
|
resource.handle_exception(e)
|
54
54
|
500
|
55
55
|
rescue MalformedRequest => e
|
56
|
-
Webmachine.render_error(400, request, response, :
|
56
|
+
Webmachine.render_error(400, request, response, message: e.message)
|
57
57
|
400
|
58
58
|
end
|
59
59
|
|
60
|
-
def respond(code, headers={})
|
60
|
+
def respond(code, headers = {})
|
61
61
|
response.code = code
|
62
62
|
response.headers.merge!(headers)
|
63
63
|
case code
|
@@ -31,24 +31,24 @@ module Webmachine
|
|
31
31
|
body = response.body
|
32
32
|
chosen_charset = metadata[CHARSET]
|
33
33
|
chosen_encoding = metadata[CONTENT_ENCODING]
|
34
|
-
charsetter = resource.charsets_provided
|
34
|
+
charsetter = resource.charsets_provided&.find { |c, _| c == chosen_charset }&.last || :charset_nop
|
35
35
|
encoder = resource.encodings_provided[chosen_encoding]
|
36
36
|
response.body = case body
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
37
|
+
when String # 1.8 treats Strings as Enumerable
|
38
|
+
resource.send(encoder, resource.send(charsetter, body))
|
39
|
+
when IO, StringIO
|
40
|
+
IOEncoder.new(resource, encoder, charsetter, body)
|
41
|
+
when Fiber
|
42
|
+
FiberEncoder.new(resource, encoder, charsetter, body)
|
43
|
+
when Enumerable
|
44
|
+
EnumerableEncoder.new(resource, encoder, charsetter, body)
|
45
|
+
else
|
46
|
+
if body.respond_to?(:call)
|
47
|
+
CallableEncoder.new(resource, encoder, charsetter, body)
|
48
|
+
else
|
49
|
+
resource.send(encoder, resource.send(charsetter, body))
|
50
|
+
end
|
51
|
+
end
|
52
52
|
if body_is_fixed_length?
|
53
53
|
ensure_content_length(response)
|
54
54
|
else
|
@@ -60,7 +60,7 @@ module Webmachine
|
|
60
60
|
# Assists in receiving request bodies
|
61
61
|
def accept_helper
|
62
62
|
content_type = MediaType.parse(request.content_type || 'application/octet-stream')
|
63
|
-
acceptable = resource.content_types_accepted.find {|ct, _| content_type.match?(ct) }
|
63
|
+
acceptable = resource.content_types_accepted.find { |ct, _| content_type.match?(ct) }
|
64
64
|
if acceptable
|
65
65
|
resource.send(acceptable.last)
|
66
66
|
else
|
@@ -71,10 +71,10 @@ module Webmachine
|
|
71
71
|
# Computes the entries for the 'Vary' response header
|
72
72
|
def variances
|
73
73
|
resource.variances.tap do |v|
|
74
|
-
v.unshift
|
75
|
-
v.unshift
|
76
|
-
v.unshift
|
77
|
-
v.unshift
|
74
|
+
v.unshift 'Accept-Language' if resource.languages_provided.size > 1
|
75
|
+
v.unshift 'Accept-Charset' if resource.charsets_provided && resource.charsets_provided.size > 1
|
76
|
+
v.unshift 'Accept-Encoding' if resource.encodings_provided.size > 1
|
77
|
+
v.unshift 'Accept' if resource.content_types_provided.size > 1
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -28,6 +28,21 @@ module Webmachine
|
|
28
28
|
# String version of MATCH_ALL, deprecated. Use the symbol instead.
|
29
29
|
MATCH_ALL_STR = '*'.freeze
|
30
30
|
|
31
|
+
# Decode a string using the scheme described in RFC 3986 2.1. Percent-Encoding (https://www.ietf.org/rfc/rfc3986.txt)
|
32
|
+
def self.rfc3986_percent_decode(value)
|
33
|
+
s = StringScanner.new(value)
|
34
|
+
result = ''
|
35
|
+
until s.eos?
|
36
|
+
encoded_val = s.scan(/%([0-9a-fA-F]){2}/)
|
37
|
+
result << if encoded_val.nil?
|
38
|
+
s.getch
|
39
|
+
else
|
40
|
+
[encoded_val[1..-1]].pack('H*')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
31
46
|
# Creates a new Route that will associate a pattern to a
|
32
47
|
# {Resource}.
|
33
48
|
#
|
@@ -58,25 +73,25 @@ module Webmachine
|
|
58
73
|
# @yield [req] an optional guard block
|
59
74
|
# @yieldparam [Request] req the request object
|
60
75
|
# @see Dispatcher#add_route
|
61
|
-
def initialize(path_spec, *args)
|
62
|
-
if args.last.is_a? Hash
|
63
|
-
|
76
|
+
def initialize(path_spec, *args, &block)
|
77
|
+
bindings = if args.last.is_a? Hash
|
78
|
+
args.pop
|
64
79
|
else
|
65
|
-
|
80
|
+
{}
|
66
81
|
end
|
67
82
|
|
68
83
|
resource = args.pop
|
69
84
|
guards = args
|
70
|
-
guards <<
|
85
|
+
guards << block if block
|
71
86
|
|
72
87
|
warn t('match_all_symbol') if path_spec.include? MATCH_ALL_STR
|
73
88
|
|
74
89
|
@path_spec = path_spec
|
75
|
-
@guards
|
76
|
-
@resource
|
77
|
-
@bindings
|
90
|
+
@guards = guards
|
91
|
+
@resource = resource
|
92
|
+
@bindings = bindings
|
78
93
|
|
79
|
-
raise ArgumentError, t('not_resource_class', :
|
94
|
+
raise ArgumentError, t('not_resource_class', class: resource.name) unless resource < Resource
|
80
95
|
end
|
81
96
|
|
82
97
|
# Determines whether the given request matches this route and
|
@@ -94,11 +109,12 @@ module Webmachine
|
|
94
109
|
request.disp_path = request.routing_tokens.join(SLASH)
|
95
110
|
request.path_info = @bindings.dup
|
96
111
|
tokens = request.routing_tokens
|
97
|
-
|
112
|
+
_depth, trailing = bind(tokens, request.path_info)
|
98
113
|
request.path_tokens = trailing || []
|
99
114
|
end
|
100
115
|
|
101
116
|
private
|
117
|
+
|
102
118
|
# Attempts to match the path spec against the path tokens, while
|
103
119
|
# accumulating variable bindings.
|
104
120
|
# @param [Array<String>] tokens the list of path segments
|
@@ -125,9 +141,8 @@ module Webmachine
|
|
125
141
|
if spec.first.named_captures.empty?
|
126
142
|
bindings[:captures] = (bindings[:captures] || []) + matches.captures
|
127
143
|
else
|
128
|
-
spec.first.named_captures.
|
129
|
-
bindings[name.to_sym] = matches.captures[idxs.first-1]
|
130
|
-
bindings
|
144
|
+
spec.first.named_captures.each_with_object(bindings) do |(name, idxs), bindings|
|
145
|
+
bindings[name.to_sym] = matches.captures[idxs.first - 1]
|
131
146
|
end
|
132
147
|
end
|
133
148
|
else
|
@@ -144,21 +159,6 @@ module Webmachine
|
|
144
159
|
depth += 1
|
145
160
|
end
|
146
161
|
end
|
147
|
-
|
148
|
-
# Decode a string using the scheme described in RFC 3986 2.1. Percent-Encoding (https://www.ietf.org/rfc/rfc3986.txt)
|
149
|
-
def self.rfc3986_percent_decode(value)
|
150
|
-
s = StringScanner.new(value)
|
151
|
-
result = ''
|
152
|
-
until s.eos?
|
153
|
-
encoded_val = s.scan(/%([0-9a-fA-F]){2}/)
|
154
|
-
result << if encoded_val.nil?
|
155
|
-
s.getch
|
156
|
-
else
|
157
|
-
[encoded_val[1..-1]].pack('H*')
|
158
|
-
end
|
159
|
-
end
|
160
|
-
result
|
161
|
-
end
|
162
162
|
end # class Route
|
163
163
|
end # module Dispatcher
|
164
164
|
end # module Webmachine
|
@@ -33,7 +33,7 @@ module Webmachine
|
|
33
33
|
@routes << route
|
34
34
|
route
|
35
35
|
end
|
36
|
-
|
36
|
+
alias_method :add, :add_route
|
37
37
|
|
38
38
|
# Dispatches a request to the appropriate {Resource} in the
|
39
39
|
# dispatch list. If a matching resource is not found, a "404 Not
|
@@ -72,10 +72,11 @@ module Webmachine
|
|
72
72
|
# Find the first route that matches an incoming request
|
73
73
|
# @param [Request] request the request to match
|
74
74
|
def find_route(request)
|
75
|
-
@routes.find {|r| r.match?(request) }
|
75
|
+
@routes.find { |r| r.match?(request) }
|
76
76
|
end
|
77
77
|
|
78
78
|
private
|
79
|
+
|
79
80
|
def prepare_resource(route, request, response)
|
80
81
|
route.apply(request)
|
81
82
|
@resource_creator.call(route, request, response)
|
data/lib/webmachine/errors.rb
CHANGED
@@ -14,23 +14,22 @@ module Webmachine
|
|
14
14
|
# @param [Response] req the response object
|
15
15
|
# @param [Hash] options keys to override the defaults when rendering
|
16
16
|
# the response body
|
17
|
-
def self.render_error(code, req, res, options={})
|
17
|
+
def self.render_error(code, req, res, options = {})
|
18
18
|
res.code = code
|
19
19
|
unless res.body
|
20
20
|
title, message = t(["errors.#{code}.title", "errors.#{code}.message"],
|
21
|
-
|
22
|
-
|
23
|
-
res.body = t(
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
{method: req.method,
|
22
|
+
error: res.error}.merge(options))
|
23
|
+
res.body = t('errors.standard_body',
|
24
|
+
{title: title,
|
25
|
+
message: message,
|
26
|
+
version: Webmachine::SERVER_STRING}.merge(options))
|
27
27
|
res.headers[CONTENT_TYPE] = TEXT_HTML
|
28
28
|
end
|
29
29
|
ensure_content_length(res)
|
30
30
|
ensure_date_header(res)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
33
|
# Superclass of all errors generated by Webmachine.
|
35
34
|
class Error < ::StandardError; end
|
36
35
|
|
data/lib/webmachine/etags.rb
CHANGED
@@ -10,7 +10,7 @@ module Webmachine
|
|
10
10
|
|
11
11
|
def self.new(etag)
|
12
12
|
return etag if ETag === etag
|
13
|
-
klass = etag
|
13
|
+
klass = WEAK_ETAG.match?(etag) ? WeakETag : self
|
14
14
|
klass.send(:allocate).tap do |obj|
|
15
15
|
obj.send(:initialize, etag)
|
16
16
|
end
|
@@ -53,6 +53,7 @@ module Webmachine
|
|
53
53
|
end
|
54
54
|
|
55
55
|
private
|
56
|
+
|
56
57
|
def unquote(str)
|
57
58
|
if str =~ WEAK_ETAG
|
58
59
|
unescape_quotes $1
|
@@ -3,19 +3,18 @@
|
|
3
3
|
module Webmachine
|
4
4
|
module HeaderNegotiation
|
5
5
|
def ensure_date_header(res)
|
6
|
-
if (200..499).
|
6
|
+
if (200..499).cover?(res.code)
|
7
7
|
res.headers[DATE] ||= Time.now.httpdate
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def ensure_content_length(res)
|
12
12
|
body = res.body
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
when [204, 205, 304].include?(res.code)
|
13
|
+
if res.headers[TRANSFER_ENCODING]
|
14
|
+
nil
|
15
|
+
elsif [204, 205, 304].include?(res.code)
|
17
16
|
res.headers.delete CONTENT_LENGTH
|
18
|
-
|
17
|
+
elsif !body.nil?
|
19
18
|
res.headers[CONTENT_LENGTH] = body.respond_to?(:bytesize) ? body.bytesize.to_s : body.length.to_s
|
20
19
|
else
|
21
20
|
res.headers[CONTENT_LENGTH] = '0'
|
data/lib/webmachine/headers.rb
CHANGED
@@ -10,11 +10,10 @@ module Webmachine
|
|
10
10
|
# @param [Hash] env a hash of CGI-style env/headers
|
11
11
|
# @return [Webmachine::Headers]
|
12
12
|
def self.from_cgi(env)
|
13
|
-
env.
|
13
|
+
env.each_with_object(new) do |(k, v), h|
|
14
14
|
if k =~ CGI_HTTP_MATCH || k =~ CONTENT_TYPE_LENGTH_MATCH
|
15
15
|
h[$1.tr(UNDERSCORE, DASH)] = v
|
16
16
|
end
|
17
|
-
h
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
@@ -33,7 +32,7 @@ module Webmachine
|
|
33
32
|
# @param [Object]
|
34
33
|
# @return [Webmachine::Headers]
|
35
34
|
def self.[](*args)
|
36
|
-
super(super(*args).map {|k, v| [k.to_s.downcase, v]})
|
35
|
+
super(super(*args).map { |k, v| [k.to_s.downcase, v] })
|
37
36
|
end
|
38
37
|
|
39
38
|
# Fetch a header
|
@@ -42,7 +41,7 @@ module Webmachine
|
|
42
41
|
end
|
43
42
|
|
44
43
|
# Set a header
|
45
|
-
def []=(key,value)
|
44
|
+
def []=(key, value)
|
46
45
|
super transform_key(key), value
|
47
46
|
end
|
48
47
|
|
@@ -76,10 +75,11 @@ module Webmachine
|
|
76
75
|
|
77
76
|
# Select matching headers
|
78
77
|
def grep(pattern)
|
79
|
-
self.class[select { |k,_| pattern === k }]
|
78
|
+
self.class[select { |k, _| pattern === k }]
|
80
79
|
end
|
81
80
|
|
82
81
|
private
|
82
|
+
|
83
83
|
def transform_key(key)
|
84
84
|
key.to_s.downcase
|
85
85
|
end
|