webbed 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +22 -0
- data/.travis.yml +7 -0
- data/.yardopts +6 -0
- data/Gemfile +5 -13
- data/Guardfile +5 -0
- data/README.md +36 -31
- data/Rakefile +7 -14
- data/lib/webbed.rb +28 -6
- data/lib/webbed/generic_message.rb +27 -15
- data/lib/webbed/headers.rb +2 -0
- data/lib/webbed/helpers/entity_headers_helper.rb +62 -0
- data/lib/webbed/helpers/method_helper.rb +83 -0
- data/lib/webbed/helpers/rack_request_helper.rb +86 -0
- data/lib/webbed/helpers/rack_response_helper.rb +56 -0
- data/lib/webbed/helpers/request_headers_helper.rb +62 -0
- data/lib/webbed/helpers/request_uri_helper.rb +34 -0
- data/lib/webbed/helpers/response_headers_helper.rb +48 -0
- data/lib/webbed/helpers/scheme_helper.rb +26 -0
- data/lib/webbed/http_version.rb +88 -33
- data/lib/webbed/media_type.rb +160 -0
- data/lib/webbed/method.rb +63 -33
- data/lib/webbed/request.rb +65 -21
- data/lib/webbed/response.rb +65 -24
- data/lib/webbed/status_code.rb +84 -17
- data/lib/webbed/version.rb +1 -1
- data/test/support/assertions.rb +17 -0
- data/test/support/runner.rb +326 -0
- data/test/test_helper.rb +13 -0
- data/test/webbed/generic_message_test.rb +44 -0
- data/test/webbed/headers_test.rb +31 -0
- data/test/webbed/helpers/entity_headers_helper_test.rb +68 -0
- data/test/webbed/helpers/method_helper_test.rb +151 -0
- data/test/webbed/helpers/rack_request_helper_test.rb +108 -0
- data/test/webbed/helpers/rack_response_helper_test.rb +33 -0
- data/test/webbed/helpers/request_headers_helper_test.rb +57 -0
- data/test/webbed/helpers/request_uri_helper_test.rb +32 -0
- data/test/webbed/helpers/response_headers_helper_test.rb +46 -0
- data/test/webbed/helpers/scheme_helper_test.rb +28 -0
- data/test/webbed/http_version_test.rb +52 -0
- data/test/webbed/media_type_test.rb +100 -0
- data/test/webbed/method_test.rb +160 -0
- data/test/webbed/request_test.rb +74 -0
- data/test/webbed/response_test.rb +86 -0
- data/test/webbed/status_code_test.rb +105 -0
- data/webbed.gemspec +31 -0
- metadata +128 -41
data/lib/webbed/method.rb
CHANGED
@@ -1,64 +1,94 @@
|
|
1
1
|
module Webbed
|
2
|
+
# Representation of an HTTP Method.
|
2
3
|
class Method
|
3
|
-
|
4
|
-
attr_reader :name
|
5
|
-
alias :to_s :name
|
6
|
-
alias :inspect :name
|
7
|
-
|
8
4
|
DEFAULTS = {
|
9
5
|
:safe => false,
|
10
6
|
:idempotent => false,
|
11
|
-
:
|
7
|
+
:allowable_entities => [:request, :response]
|
12
8
|
}
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
# The allowable entities of a message.
|
11
|
+
#
|
12
|
+
# It can contain any combination of `:request` and `:response`.
|
13
|
+
#
|
14
|
+
# @return [Array<:request, :response>]
|
15
|
+
attr_reader :allowable_entities
|
16
|
+
|
17
|
+
# Checks for a cached Method or creates a new one.
|
18
|
+
#
|
19
|
+
# It caches the standard HTTP Methods that are in RFC 2616 as well as the
|
20
|
+
# new method `PATCH`. If the Method cannot be found, it calls `#initialize`.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# Webbed::Method.new('GET') # => Webbed::Method::GET
|
24
|
+
#
|
25
|
+
# @param (see #initialize)
|
26
|
+
# @return [Method] the new or cached Method
|
27
|
+
# @see #initialize
|
28
|
+
def self.new(value, options = {})
|
29
|
+
if const_defined?(value)
|
30
|
+
const_get(value)
|
17
31
|
else
|
18
|
-
super(
|
32
|
+
super(value, options)
|
19
33
|
end
|
20
34
|
end
|
21
35
|
|
36
|
+
# Creates a new Method.
|
37
|
+
#
|
38
|
+
# @param [String] name the name of the Method to create
|
39
|
+
# @param [Hash] options the options to create the Method with
|
40
|
+
# @option options [Boolean] :safe (false) whether or not the Method is safe
|
41
|
+
# @option options [Boolean] :idempotent (false) whether or not the Method is
|
42
|
+
# idempotent
|
43
|
+
# @option options [Array<:request, :response>] :allowable_entities
|
44
|
+
# ([:request, :response]) the allowable entites of a message
|
22
45
|
def initialize(name, options = {})
|
23
|
-
options = DEFAULTS.merge
|
46
|
+
options = DEFAULTS.merge(options)
|
24
47
|
@name = name
|
25
48
|
@safe = options[:safe]
|
26
49
|
@idempotent = options[:safe] || options[:idempotent]
|
27
|
-
@
|
28
|
-
@has_response_entity = options[:entities].include? :response
|
50
|
+
@allowable_entities = options[:allowable_entities]
|
29
51
|
end
|
30
52
|
|
53
|
+
# Whether or not the Method is safe.
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
31
56
|
def safe?
|
32
57
|
@safe
|
33
58
|
end
|
34
59
|
|
60
|
+
# Whether or not the Method is idempotent.
|
61
|
+
#
|
62
|
+
# @return [Boolean]
|
35
63
|
def idempotent?
|
36
64
|
@idempotent
|
37
65
|
end
|
38
66
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@has_response_entity
|
67
|
+
# Converts the Method to a string.
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
def to_s
|
71
|
+
@name
|
45
72
|
end
|
46
73
|
|
74
|
+
# Compares the Method to another Method.
|
75
|
+
#
|
76
|
+
# Two Methods are equal if they have the same name.
|
77
|
+
#
|
78
|
+
# @param [#to_s] other_method the other Method
|
79
|
+
# @return [Boolean]
|
47
80
|
def ==(other_method)
|
48
|
-
|
81
|
+
to_s == other_method.to_s
|
49
82
|
end
|
50
83
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
TRACE = new 'TRACE', :safe => true, :idempotent => true, :entities => [:response]
|
61
|
-
CONNECT = new 'CONNECT', :safe => false, :idempotent => false, :entities => [:request, :response]
|
62
|
-
PATCH = new 'PATCH', :safe => false, :idempotent => false, :entities => [:request, :response]
|
84
|
+
OPTIONS = new('OPTIONS', :safe => true, :idempotent => true, :allowable_entities => [:response])
|
85
|
+
GET = new('GET', :safe => true, :idempotent => true, :allowable_entities => [:response])
|
86
|
+
HEAD = new('HEAD', :safe => true, :idempotent => true, :allowable_entities => [])
|
87
|
+
POST = new('POST', :safe => false, :idempotent => false, :allowable_entities => [:request, :response])
|
88
|
+
PUT = new('PUT', :safe => false, :idempotent => true, :allowable_entities => [:request, :response])
|
89
|
+
DELETE = new('DELETE', :safe => false, :idempotent => true, :allowable_entities => [:response])
|
90
|
+
TRACE = new('TRACE', :safe => true, :idempotent => true, :allowable_entities => [:response])
|
91
|
+
CONNECT = new('CONNECT', :safe => false, :idempotent => false, :allowable_entities => [:request, :response])
|
92
|
+
PATCH = new('PATCH', :safe => false, :idempotent => false, :allowable_entities => [:request, :response])
|
63
93
|
end
|
64
|
-
end
|
94
|
+
end
|
data/lib/webbed/request.rb
CHANGED
@@ -1,44 +1,88 @@
|
|
1
1
|
require 'addressable/uri'
|
2
2
|
|
3
3
|
module Webbed
|
4
|
+
# Representation of an HTTP Request.
|
5
|
+
#
|
6
|
+
# This class contains the absolute minimum for accessing the different parts
|
7
|
+
# of an HTTP Request. Helper modules provide far more functionality.
|
4
8
|
class Request
|
9
|
+
include GenericMessage
|
5
10
|
|
6
|
-
|
11
|
+
# The Request-URI of the Request.
|
12
|
+
#
|
13
|
+
# The method automatically converts the new value to an instance of
|
14
|
+
# `Addressable::URI` if it is not already one.
|
15
|
+
#
|
16
|
+
# @return [Addressable::URI]
|
17
|
+
# @note {Helpers::RequestURIHelper} aliases this method to `#request_url`.
|
7
18
|
attr_reader :request_uri
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
|
20
|
+
def request_uri=(request_uri)
|
21
|
+
@request_uri = Addressable::URI.parse(request_uri)
|
22
|
+
end
|
23
|
+
|
24
|
+
# The scheme of the Request.
|
25
|
+
#
|
26
|
+
# @return ['http', 'https']
|
27
|
+
attr_accessor :scheme
|
28
|
+
|
29
|
+
# Creates a new Request.
|
30
|
+
#
|
31
|
+
# The method converts the values passed in to their proper types.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# Webbed::Request.new('GET', 'http://example.com', {}, '')
|
35
|
+
#
|
36
|
+
# @param [Method, String] method
|
37
|
+
# @param [Addressable::URI, String] request_uri
|
38
|
+
# @param [Headers, Hash] headers
|
39
|
+
# @param [#to_s] entity_body
|
40
|
+
# @param [Array] options the options to create the Request with
|
41
|
+
# @option options [#to_s] :http_version (1.1) the HTTP-Version of the
|
42
|
+
# Request
|
43
|
+
# @option options ['http', 'https'] :scheme ('http') the scheme of the
|
44
|
+
# Request
|
45
|
+
def initialize(method, request_uri, headers, entity_body, options = {})
|
46
|
+
self.method = method
|
47
|
+
self.request_uri = request_uri
|
48
|
+
self.headers = headers
|
49
|
+
self.entity_body = entity_body
|
50
|
+
self.http_version = options[:http_version] || 1.1
|
51
|
+
self.scheme = options[:scheme] || 'http'
|
24
52
|
end
|
25
53
|
|
54
|
+
# The Method of the Request.
|
55
|
+
#
|
56
|
+
# @return [Method]
|
26
57
|
def method(*args)
|
27
58
|
return super(*args) unless args.empty?
|
28
59
|
@method
|
29
60
|
end
|
30
61
|
|
62
|
+
# Sets the Method of the Request.
|
63
|
+
#
|
64
|
+
# @param [Method] method
|
31
65
|
def method=(method)
|
32
66
|
@method = Webbed::Method.new(method)
|
33
67
|
end
|
34
68
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
69
|
+
# The Request-Line of the Request as defined in RFC 2616.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# request = Webbed::Request.new(['GET', 'http://example.com', {}, ''])
|
73
|
+
# request.request_line # => "GET * HTTP/1.1\r\n"
|
74
|
+
#
|
75
|
+
# @return [String]
|
39
76
|
def request_line
|
40
77
|
"#{method} #{request_uri} #{http_version}\r\n"
|
41
78
|
end
|
42
79
|
alias :start_line :request_line
|
80
|
+
|
81
|
+
include Helpers::MethodHelper
|
82
|
+
include Helpers::RequestURIHelper
|
83
|
+
include Helpers::RackRequestHelper
|
84
|
+
include Helpers::SchemeHelper
|
85
|
+
include Helpers::RequestHeadersHelper
|
86
|
+
include Helpers::EntityHeadersHelper
|
43
87
|
end
|
44
88
|
end
|
data/lib/webbed/response.rb
CHANGED
@@ -1,42 +1,83 @@
|
|
1
1
|
module Webbed
|
2
|
+
# Representation of an HTTP Response.
|
3
|
+
#
|
4
|
+
# This class contains the absolute minimum for accessing the different parts
|
5
|
+
# of an HTTP Response. Helper modules provide far more functionality.
|
2
6
|
class Response
|
7
|
+
include GenericMessage
|
3
8
|
|
4
|
-
|
9
|
+
STATUS_CODE_REGEX = /^(\d{3}) (.*)$/
|
10
|
+
|
11
|
+
# The Status Code of the Response.
|
12
|
+
#
|
13
|
+
# The method automatically converts the new value to an instance of
|
14
|
+
# {StatusCode} if it is not already one.
|
15
|
+
#
|
16
|
+
# @return [#to_i]
|
5
17
|
attr_reader :status_code
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
:status_code => 200,
|
10
|
-
:reason_phrase => nil,
|
11
|
-
:headers => {},
|
12
|
-
:entity_body => ''
|
13
|
-
}
|
14
|
-
|
15
|
-
def initialize(response_hash = {})
|
16
|
-
response_hash = DEFAULTS.merge(response_hash)
|
17
|
-
|
18
|
-
self.http_version = response_hash[:http_version]
|
19
|
-
self.status_code = response_hash[:status_code]
|
20
|
-
self.reason_phrase = response_hash[:reason_phrase]
|
21
|
-
self.headers.merge!(response_hash[:headers])
|
22
|
-
self.entity_body = response_hash[:entity_body]
|
18
|
+
|
19
|
+
def status_code=(status_code)
|
20
|
+
@status_code = Webbed::StatusCode.new(status_code)
|
23
21
|
end
|
24
22
|
|
23
|
+
# The Reason Phrase of the Response.
|
24
|
+
#
|
25
|
+
# The method returns the Status Code's default reason phrase if the Reason
|
26
|
+
# Phrase has not already been set.
|
27
|
+
#
|
28
|
+
# @return [String]
|
25
29
|
def reason_phrase
|
26
|
-
@reason_phrase
|
30
|
+
defined?(@reason_phrase) ? @reason_phrase : @status_code.default_reason_phrase
|
27
31
|
end
|
28
32
|
|
29
|
-
|
30
|
-
@status_code.default_reason_phrase
|
31
|
-
end
|
33
|
+
attr_writer :reason_phrase
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
+
# Creates a new Response.
|
36
|
+
#
|
37
|
+
# The Status Code may be passed in as a string with a Reason Phrase or just
|
38
|
+
# a number. If a number is provided, the default Reason Phrase for that
|
39
|
+
# Status Code is used.
|
40
|
+
#
|
41
|
+
# The method converts the values passed in to their proper types.
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# Webbed::Response.new(200, {}, '')
|
45
|
+
# Webbed::Response.new('404 Missing File', {}, '')
|
46
|
+
#
|
47
|
+
# @param [Fixnum, String] status_code
|
48
|
+
# @param [Headers, Hash] headers
|
49
|
+
# @param [#to_s] entity_body
|
50
|
+
# @param [Array] options the options to create the Response with @option
|
51
|
+
# @options options [#to_s] :http_version (1.1) the HTTP-Version of the
|
52
|
+
# Response
|
53
|
+
def initialize(status_code, headers, entity_body, options = {})
|
54
|
+
if STATUS_CODE_REGEX =~ status_code.to_s
|
55
|
+
self.status_code = $1
|
56
|
+
self.reason_phrase = $2
|
57
|
+
else
|
58
|
+
self.status_code = status_code
|
59
|
+
end
|
60
|
+
|
61
|
+
self.headers = headers
|
62
|
+
self.entity_body = entity_body
|
63
|
+
|
64
|
+
self.http_version = options.delete(:http_version) || 1.1
|
35
65
|
end
|
36
66
|
|
67
|
+
# The Status-Line of the Response.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# response = Webbed::Response.new([200, {}, ''])
|
71
|
+
# response.status_line # => "HTTP/1.1 200 OK\r\n"
|
72
|
+
#
|
73
|
+
# @return [String]
|
37
74
|
def status_line
|
38
75
|
"#{http_version} #{status_code} #{reason_phrase}\r\n"
|
39
76
|
end
|
40
77
|
alias :start_line :status_line
|
78
|
+
|
79
|
+
include Helpers::RackResponseHelper
|
80
|
+
include Helpers::ResponseHeadersHelper
|
81
|
+
include Helpers::EntityHeadersHelper
|
41
82
|
end
|
42
83
|
end
|
data/lib/webbed/status_code.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
module Webbed
|
2
|
+
# Representation of an HTTP Status Code.
|
2
3
|
class StatusCode
|
3
|
-
|
4
4
|
include Comparable
|
5
|
-
attr_reader :status_code, :default_reason_phrase
|
6
|
-
CACHED = {}
|
7
5
|
|
8
6
|
UNKNOWN_REASON_PHRASE = 'Unknown Status Code'
|
9
7
|
REASON_PHRASES = {
|
@@ -49,54 +47,123 @@ module Webbed
|
|
49
47
|
505 => 'HTTP Version not supported'
|
50
48
|
}
|
51
49
|
|
50
|
+
@@cached = {}
|
51
|
+
|
52
|
+
# The default Reason Phrase of the Status Code.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
attr_reader :default_reason_phrase
|
56
|
+
|
57
|
+
# Retrieves a Status Code from the cache or creates a new one.
|
58
|
+
#
|
59
|
+
# All created Status Codes are cached forever.
|
60
|
+
#
|
61
|
+
# @param (see #initialize)
|
62
|
+
# @return [StatusCode] the new or cached StatusCode
|
63
|
+
# @see #initialize
|
52
64
|
def self.new(status_code)
|
53
|
-
|
65
|
+
status_code = status_code.to_i
|
66
|
+
@@cached[status_code] ||= super(status_code)
|
54
67
|
end
|
55
68
|
|
69
|
+
# Creates a new Status Code.
|
70
|
+
#
|
71
|
+
# @param [Fixnum] status_code
|
56
72
|
def initialize(status_code)
|
57
|
-
status_code = status_code.to_i
|
58
73
|
@status_code = status_code
|
59
|
-
@default_reason_phrase = REASON_PHRASES[status_code] || UNKNOWN_REASON_PHRASE
|
74
|
+
@default_reason_phrase = REASON_PHRASES[@status_code] || UNKNOWN_REASON_PHRASE
|
60
75
|
end
|
61
76
|
|
77
|
+
# Comparse the Status Code to another Status Code.
|
78
|
+
#
|
79
|
+
# @param [#to_i] other_status_code the other Status Code
|
80
|
+
# @return [Fixnum] the sign of the comparison (either `1`, `0`, or `-1`)
|
62
81
|
def <=>(other_status_code)
|
63
|
-
status_code <=> other_status_code.to_i
|
82
|
+
@status_code <=> other_status_code.to_i
|
64
83
|
end
|
65
84
|
|
85
|
+
# Converts the Status Code to an integer.
|
86
|
+
#
|
87
|
+
# @return [Fixnum]
|
66
88
|
def to_i
|
67
|
-
status_code
|
89
|
+
@status_code
|
68
90
|
end
|
69
91
|
|
92
|
+
# Converts the Status Code to a string.
|
93
|
+
#
|
94
|
+
# @return [String]
|
70
95
|
def to_s
|
71
|
-
|
96
|
+
@status_code.to_s
|
72
97
|
end
|
73
98
|
|
99
|
+
# Whether or not the Status Code is informational.
|
100
|
+
#
|
101
|
+
# According to RFC 2616, informational status codes are in the range of 100
|
102
|
+
# to 199, inclusive.
|
103
|
+
#
|
104
|
+
# @return [Boolean]
|
74
105
|
def informational?
|
75
|
-
(100...200).include?
|
106
|
+
(100...200).include?(@status_code)
|
76
107
|
end
|
77
108
|
|
78
|
-
|
79
|
-
|
109
|
+
# Whether or not the Status Code is successful.
|
110
|
+
#
|
111
|
+
# According to RFC 2616, successful status codes are in the range of 200 to
|
112
|
+
# 299, inclusive.
|
113
|
+
#
|
114
|
+
# @return [Boolean]
|
115
|
+
def successful?
|
116
|
+
(200...300).include?(@status_code)
|
80
117
|
end
|
81
118
|
|
119
|
+
# Whether or not the Status Code is a redirection.
|
120
|
+
#
|
121
|
+
# According to RFC 2616, redirection status codes are in the range of 300 to
|
122
|
+
# 399, inclusive.
|
123
|
+
#
|
124
|
+
# @return [Boolean]
|
82
125
|
def redirection?
|
83
|
-
(300...400).include?
|
126
|
+
(300...400).include?(@status_code)
|
84
127
|
end
|
85
128
|
|
129
|
+
# Whether or not the Status Code is a client error.
|
130
|
+
#
|
131
|
+
# According to RFC 2616, client error status codes are in the range of 400
|
132
|
+
# to 499, inclusive.
|
133
|
+
#
|
134
|
+
# @return [Boolean]
|
86
135
|
def client_error?
|
87
|
-
(400...500).include?
|
136
|
+
(400...500).include?(@status_code)
|
88
137
|
end
|
89
138
|
|
139
|
+
# Whether or not the Status Code is a server error.
|
140
|
+
#
|
141
|
+
# According to RFC 2616, server error status codes are in the range of 500
|
142
|
+
# to 599, inclusive.
|
143
|
+
#
|
144
|
+
# @return [Boolean]
|
90
145
|
def server_error?
|
91
|
-
(500...600).include?
|
146
|
+
(500...600).include?(@status_code)
|
92
147
|
end
|
93
148
|
|
149
|
+
# Whether or not the Status Code is unknown.
|
150
|
+
#
|
151
|
+
# According to RFC 2616, the only defined Status Code ranges are from 100 to
|
152
|
+
# 599, inclusive. Anything outside that range is an unknown Status Code.
|
153
|
+
#
|
154
|
+
# @return [Boolean]
|
94
155
|
def unknown?
|
95
|
-
!(100...600).include?
|
156
|
+
!(100...600).include?(@status_code)
|
96
157
|
end
|
97
158
|
|
159
|
+
# Whether or not the Status Code is an error.
|
160
|
+
#
|
161
|
+
# According to RFC 2616, Status Codes that signify errors are in the range
|
162
|
+
# of 400 to 599, inclusive.
|
163
|
+
#
|
164
|
+
# @return [Boolean]
|
98
165
|
def error?
|
99
|
-
(400...600).include?
|
166
|
+
(400...600).include?(@status_code)
|
100
167
|
end
|
101
168
|
end
|
102
169
|
end
|