webmachine 1.2.2 → 1.3.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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +4 -0
  4. data/Gemfile +13 -11
  5. data/README.md +85 -89
  6. data/Rakefile +0 -1
  7. data/documentation/adapters.md +39 -0
  8. data/documentation/authentication-and-authorization.md +37 -0
  9. data/documentation/configurator.md +19 -0
  10. data/documentation/error-handling.md +86 -0
  11. data/documentation/examples.md +215 -0
  12. data/documentation/how-it-works.md +76 -0
  13. data/documentation/routes.md +97 -0
  14. data/documentation/validation.md +159 -0
  15. data/documentation/versioning-apis.md +74 -0
  16. data/documentation/visual-debugger.md +38 -0
  17. data/examples/application.rb +2 -2
  18. data/examples/debugger.rb +1 -1
  19. data/lib/webmachine.rb +3 -1
  20. data/lib/webmachine/adapter.rb +7 -13
  21. data/lib/webmachine/adapters.rb +1 -2
  22. data/lib/webmachine/adapters/httpkit.rb +74 -0
  23. data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
  24. data/lib/webmachine/adapters/rack.rb +37 -21
  25. data/lib/webmachine/adapters/reel.rb +21 -23
  26. data/lib/webmachine/adapters/webrick.rb +16 -16
  27. data/lib/webmachine/application.rb +2 -2
  28. data/lib/webmachine/chunked_body.rb +3 -4
  29. data/lib/webmachine/constants.rb +75 -0
  30. data/lib/webmachine/decision/conneg.rb +12 -10
  31. data/lib/webmachine/decision/flow.rb +31 -21
  32. data/lib/webmachine/decision/fsm.rb +10 -18
  33. data/lib/webmachine/decision/helpers.rb +9 -37
  34. data/lib/webmachine/dispatcher.rb +13 -10
  35. data/lib/webmachine/dispatcher/route.rb +18 -8
  36. data/lib/webmachine/errors.rb +7 -1
  37. data/lib/webmachine/header_negotiation.rb +25 -0
  38. data/lib/webmachine/headers.rb +7 -2
  39. data/lib/webmachine/locale/en.yml +7 -5
  40. data/lib/webmachine/media_type.rb +10 -8
  41. data/lib/webmachine/request.rb +44 -15
  42. data/lib/webmachine/resource.rb +1 -1
  43. data/lib/webmachine/resource/callbacks.rb +6 -4
  44. data/lib/webmachine/spec/IO_response.body +1 -0
  45. data/lib/webmachine/spec/adapter_lint.rb +70 -36
  46. data/lib/webmachine/spec/test_resource.rb +10 -4
  47. data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
  48. data/lib/webmachine/streaming/io_encoder.rb +6 -0
  49. data/lib/webmachine/trace.rb +1 -0
  50. data/lib/webmachine/trace/fsm.rb +20 -10
  51. data/lib/webmachine/trace/resource_proxy.rb +2 -0
  52. data/lib/webmachine/translation.rb +2 -1
  53. data/lib/webmachine/version.rb +3 -3
  54. data/memory_test.rb +37 -0
  55. data/spec/spec_helper.rb +9 -9
  56. data/spec/webmachine/adapter_spec.rb +14 -15
  57. data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
  58. data/spec/webmachine/adapters/rack_spec.rb +6 -6
  59. data/spec/webmachine/adapters/reel_spec.rb +15 -11
  60. data/spec/webmachine/adapters/webrick_spec.rb +2 -2
  61. data/spec/webmachine/application_spec.rb +18 -17
  62. data/spec/webmachine/chunked_body_spec.rb +3 -3
  63. data/spec/webmachine/configuration_spec.rb +5 -5
  64. data/spec/webmachine/cookie_spec.rb +13 -13
  65. data/spec/webmachine/decision/conneg_spec.rb +48 -42
  66. data/spec/webmachine/decision/falsey_spec.rb +4 -4
  67. data/spec/webmachine/decision/flow_spec.rb +194 -144
  68. data/spec/webmachine/decision/fsm_spec.rb +17 -17
  69. data/spec/webmachine/decision/helpers_spec.rb +20 -20
  70. data/spec/webmachine/dispatcher/route_spec.rb +73 -27
  71. data/spec/webmachine/dispatcher_spec.rb +34 -24
  72. data/spec/webmachine/errors_spec.rb +1 -1
  73. data/spec/webmachine/etags_spec.rb +19 -19
  74. data/spec/webmachine/events_spec.rb +6 -6
  75. data/spec/webmachine/headers_spec.rb +14 -14
  76. data/spec/webmachine/media_type_spec.rb +36 -36
  77. data/spec/webmachine/request_spec.rb +33 -33
  78. data/spec/webmachine/resource/authentication_spec.rb +6 -6
  79. data/spec/webmachine/response_spec.rb +12 -12
  80. data/spec/webmachine/trace/fsm_spec.rb +8 -8
  81. data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
  82. data/spec/webmachine/trace/trace_store_spec.rb +5 -5
  83. data/spec/webmachine/trace_spec.rb +3 -3
  84. data/webmachine.gemspec +2 -6
  85. metadata +48 -206
  86. data/lib/webmachine/adapters/hatetepe.rb +0 -108
  87. data/lib/webmachine/adapters/mongrel.rb +0 -127
  88. data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
  89. data/lib/webmachine/fiber18.rb +0 -88
  90. data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
  91. data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
@@ -1,9 +1,11 @@
1
- require 'stringio'
1
+ require 'stringio'
2
2
  require 'time'
3
3
  require 'webmachine/streaming'
4
4
  require 'webmachine/media_type'
5
5
  require 'webmachine/quoted_string'
6
6
  require 'webmachine/etags'
7
+ require 'webmachine/header_negotiation'
8
+ require 'webmachine/constants'
7
9
 
8
10
  module Webmachine
9
11
  module Decision
@@ -11,6 +13,7 @@ module Webmachine
11
13
  module Helpers
12
14
  include QuotedString
13
15
  include Streaming
16
+ include HeaderNegotiation
14
17
 
15
18
  # Determines if the response has a body/entity set.
16
19
  def has_response_body?
@@ -26,8 +29,8 @@ module Webmachine
26
29
  # Encodes the body in the selected charset and encoding.
27
30
  def encode_body
28
31
  body = response.body
29
- chosen_charset = metadata['Charset']
30
- chosen_encoding = metadata['Content-Encoding']
32
+ chosen_charset = metadata[CHARSET]
33
+ chosen_encoding = metadata[CONTENT_ENCODING]
31
34
  charsetter = resource.charsets_provided && resource.charsets_provided.find {|c,_| c == chosen_charset }.last || :charset_nop
32
35
  encoder = resource.encodings_provided[chosen_encoding]
33
36
  response.body = case body
@@ -47,10 +50,10 @@ module Webmachine
47
50
  end
48
51
  end
49
52
  if body_is_fixed_length?
50
- set_content_length
53
+ ensure_content_length(response)
51
54
  else
52
- response.headers.delete 'Content-Length'
53
- response.headers['Transfer-Encoding'] = 'chunked'
55
+ response.headers.delete CONTENT_LENGTH
56
+ response.headers[TRANSFER_ENCODING] = 'chunked'
54
57
  end
55
58
  end
56
59
 
@@ -88,37 +91,6 @@ module Webmachine
88
91
  end
89
92
  end
90
93
 
91
- # Ensures that responses have an appropriate Content-Length
92
- # header
93
- def ensure_content_length
94
- case
95
- when response.headers['Transfer-Encoding']
96
- return
97
- when [204, 205, 304].include?(response.code)
98
- response.headers.delete 'Content-Length'
99
- when has_response_body?
100
- set_content_length
101
- else
102
- response.headers['Content-Length'] = '0'
103
- end
104
- end
105
-
106
- # Ensures that responses have an appropriate Date header
107
- def ensure_date_header
108
- if (200..499).include?(response.code)
109
- response.headers['Date'] ||= Time.now.httpdate
110
- end
111
- end
112
-
113
- # Sets the Content-Length header on the response
114
- def set_content_length
115
- if response.body.respond_to?(:bytesize)
116
- response.headers['Content-Length'] = response.body.bytesize.to_s
117
- else
118
- response.headers['Content-Length'] = response.body.length.to_s
119
- end
120
- end
121
-
122
94
  # Determines whether the response is of a fixed lenghth, i.e. it
123
95
  # is a String or IO with known size.
124
96
  def body_is_fixed_length?
@@ -1,12 +1,13 @@
1
- require 'forwardable'
1
+ require 'forwardable'
2
2
  require 'webmachine/decision'
3
3
  require 'webmachine/dispatcher/route'
4
- require 'webmachine/dispatcher/not_found_resource'
5
4
 
6
5
  module Webmachine
7
6
  # Handles dispatching incoming requests to the proper registered
8
7
  # resources and initializing the decision logic.
9
8
  class Dispatcher
9
+ WM_DISPATCH = 'wm.dispatch'.freeze
10
+
10
11
  # @return [Array<Route>] the list of routes that will be
11
12
  # dispatched to
12
13
  # @see #add_route
@@ -40,12 +41,16 @@ module Webmachine
40
41
  # @param [Request] request the request object
41
42
  # @param [Response] response the response object
42
43
  def dispatch(request, response)
43
- resource = find_resource(request, response)
44
- Webmachine::Events.instrument('wm.dispatch') do |payload|
45
- Webmachine::Decision::FSM.new(resource, request, response).run
46
- payload[:resource] = resource.class.name
47
- payload[:request] = request.dup
48
- payload[:code] = response.code
44
+ if resource = find_resource(request, response)
45
+ Webmachine::Events.instrument(WM_DISPATCH) do |payload|
46
+ Webmachine::Decision::FSM.new(resource, request, response).run
47
+
48
+ payload[:resource] = resource.class.name
49
+ payload[:request] = request.dup
50
+ payload[:code] = response.code
51
+ end
52
+ else
53
+ Webmachine.render_error(404, request, response)
49
54
  end
50
55
  end
51
56
 
@@ -61,8 +66,6 @@ module Webmachine
61
66
  def find_resource(request, response)
62
67
  if route = find_route(request)
63
68
  prepare_resource(route, request, response)
64
- else
65
- NotFoundResource.new(request, response)
66
69
  end
67
70
  end
68
71
 
@@ -1,12 +1,13 @@
1
- require 'webmachine/resource'
1
+ require 'webmachine/resource'
2
2
  require 'webmachine/translation'
3
+ require 'webmachine/constants'
3
4
 
4
5
  module Webmachine
5
6
  class Dispatcher
6
7
  # Pairs URIs with {Resource} classes in the {Dispatcher}. To
7
8
  # create routes, use {Dispatcher#add_route}.
8
9
  class Route
9
- include Webmachine::Translation
10
+ include Translation
10
11
 
11
12
  # @return [Class] the resource this route will dispatch to, a
12
13
  # subclass of {Resource}
@@ -22,13 +23,16 @@ module Webmachine
22
23
 
23
24
  # When used in a path specification, will match all remaining
24
25
  # segments
25
- MATCH_ALL = '*'.freeze
26
+ MATCH_ALL = :*
27
+
28
+ # String version of MATCH_ALL, deprecated. Use the symbol instead.
29
+ MATCH_ALL_STR = '*'.freeze
26
30
 
27
31
  # Creates a new Route that will associate a pattern to a
28
32
  # {Resource}.
29
33
  #
30
34
  # @example Standard route
31
- # Route.new(["*"], MyResource)
35
+ # Route.new([:*], MyResource)
32
36
  #
33
37
  # @example Guarded route
34
38
  # Route.new ["/notes"],
@@ -65,6 +69,8 @@ module Webmachine
65
69
  guards = args
66
70
  guards << Proc.new if block_given?
67
71
 
72
+ warn t('match_all_symbol') if path_spec.include? MATCH_ALL_STR
73
+
68
74
  @path_spec = path_spec
69
75
  @guards = guards
70
76
  @resource = resource
@@ -73,11 +79,13 @@ module Webmachine
73
79
  raise ArgumentError, t('not_resource_class', :class => resource.name) unless resource < Resource
74
80
  end
75
81
 
82
+ PATH_MATCH = /^\/(.*)/.freeze
83
+
76
84
  # Determines whether the given request matches this route and
77
85
  # should be dispatched to the {#resource}.
78
86
  # @param [Reqeust] request the request object
79
87
  def match?(request)
80
- tokens = request.uri.path.match(/^\/(.*)/)[1].split('/')
88
+ tokens = request.uri.path.match(PATH_MATCH)[1].split(SLASH)
81
89
  bind(tokens, {}) && guards.all? { |guard| guard.call(request) }
82
90
  end
83
91
 
@@ -85,9 +93,9 @@ module Webmachine
85
93
  # route, including path bindings.
86
94
  # @param [Request] request the request object
87
95
  def apply(request)
88
- request.disp_path = request.uri.path.match(/^\/(.*)/)[1]
96
+ request.disp_path = request.uri.path.match(PATH_MATCH)[1]
89
97
  request.path_info = @bindings.dup
90
- tokens = request.disp_path.split('/')
98
+ tokens = request.disp_path.split(SLASH)
91
99
  depth, trailing = bind(tokens, request.path_info)
92
100
  request.path_tokens = trailing || []
93
101
  end
@@ -107,12 +115,14 @@ module Webmachine
107
115
  case
108
116
  when spec.empty? && tokens.empty?
109
117
  return depth
118
+ when spec == [MATCH_ALL_STR]
119
+ return [depth, tokens]
110
120
  when spec == [MATCH_ALL]
111
121
  return [depth, tokens]
112
122
  when tokens.empty?
113
123
  return false
114
124
  when Symbol === spec.first
115
- bindings[spec.first] = tokens.first
125
+ bindings[spec.first] = URI.decode(tokens.first)
116
126
  when spec.first == tokens.first
117
127
  else
118
128
  return false
@@ -1,7 +1,10 @@
1
+ require 'webmachine/header_negotiation'
1
2
  require 'webmachine/translation'
3
+ require 'webmachine/constants'
2
4
  require 'webmachine/version'
3
5
 
4
6
  module Webmachine
7
+ extend HeaderNegotiation
5
8
  extend Translation
6
9
 
7
10
  # Renders a standard error message body for the response. The
@@ -21,10 +24,13 @@ module Webmachine
21
24
  {:title => title,
22
25
  :message => message,
23
26
  :version => Webmachine::SERVER_STRING}.merge(options))
24
- res.headers['Content-Type'] = "text/html"
27
+ res.headers[CONTENT_TYPE] = TEXT_HTML
25
28
  end
29
+ ensure_content_length(res)
30
+ ensure_date_header(res)
26
31
  end
27
32
 
33
+
28
34
  # Superclass of all errors generated by Webmachine.
29
35
  class Error < ::StandardError; end
30
36
 
@@ -0,0 +1,25 @@
1
+ require 'webmachine/constants'
2
+
3
+ module Webmachine
4
+ module HeaderNegotiation
5
+ def ensure_date_header(res)
6
+ if (200..499).include?(res.code)
7
+ res.headers[DATE] ||= Time.now.httpdate
8
+ end
9
+ end
10
+
11
+ def ensure_content_length(res)
12
+ body = res.body
13
+ case
14
+ when res.headers[TRANSFER_ENCODING]
15
+ return
16
+ when [204, 205, 304].include?(res.code)
17
+ res.headers.delete CONTENT_LENGTH
18
+ when body != nil
19
+ res.headers[CONTENT_LENGTH] = body.respond_to?(:bytesize) ? body.bytesize.to_s : body.length.to_s
20
+ else
21
+ res.headers[CONTENT_LENGTH] = '0'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,13 +1,18 @@
1
+ require 'webmachine/constants'
2
+
1
3
  module Webmachine
2
4
  # Case-insensitive Hash of Request headers
3
5
  class Headers < ::Hash
6
+ CGI_HTTP_MATCH = /^HTTP_(\w+)$/.freeze
7
+ CONTENT_TYPE_LENGTH_MATCH = /^(CONTENT_(?:TYPE|LENGTH))$/.freeze
8
+
4
9
  # Convert CGI-style Hash into Request headers
5
10
  # @param [Hash] env a hash of CGI-style env/headers
6
11
  # @return [Webmachine::Headers]
7
12
  def self.from_cgi(env)
8
13
  env.inject(new) do |h,(k,v)|
9
- if k =~ /^HTTP_(\w+)$/ || k =~ /^(CONTENT_(?:TYPE|LENGTH))$/
10
- h[$1.tr("_", "-")] = v
14
+ if k =~ CGI_HTTP_MATCH || k =~ CONTENT_TYPE_LENGTH_MATCH
15
+ h[$1.tr(UNDERSCORE, DASH)] = v
11
16
  end
12
17
  h
13
18
  end
@@ -1,10 +1,11 @@
1
1
  en:
2
2
  webmachine:
3
3
  errors:
4
- standard_body: "<!DOCTYPE html><html>
5
- <head><title>%{title}</title></head>
6
- <body><h1>%{title}</h1><p>%{message}</p>
7
- <address>%{version} server</address></body></html>"
4
+ standard_body: "<!DOCTYPE html><html>\n
5
+ <head><title>%{title}</title></head>\n
6
+ <body><h1>%{title}</h1>\n
7
+ <p>%{message}</p>\n
8
+ <address>%{version} server</address></body></html>\n"
8
9
  "400":
9
10
  title: 400 Malformed Request
10
11
  message: The request was malformed and could not be processed.
@@ -13,7 +14,7 @@ en:
13
14
  message: The requested document was not found on this server.
14
15
  "500":
15
16
  title: 500 Internal Server Error
16
- message: "The server encountered an error while processing this request: <pre>%{error}</pre>"
17
+ message: "The server encountered an error while processing this request: <pre>\n%{error}</pre>"
17
18
  "501":
18
19
  title: 501 Not Implemented
19
20
  message: "The server does not support the %{method} method."
@@ -26,3 +27,4 @@ en:
26
27
  invalid_media_type: "Invalid media type: %{type}"
27
28
  not_resource_class: "%{class} is not a subclass of Webmachine::Resource"
28
29
  process_post_invalid: "process_post returned %{result}"
30
+ match_all_symbol: '"*" as a path segment is deprecated and will be removed in a future release. Please use :*'
@@ -1,14 +1,16 @@
1
- require 'webmachine/translation'
1
+ require 'webmachine/translation'
2
+ require 'webmachine/constants'
3
+ require 'webmachine/dispatcher/route'
2
4
 
3
5
  module Webmachine
4
6
  # Encapsulates a MIME media type, with logic for matching types.
5
7
  class MediaType
6
8
  extend Translation
7
9
  # Matches valid media types
8
- MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s*$/
10
+ MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s*$/.freeze
9
11
 
10
12
  # Matches sub-type parameters
11
- PARAMS_REGEX = /;\s*([^=]+)(=([^;=\s]*))?/
13
+ PARAMS_REGEX = /;\s*([^=]+)(=([^;=\s]*))?/.freeze
12
14
 
13
15
  # Creates a new MediaType by parsing an alternate representation.
14
16
  # @param [MediaType, String, Array<String,Hash>] obj the raw type
@@ -48,7 +50,7 @@ module Webmachine
48
50
  # Detects whether the {MediaType} represents an open wildcard
49
51
  # type, that is, "*/*" without any {#params}.
50
52
  def matches_all?
51
- @type == "*/*" && @params.empty?
53
+ @type == MATCHES_ALL && @params.empty?
52
54
  end
53
55
 
54
56
  # @return [true,false] Are these two types strictly equal?
@@ -97,12 +99,12 @@ module Webmachine
97
99
 
98
100
  # @return [String] The major type, e.g. "application", "text", "image"
99
101
  def major
100
- type.split("/").first
102
+ type.split(SLASH).first
101
103
  end
102
104
 
103
105
  # @return [String] the minor or sub-type, e.g. "json", "html", "jpeg"
104
106
  def minor
105
- type.split("/").last
107
+ type.split(SLASH).last
106
108
  end
107
109
 
108
110
  # @param [MediaType] other the other type
@@ -110,10 +112,10 @@ module Webmachine
110
112
  # ignoring params and taking into account wildcards
111
113
  def type_matches?(other)
112
114
  other = self.class.parse(other)
113
- if ["*", "*/*", type].include?(other.type)
115
+ if [Dispatcher::Route::MATCH_ALL_STR, MATCHES_ALL, type].include?(other.type)
114
116
  true
115
117
  else
116
- other.major == major && other.minor == "*"
118
+ other.major == major && other.minor == Dispatcher::Route::MATCH_ALL_STR
117
119
  end
118
120
  end
119
121
  end # class MediaType
@@ -1,16 +1,18 @@
1
- require 'cgi'
1
+ require 'cgi'
2
2
  require 'forwardable'
3
+ require 'webmachine/constants'
3
4
 
4
5
  module Webmachine
5
6
  # Request represents a single HTTP request sent from a client. It
6
7
  # should be instantiated by {Adapters} when a request is received
7
8
  class Request
9
+ HTTP_HEADERS_MATCH = /^(?:[a-z0-9])+(?:_[a-z0-9]+)*$/i.freeze
10
+
8
11
  extend Forwardable
12
+
9
13
  attr_reader :method, :uri, :headers, :body
10
14
  attr_accessor :disp_path, :path_info, :path_tokens
11
15
 
12
- STANDARD_HTTP_METHODS = %w[GET HEAD POST PUT DELETE TRACE CONNECT OPTIONS]
13
-
14
16
  # @param [String] method the HTTP request method
15
17
  # @param [URI] uri the requested URI, including host, scheme and
16
18
  # port
@@ -18,7 +20,8 @@ module Webmachine
18
20
  # @param [String,#to_s,#each,nil] body the entity included in the
19
21
  # request, if present
20
22
  def initialize(method, uri, headers, body)
21
- @method, @uri, @headers, @body = method, uri, headers, body
23
+ @method, @headers, @body = method, headers, body
24
+ @uri = build_uri(uri, headers)
22
25
  end
23
26
 
24
27
  def_delegators :headers, :[]
@@ -27,9 +30,18 @@ module Webmachine
27
30
  # lowercased-underscored version of the header name, e.g.
28
31
  # `if_unmodified_since`.
29
32
  def method_missing(m, *args, &block)
30
- if m.to_s =~ /^(?:[a-z0-9])+(?:_[a-z0-9]+)*$/i
33
+ if m =~ HTTP_HEADERS_MATCH
31
34
  # Access headers more easily as underscored methods.
32
- self[m.to_s.tr('_', '-')]
35
+ header_name = m.to_s.tr(UNDERSCORE, DASH)
36
+ if (header_value = headers[header_name])
37
+ # Make future lookups faster.
38
+ self.class.class_eval <<-RUBY, __FILE__, __LINE__
39
+ def #{m}
40
+ headers["#{header_name}"]
41
+ end
42
+ RUBY
43
+ end
44
+ header_value
33
45
  else
34
46
  super
35
47
  end
@@ -45,7 +57,7 @@ module Webmachine
45
57
  # @return [URI]
46
58
  def base_uri
47
59
  @base_uri ||= uri.dup.tap do |u|
48
- u.path = "/"
60
+ u.path = SLASH
49
61
  u.query = nil
50
62
  end
51
63
  end
@@ -92,7 +104,7 @@ module Webmachine
92
104
  # @return [Boolean]
93
105
  # true if this request was made with the GET method
94
106
  def get?
95
- method == "GET"
107
+ method == GET_METHOD
96
108
  end
97
109
 
98
110
  # Is this a HEAD request?
@@ -100,7 +112,7 @@ module Webmachine
100
112
  # @return [Boolean]
101
113
  # true if this request was made with the HEAD method
102
114
  def head?
103
- method == "HEAD"
115
+ method == HEAD_METHOD
104
116
  end
105
117
 
106
118
  # Is this a POST request?
@@ -108,7 +120,7 @@ module Webmachine
108
120
  # @return [Boolean]
109
121
  # true if this request was made with the GET method
110
122
  def post?
111
- method == "POST"
123
+ method == POST_METHOD
112
124
  end
113
125
 
114
126
  # Is this a PUT request?
@@ -116,7 +128,7 @@ module Webmachine
116
128
  # @return [Boolean]
117
129
  # true if this request was made with the PUT method
118
130
  def put?
119
- method == "PUT"
131
+ method == PUT_METHOD
120
132
  end
121
133
 
122
134
  # Is this a DELETE request?
@@ -124,7 +136,7 @@ module Webmachine
124
136
  # @return [Boolean]
125
137
  # true if this request was made with the DELETE method
126
138
  def delete?
127
- method == "DELETE"
139
+ method == DELETE_METHOD
128
140
  end
129
141
 
130
142
  # Is this a TRACE request?
@@ -132,7 +144,7 @@ module Webmachine
132
144
  # @return [Boolean]
133
145
  # true if this request was made with the TRACE method
134
146
  def trace?
135
- method == "TRACE"
147
+ method == TRACE_METHOD
136
148
  end
137
149
 
138
150
  # Is this a CONNECT request?
@@ -140,7 +152,7 @@ module Webmachine
140
152
  # @return [Boolean]
141
153
  # true if this request was made with the CONNECT method
142
154
  def connect?
143
- method == "CONNECT"
155
+ method == CONNECT_METHOD
144
156
  end
145
157
 
146
158
  # Is this an OPTIONS request?
@@ -148,7 +160,24 @@ module Webmachine
148
160
  # @return [Boolean]
149
161
  # true if this request was made with the OPTIONS method
150
162
  def options?
151
- method == "OPTIONS"
163
+ method == OPTIONS_METHOD
164
+ end
165
+
166
+ private
167
+
168
+ def build_uri(uri, headers)
169
+ uri = URI(uri)
170
+
171
+ host, _, port = headers.fetch(HOST, "").rpartition(COLON)
172
+ return uri if host.empty?
173
+
174
+ host = "[#{host}]" if host.include?(COLON)
175
+ port = 80 if port.empty?
176
+
177
+ uri.scheme = HTTP
178
+ uri.host, uri.port = host, port.to_i
179
+
180
+ URI.parse(uri.to_s)
152
181
  end
153
182
 
154
183
  end # class Request