webmachine 1.6.0 → 2.0.0.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|