webmachine 1.2.2 → 1.6.0
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 +7 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +20 -15
- data/README.md +89 -91
- data/RELEASING.md +21 -0
- data/Rakefile +5 -21
- data/documentation/adapters.md +41 -0
- data/documentation/authentication-and-authorization.md +37 -0
- data/documentation/configurator.md +19 -0
- data/documentation/error-handling.md +86 -0
- data/documentation/examples.md +224 -0
- data/documentation/how-it-works.md +76 -0
- data/documentation/routes.md +112 -0
- data/documentation/validation.md +159 -0
- data/documentation/versioning-apis.md +74 -0
- data/documentation/visual-debugger.md +38 -0
- data/examples/application.rb +2 -2
- data/examples/debugger.rb +1 -1
- data/lib/webmachine.rb +3 -1
- data/lib/webmachine/adapter.rb +7 -13
- data/lib/webmachine/adapters.rb +1 -2
- data/lib/webmachine/adapters/httpkit.rb +74 -0
- data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
- data/lib/webmachine/adapters/rack.rb +70 -25
- data/lib/webmachine/adapters/rack_mapped.rb +42 -0
- data/lib/webmachine/adapters/reel.rb +22 -23
- data/lib/webmachine/adapters/webrick.rb +16 -16
- data/lib/webmachine/application.rb +2 -2
- data/lib/webmachine/chunked_body.rb +3 -4
- data/lib/webmachine/configuration.rb +1 -1
- data/lib/webmachine/constants.rb +75 -0
- data/lib/webmachine/decision/conneg.rb +12 -10
- data/lib/webmachine/decision/flow.rb +42 -32
- data/lib/webmachine/decision/fsm.rb +14 -21
- data/lib/webmachine/decision/helpers.rb +10 -38
- data/lib/webmachine/dispatcher.rb +13 -10
- data/lib/webmachine/dispatcher/route.rb +45 -9
- data/lib/webmachine/errors.rb +9 -3
- data/lib/webmachine/events.rb +2 -2
- data/lib/webmachine/header_negotiation.rb +25 -0
- data/lib/webmachine/headers.rb +8 -3
- data/lib/webmachine/locale/en.yml +7 -5
- data/lib/webmachine/media_type.rb +10 -8
- data/lib/webmachine/request.rb +67 -26
- data/lib/webmachine/rescueable_exception.rb +62 -0
- data/lib/webmachine/resource.rb +1 -1
- data/lib/webmachine/resource/callbacks.rb +11 -9
- data/lib/webmachine/response.rb +3 -5
- data/lib/webmachine/spec/IO_response.body +1 -0
- data/lib/webmachine/spec/adapter_lint.rb +83 -37
- data/lib/webmachine/spec/test_resource.rb +15 -4
- data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
- data/lib/webmachine/streaming/io_encoder.rb +7 -1
- data/lib/webmachine/trace.rb +1 -0
- data/lib/webmachine/trace/fsm.rb +20 -10
- data/lib/webmachine/trace/resource_proxy.rb +2 -0
- data/lib/webmachine/translation.rb +2 -1
- data/lib/webmachine/version.rb +3 -3
- data/memory_test.rb +37 -0
- data/spec/spec_helper.rb +17 -9
- data/spec/webmachine/adapter_spec.rb +14 -15
- data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
- data/spec/webmachine/adapters/rack_mapped_spec.rb +71 -0
- data/spec/webmachine/adapters/rack_spec.rb +32 -6
- data/spec/webmachine/adapters/reel_spec.rb +16 -12
- data/spec/webmachine/adapters/webrick_spec.rb +2 -2
- data/spec/webmachine/application_spec.rb +18 -17
- data/spec/webmachine/chunked_body_spec.rb +3 -3
- data/spec/webmachine/configuration_spec.rb +5 -5
- data/spec/webmachine/cookie_spec.rb +13 -13
- data/spec/webmachine/decision/conneg_spec.rb +49 -43
- data/spec/webmachine/decision/falsey_spec.rb +4 -4
- data/spec/webmachine/decision/flow_spec.rb +195 -145
- data/spec/webmachine/decision/fsm_spec.rb +81 -19
- data/spec/webmachine/decision/helpers_spec.rb +20 -20
- data/spec/webmachine/dispatcher/rfc3986_percent_decode_spec.rb +22 -0
- data/spec/webmachine/dispatcher/route_spec.rb +114 -32
- data/spec/webmachine/dispatcher_spec.rb +49 -24
- data/spec/webmachine/errors_spec.rb +1 -1
- data/spec/webmachine/etags_spec.rb +19 -19
- data/spec/webmachine/events_spec.rb +6 -6
- data/spec/webmachine/headers_spec.rb +14 -14
- data/spec/webmachine/media_type_spec.rb +36 -36
- data/spec/webmachine/request_spec.rb +70 -39
- data/spec/webmachine/rescueable_exception_spec.rb +15 -0
- data/spec/webmachine/resource/authentication_spec.rb +6 -6
- data/spec/webmachine/response_spec.rb +18 -12
- data/spec/webmachine/trace/fsm_spec.rb +8 -8
- data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
- data/spec/webmachine/trace/trace_store_spec.rb +5 -5
- data/spec/webmachine/trace_spec.rb +3 -3
- data/webmachine.gemspec +2 -6
- metadata +78 -228
- data/lib/webmachine/adapters/hatetepe.rb +0 -108
- data/lib/webmachine/adapters/mongrel.rb +0 -127
- data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
- data/lib/webmachine/fiber18.rb +0 -88
- data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
- data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
|
@@ -8,7 +8,7 @@ module Webmachine
|
|
|
8
8
|
#
|
|
9
9
|
# MyApp = Webmachine::Application.new do |app|
|
|
10
10
|
# app.routes do
|
|
11
|
-
# add [
|
|
11
|
+
# add [:*], AssetResource
|
|
12
12
|
# end
|
|
13
13
|
#
|
|
14
14
|
# app.configure do |config|
|
|
@@ -56,7 +56,7 @@ module Webmachine
|
|
|
56
56
|
# @return an instance of the configured web-server adapter
|
|
57
57
|
# @see Adapters
|
|
58
58
|
def adapter
|
|
59
|
-
@adapter ||= adapter_class.new(
|
|
59
|
+
@adapter ||= adapter_class.new(self)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# @return an instance of the configured web-server adapter
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'webmachine/constants'
|
|
2
|
+
|
|
1
3
|
module Webmachine
|
|
2
4
|
# {ChunkedBody} is used to wrap an {Enumerable} object (like an enumerable
|
|
3
5
|
# {Response#body}) so it yields proper chunks for chunked transfer encoding.
|
|
@@ -13,11 +15,8 @@ module Webmachine
|
|
|
13
15
|
#
|
|
14
16
|
# This is needed for Ruby webservers which don't do the chunking themselves.
|
|
15
17
|
class ChunkedBody
|
|
16
|
-
# Delimiter for chunked encoding
|
|
17
|
-
CRLF = "\r\n"
|
|
18
|
-
|
|
19
18
|
# Final chunk in any chunked-encoding response
|
|
20
|
-
FINAL_CHUNK = "0#{CRLF}#{CRLF}"
|
|
19
|
+
FINAL_CHUNK = "0#{CRLF}#{CRLF}".freeze
|
|
21
20
|
|
|
22
21
|
# Creates a new {ChunkedBody} from the given {Enumerable}.
|
|
23
22
|
# @param [Enumerable] body the enumerable response body
|
|
@@ -5,7 +5,7 @@ module Webmachine
|
|
|
5
5
|
# defaults will be filled in when {Webmachine::run} is called.
|
|
6
6
|
# @attr [String] ip the interface to bind to, defaults to "0.0.0.0"
|
|
7
7
|
# (all interfaces)
|
|
8
|
-
# @attr [
|
|
8
|
+
# @attr [Integer] port the port to bind to, defaults to 8080
|
|
9
9
|
# @attr [Symbol] adapter the adapter to use, defaults to :WEBrick
|
|
10
10
|
# @attr [Hash] adapter_options adapter-specific options, defaults to {}
|
|
11
11
|
Configuration = Struct.new(:ip, :port, :adapter, :adapter_options)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Webmachine
|
|
2
|
+
# Universal HTTP delimiter
|
|
3
|
+
CRLF = "\r\n".freeze
|
|
4
|
+
|
|
5
|
+
# HTTP Content-Type
|
|
6
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
|
7
|
+
|
|
8
|
+
# Default Content-Type
|
|
9
|
+
TEXT_HTML = 'text/html'.freeze
|
|
10
|
+
|
|
11
|
+
# HTTP Date
|
|
12
|
+
DATE = 'Date'.freeze
|
|
13
|
+
|
|
14
|
+
# HTTP Transfer-Encoding
|
|
15
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
|
|
16
|
+
|
|
17
|
+
# HTTP Content-Length
|
|
18
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
|
19
|
+
|
|
20
|
+
# A underscore
|
|
21
|
+
UNDERSCORE = '_'.freeze
|
|
22
|
+
|
|
23
|
+
# A dash
|
|
24
|
+
DASH = '-'.freeze
|
|
25
|
+
|
|
26
|
+
# A Slash
|
|
27
|
+
SLASH = '/'.freeze
|
|
28
|
+
|
|
29
|
+
MATCHES_ALL = '*/*'.freeze
|
|
30
|
+
|
|
31
|
+
GET_METHOD = "GET"
|
|
32
|
+
HEAD_METHOD = "HEAD"
|
|
33
|
+
POST_METHOD = "POST"
|
|
34
|
+
PUT_METHOD = "PUT"
|
|
35
|
+
DELETE_METHOD = "DELETE"
|
|
36
|
+
OPTIONS_METHOD = "OPTIONS"
|
|
37
|
+
TRACE_METHOD = "TRACE"
|
|
38
|
+
CONNECT_METHOD = "CONNECT"
|
|
39
|
+
|
|
40
|
+
STANDARD_HTTP_METHODS = [
|
|
41
|
+
GET_METHOD, HEAD_METHOD, POST_METHOD,
|
|
42
|
+
PUT_METHOD, DELETE_METHOD, TRACE_METHOD,
|
|
43
|
+
CONNECT_METHOD, OPTIONS_METHOD
|
|
44
|
+
].map!(&:freeze)
|
|
45
|
+
STANDARD_HTTP_METHODS.freeze
|
|
46
|
+
|
|
47
|
+
# A colon
|
|
48
|
+
COLON = ':'.freeze
|
|
49
|
+
|
|
50
|
+
# http string
|
|
51
|
+
HTTP = 'http'.freeze
|
|
52
|
+
|
|
53
|
+
# Host string
|
|
54
|
+
HOST = 'Host'.freeze
|
|
55
|
+
|
|
56
|
+
# HTTP Content-Encoding
|
|
57
|
+
CONTENT_ENCODING = 'Content-Encoding'.freeze
|
|
58
|
+
|
|
59
|
+
# Charset string
|
|
60
|
+
CHARSET = 'Charset'.freeze
|
|
61
|
+
|
|
62
|
+
# Comma split match
|
|
63
|
+
SPLIT_COMMA = /\s*,\s*/.freeze
|
|
64
|
+
|
|
65
|
+
# Star Character
|
|
66
|
+
STAR = '*'.freeze
|
|
67
|
+
|
|
68
|
+
# HTTP Location
|
|
69
|
+
LOCATION = 'Location'.freeze
|
|
70
|
+
|
|
71
|
+
# identity Encoding
|
|
72
|
+
IDENTITY = 'identity'.freeze
|
|
73
|
+
|
|
74
|
+
SERVER = 'Server'.freeze
|
|
75
|
+
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'webmachine/constants'
|
|
1
2
|
require 'webmachine/translation'
|
|
2
3
|
require 'webmachine/media_type'
|
|
3
4
|
|
|
@@ -13,7 +14,8 @@ module Webmachine
|
|
|
13
14
|
# appropriate media type.
|
|
14
15
|
# @api private
|
|
15
16
|
def choose_media_type(provided, header)
|
|
16
|
-
|
|
17
|
+
types = Array(header).map{|h| h.split(SPLIT_COMMA) }.flatten
|
|
18
|
+
requested = MediaTypeList.build(types)
|
|
17
19
|
provided = provided.map do |p| # normalize_provided
|
|
18
20
|
MediaType.parse(p)
|
|
19
21
|
end
|
|
@@ -30,9 +32,9 @@ module Webmachine
|
|
|
30
32
|
# @api private
|
|
31
33
|
def choose_encoding(provided, header)
|
|
32
34
|
encodings = provided.keys
|
|
33
|
-
if encoding = do_choose(encodings, header,
|
|
34
|
-
response.headers[
|
|
35
|
-
metadata[
|
|
35
|
+
if encoding = do_choose(encodings, header, IDENTITY)
|
|
36
|
+
response.headers[CONTENT_ENCODING] = encoding unless encoding == IDENTITY
|
|
37
|
+
metadata[CONTENT_ENCODING] = encoding
|
|
36
38
|
end
|
|
37
39
|
end
|
|
38
40
|
|
|
@@ -43,7 +45,7 @@ module Webmachine
|
|
|
43
45
|
if provided && !provided.empty?
|
|
44
46
|
charsets = provided.map {|c| c.first }
|
|
45
47
|
if charset = do_choose(charsets, header, HAS_ENCODING ? Encoding.default_external.name : kcode_charset)
|
|
46
|
-
metadata[
|
|
48
|
+
metadata[CHARSET] = charset
|
|
47
49
|
end
|
|
48
50
|
else
|
|
49
51
|
true
|
|
@@ -55,8 +57,8 @@ module Webmachine
|
|
|
55
57
|
# @api private
|
|
56
58
|
def choose_language(provided, header)
|
|
57
59
|
if provided && !provided.empty?
|
|
58
|
-
requested = PriorityList.build(header.split(
|
|
59
|
-
star_priority = requested.priority_of(
|
|
60
|
+
requested = PriorityList.build(header.split(SPLIT_COMMA))
|
|
61
|
+
star_priority = requested.priority_of(STAR)
|
|
60
62
|
any_ok = star_priority && star_priority > 0.0
|
|
61
63
|
accepted = requested.find do |priority, range|
|
|
62
64
|
if priority == 0.0
|
|
@@ -97,9 +99,9 @@ module Webmachine
|
|
|
97
99
|
# @api private
|
|
98
100
|
def do_choose(choices, header, default)
|
|
99
101
|
choices = choices.dup.map {|s| s.downcase }
|
|
100
|
-
accepted = PriorityList.build(header.split(
|
|
102
|
+
accepted = PriorityList.build(header.split(SPLIT_COMMA))
|
|
101
103
|
default_priority = accepted.priority_of(default)
|
|
102
|
-
star_priority = accepted.priority_of(
|
|
104
|
+
star_priority = accepted.priority_of(STAR)
|
|
103
105
|
default_ok = (default_priority.nil? && star_priority != 0.0) || default_priority
|
|
104
106
|
any_ok = star_priority && star_priority > 0.0
|
|
105
107
|
chosen = accepted.find do |priority, acceptable|
|
|
@@ -117,7 +119,7 @@ module Webmachine
|
|
|
117
119
|
|
|
118
120
|
private
|
|
119
121
|
# Matches acceptable items that include 'q' values
|
|
120
|
-
CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s
|
|
122
|
+
CONNEG_REGEX = /^\s*(\S+);\s*q=(\S*)\s*$/.freeze
|
|
121
123
|
|
|
122
124
|
# Matches the requested media type (with potential modifiers)
|
|
123
125
|
# against the provided types (with potential modifiers).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
require 'time'
|
|
1
|
+
require 'time'
|
|
2
2
|
require 'digest/md5'
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'webmachine/constants'
|
|
3
5
|
require 'webmachine/decision/conneg'
|
|
4
6
|
require 'webmachine/decision/falsey'
|
|
5
7
|
require 'webmachine/translation'
|
|
@@ -16,6 +18,8 @@ module Webmachine
|
|
|
16
18
|
# of the chart.
|
|
17
19
|
# @see https://raw.github.com/wiki/basho/webmachine/images/http-headers-status-v3.png
|
|
18
20
|
module Flow
|
|
21
|
+
include Base64
|
|
22
|
+
|
|
19
23
|
# Version of the flow diagram
|
|
20
24
|
VERSION = 3
|
|
21
25
|
|
|
@@ -31,7 +35,7 @@ module Webmachine
|
|
|
31
35
|
# Handles standard decisions where halting is allowed
|
|
32
36
|
def decision_test(test, iftrue, iffalse)
|
|
33
37
|
case test
|
|
34
|
-
when
|
|
38
|
+
when Integer # Allows callbacks to "halt" with a given response code
|
|
35
39
|
test
|
|
36
40
|
when Falsey
|
|
37
41
|
iffalse
|
|
@@ -73,7 +77,7 @@ module Webmachine
|
|
|
73
77
|
# Content-MD5 valid?
|
|
74
78
|
def b9a
|
|
75
79
|
case valid = resource.validate_content_checksum
|
|
76
|
-
when
|
|
80
|
+
when Integer
|
|
77
81
|
valid
|
|
78
82
|
when true
|
|
79
83
|
:b9b
|
|
@@ -81,7 +85,7 @@ module Webmachine
|
|
|
81
85
|
response.body = "Content-MD5 header does not match request body."
|
|
82
86
|
400
|
|
83
87
|
else # not_validated
|
|
84
|
-
if request.content_md5 == Digest::MD5.hexdigest(request.body)
|
|
88
|
+
if decode64(request.content_md5) == Digest::MD5.hexdigest(request.body)
|
|
85
89
|
:b9b
|
|
86
90
|
else
|
|
87
91
|
response.body = "Content-MD5 header does not match request body."
|
|
@@ -101,7 +105,7 @@ module Webmachine
|
|
|
101
105
|
case result
|
|
102
106
|
when true
|
|
103
107
|
:b7
|
|
104
|
-
when
|
|
108
|
+
when Integer
|
|
105
109
|
result
|
|
106
110
|
when String
|
|
107
111
|
response.headers['WWW-Authenticate'] = result
|
|
@@ -116,9 +120,10 @@ module Webmachine
|
|
|
116
120
|
decision_test(resource.forbidden?, 403, :b6)
|
|
117
121
|
end
|
|
118
122
|
|
|
123
|
+
CONTENT = /content-/.freeze
|
|
119
124
|
# Okay Content-* Headers?
|
|
120
125
|
def b6
|
|
121
|
-
decision_test(resource.valid_content_headers?(request.headers.grep(
|
|
126
|
+
decision_test(resource.valid_content_headers?(request.headers.grep(CONTENT)), :b5, 501)
|
|
122
127
|
end
|
|
123
128
|
|
|
124
129
|
# Known Content-Type?
|
|
@@ -144,7 +149,7 @@ module Webmachine
|
|
|
144
149
|
# Accept exists?
|
|
145
150
|
def c3
|
|
146
151
|
if !request.accept
|
|
147
|
-
metadata[
|
|
152
|
+
metadata[CONTENT_TYPE] = MediaType.parse(resource.content_types_provided.first.first)
|
|
148
153
|
:d4
|
|
149
154
|
else
|
|
150
155
|
:c4
|
|
@@ -158,7 +163,7 @@ module Webmachine
|
|
|
158
163
|
if !chosen_type
|
|
159
164
|
406
|
|
160
165
|
else
|
|
161
|
-
metadata[
|
|
166
|
+
metadata[CONTENT_TYPE] = chosen_type
|
|
162
167
|
:d4
|
|
163
168
|
end
|
|
164
169
|
end
|
|
@@ -166,7 +171,7 @@ module Webmachine
|
|
|
166
171
|
# Accept-Language exists?
|
|
167
172
|
def d4
|
|
168
173
|
if !request.accept_language
|
|
169
|
-
if language = choose_language(resource.languages_provided,
|
|
174
|
+
if language = choose_language(resource.languages_provided, STAR)
|
|
170
175
|
resource.language_chosen(language)
|
|
171
176
|
:e5
|
|
172
177
|
else
|
|
@@ -190,7 +195,7 @@ module Webmachine
|
|
|
190
195
|
# Accept-Charset exists?
|
|
191
196
|
def e5
|
|
192
197
|
if !request.accept_charset
|
|
193
|
-
choose_charset(resource.charsets_provided,
|
|
198
|
+
choose_charset(resource.charsets_provided, STAR) ? :f6 : 406
|
|
194
199
|
else
|
|
195
200
|
:e6
|
|
196
201
|
end
|
|
@@ -204,11 +209,11 @@ module Webmachine
|
|
|
204
209
|
# Accept-Encoding exists?
|
|
205
210
|
# (also, set content-type header here, now that charset is chosen)
|
|
206
211
|
def f6
|
|
207
|
-
chosen_type = metadata[
|
|
208
|
-
if chosen_charset = metadata[
|
|
212
|
+
chosen_type = metadata[CONTENT_TYPE]
|
|
213
|
+
if chosen_charset = metadata[CHARSET]
|
|
209
214
|
chosen_type.params['charset'] = chosen_charset
|
|
210
215
|
end
|
|
211
|
-
response.headers[
|
|
216
|
+
response.headers[CONTENT_TYPE] = chosen_type.to_s
|
|
212
217
|
if !request.accept_encoding
|
|
213
218
|
choose_encoding(resource.encodings_provided, "identity;q=1.0,*;q=0.5") ? :g7 : 406
|
|
214
219
|
else
|
|
@@ -240,13 +245,13 @@ module Webmachine
|
|
|
240
245
|
|
|
241
246
|
# ETag in If-Match
|
|
242
247
|
def g11
|
|
243
|
-
request_etags = request.if_match.split(
|
|
248
|
+
request_etags = request.if_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
|
|
244
249
|
request_etags.include?(ETag.new(resource.generate_etag)) ? :h10 : 412
|
|
245
250
|
end
|
|
246
251
|
|
|
247
252
|
# If-Match exists?
|
|
248
253
|
def h7
|
|
249
|
-
(request.if_match && unquote(request.if_match) ==
|
|
254
|
+
(request.if_match && unquote(request.if_match) == STAR) ? 412 : :i7
|
|
250
255
|
end
|
|
251
256
|
|
|
252
257
|
# If-Unmodified-Since exists?
|
|
@@ -273,9 +278,9 @@ module Webmachine
|
|
|
273
278
|
def i4
|
|
274
279
|
case uri = resource.moved_permanently?
|
|
275
280
|
when String, URI
|
|
276
|
-
response.headers[
|
|
281
|
+
response.headers[LOCATION] = uri.to_s
|
|
277
282
|
301
|
|
278
|
-
when
|
|
283
|
+
when Integer
|
|
279
284
|
uri
|
|
280
285
|
else
|
|
281
286
|
:p3
|
|
@@ -306,9 +311,9 @@ module Webmachine
|
|
|
306
311
|
def k5
|
|
307
312
|
case uri = resource.moved_permanently?
|
|
308
313
|
when String, URI
|
|
309
|
-
response.headers[
|
|
314
|
+
response.headers[LOCATION] = uri.to_s
|
|
310
315
|
301
|
|
311
|
-
when
|
|
316
|
+
when Integer
|
|
312
317
|
uri
|
|
313
318
|
else
|
|
314
319
|
:l5
|
|
@@ -322,17 +327,22 @@ module Webmachine
|
|
|
322
327
|
|
|
323
328
|
# Etag in if-none-match?
|
|
324
329
|
def k13
|
|
325
|
-
request_etags = request.if_none_match.split(
|
|
326
|
-
|
|
330
|
+
request_etags = request.if_none_match.split(SPLIT_COMMA).map {|etag| ETag.new(etag) }
|
|
331
|
+
resource_etag = resource.generate_etag
|
|
332
|
+
if resource_etag && request_etags.include?(ETag.new(resource_etag))
|
|
333
|
+
:j18
|
|
334
|
+
else
|
|
335
|
+
:l13
|
|
336
|
+
end
|
|
327
337
|
end
|
|
328
338
|
|
|
329
339
|
# Moved temporarily?
|
|
330
340
|
def l5
|
|
331
341
|
case uri = resource.moved_temporarily?
|
|
332
342
|
when String, URI
|
|
333
|
-
response.headers[
|
|
343
|
+
response.headers[LOCATION] = uri.to_s
|
|
334
344
|
307
|
|
335
|
-
when
|
|
345
|
+
when Integer
|
|
336
346
|
uri
|
|
337
347
|
else
|
|
338
348
|
:m5
|
|
@@ -410,22 +420,22 @@ module Webmachine
|
|
|
410
420
|
base_uri = resource.base_uri || request.base_uri
|
|
411
421
|
new_uri = URI.join(base_uri.to_s, uri)
|
|
412
422
|
request.disp_path = new_uri.path
|
|
413
|
-
response.headers[
|
|
423
|
+
response.headers[LOCATION] = new_uri.to_s
|
|
414
424
|
result = accept_helper
|
|
415
|
-
return result if
|
|
425
|
+
return result if Integer === result
|
|
416
426
|
end
|
|
417
427
|
else
|
|
418
428
|
case result = resource.process_post
|
|
419
429
|
when true
|
|
420
430
|
encode_body_if_set
|
|
421
|
-
when
|
|
431
|
+
when Integer
|
|
422
432
|
return result
|
|
423
433
|
else
|
|
424
434
|
raise InvalidResource, t('process_post_invalid', :result => result.inspect)
|
|
425
435
|
end
|
|
426
436
|
end
|
|
427
437
|
if response.is_redirect?
|
|
428
|
-
if response.headers[
|
|
438
|
+
if response.headers[LOCATION]
|
|
429
439
|
303
|
|
430
440
|
else
|
|
431
441
|
raise InvalidResource, t('do_redirect')
|
|
@@ -446,7 +456,7 @@ module Webmachine
|
|
|
446
456
|
409
|
|
447
457
|
else
|
|
448
458
|
res = accept_helper
|
|
449
|
-
(
|
|
459
|
+
(Integer === res) ? res : :p11
|
|
450
460
|
end
|
|
451
461
|
end
|
|
452
462
|
|
|
@@ -460,10 +470,10 @@ module Webmachine
|
|
|
460
470
|
def o18
|
|
461
471
|
if request.get? || request.head?
|
|
462
472
|
add_caching_headers
|
|
463
|
-
content_type = metadata[
|
|
473
|
+
content_type = metadata[CONTENT_TYPE]
|
|
464
474
|
handler = resource.content_types_provided.find {|ct, _| content_type.type_matches?(MediaType.parse(ct)) }.last
|
|
465
475
|
result = resource.send(handler)
|
|
466
|
-
if
|
|
476
|
+
if Integer === result
|
|
467
477
|
result
|
|
468
478
|
else
|
|
469
479
|
response.body = result
|
|
@@ -491,13 +501,13 @@ module Webmachine
|
|
|
491
501
|
409
|
|
492
502
|
else
|
|
493
503
|
res = accept_helper
|
|
494
|
-
(
|
|
504
|
+
(Integer === res) ? res : :p11
|
|
495
505
|
end
|
|
496
506
|
end
|
|
497
507
|
|
|
498
508
|
# New resource?
|
|
499
509
|
def p11
|
|
500
|
-
!response.headers[
|
|
510
|
+
!response.headers[LOCATION] ? :o20 : 201
|
|
501
511
|
end
|
|
502
512
|
|
|
503
513
|
end # module Flow
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
require 'webmachine/decision/helpers'
|
|
1
|
+
require 'webmachine/decision/helpers'
|
|
2
|
+
require 'webmachine/trace'
|
|
2
3
|
require 'webmachine/translation'
|
|
4
|
+
require 'webmachine/constants'
|
|
5
|
+
require 'webmachine/rescueable_exception'
|
|
3
6
|
|
|
4
7
|
module Webmachine
|
|
5
8
|
module Decision
|
|
@@ -8,6 +11,7 @@ module Webmachine
|
|
|
8
11
|
class FSM
|
|
9
12
|
include Flow
|
|
10
13
|
include Helpers
|
|
14
|
+
include Trace::FSM
|
|
11
15
|
include Translation
|
|
12
16
|
|
|
13
17
|
attr_reader :resource, :request, :response, :metadata
|
|
@@ -15,7 +19,7 @@ module Webmachine
|
|
|
15
19
|
def initialize(resource, request, response)
|
|
16
20
|
@resource, @request, @response = resource, request, response
|
|
17
21
|
@metadata = {}
|
|
18
|
-
|
|
22
|
+
super
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
# Processes the request, iteratively invoking the decision methods in {Flow}.
|
|
@@ -26,7 +30,7 @@ module Webmachine
|
|
|
26
30
|
trace_decision(state)
|
|
27
31
|
result = handle_exceptions { send(state) }
|
|
28
32
|
case result
|
|
29
|
-
when
|
|
33
|
+
when Integer # Response code
|
|
30
34
|
respond(result)
|
|
31
35
|
break
|
|
32
36
|
when Symbol # Next state
|
|
@@ -35,7 +39,7 @@ module Webmachine
|
|
|
35
39
|
raise InvalidResource, t('fsm_broke', :state => state, :result => result.inspect)
|
|
36
40
|
end
|
|
37
41
|
end
|
|
38
|
-
rescue
|
|
42
|
+
rescue => e
|
|
39
43
|
Webmachine.render_error(500, request, response, :message => e.message)
|
|
40
44
|
ensure
|
|
41
45
|
trace_response(response)
|
|
@@ -45,12 +49,12 @@ module Webmachine
|
|
|
45
49
|
|
|
46
50
|
def handle_exceptions
|
|
47
51
|
yield
|
|
52
|
+
rescue Webmachine::RescuableException => e
|
|
53
|
+
resource.handle_exception(e)
|
|
54
|
+
500
|
|
48
55
|
rescue MalformedRequest => e
|
|
49
56
|
Webmachine.render_error(400, request, response, :message => e.message)
|
|
50
57
|
400
|
|
51
|
-
rescue Exception => e
|
|
52
|
-
resource.handle_exception(e)
|
|
53
|
-
500
|
|
54
58
|
end
|
|
55
59
|
|
|
56
60
|
def respond(code, headers={})
|
|
@@ -60,7 +64,7 @@ module Webmachine
|
|
|
60
64
|
when 404
|
|
61
65
|
Webmachine.render_error(code, request, response)
|
|
62
66
|
when 304
|
|
63
|
-
response.headers.delete(
|
|
67
|
+
response.headers.delete(CONTENT_TYPE)
|
|
64
68
|
add_caching_headers
|
|
65
69
|
end
|
|
66
70
|
|
|
@@ -69,19 +73,8 @@ module Webmachine
|
|
|
69
73
|
response.code
|
|
70
74
|
end
|
|
71
75
|
|
|
72
|
-
ensure_content_length
|
|
73
|
-
ensure_date_header
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# When tracing is disabled, this does nothing.
|
|
77
|
-
def trace_decision(state); end
|
|
78
|
-
# When tracing is disabled, this does nothing.
|
|
79
|
-
def trace_request(request); end
|
|
80
|
-
# When tracing is disabled, this does nothing.
|
|
81
|
-
def trace_response(response); end
|
|
82
|
-
|
|
83
|
-
def initialize_tracing
|
|
84
|
-
extend Trace::FSM if Trace.trace?(resource)
|
|
76
|
+
ensure_content_length(response)
|
|
77
|
+
ensure_date_header(response)
|
|
85
78
|
end
|
|
86
79
|
end # class FSM
|
|
87
80
|
end # module Decision
|