webbed 0.1.1 → 0.2.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.
- 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/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
coverage
|
2
|
+
rdoc
|
3
|
+
pkg
|
4
|
+
test/tmp
|
5
|
+
test/version_tmp
|
6
|
+
tmp
|
7
|
+
pkg
|
8
|
+
*.gem
|
9
|
+
*.rbc
|
10
|
+
lib/bundler/man
|
11
|
+
spec/reports
|
12
|
+
.config
|
13
|
+
InstalledFiles
|
14
|
+
.bundle
|
15
|
+
|
16
|
+
# YARD artifacts
|
17
|
+
.yardoc
|
18
|
+
_yardoc
|
19
|
+
doc/
|
20
|
+
|
21
|
+
# Gem-specific
|
22
|
+
Gemfile.lock
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
source
|
1
|
+
source :gemcutter
|
2
2
|
|
3
|
-
|
4
|
-
gem '
|
3
|
+
# This is temporary until guard-minitest accepts my patch.
|
4
|
+
gem 'guard-minitest', :git => 'git://github.com/CapnKernul/guard-minitest.git'
|
5
|
+
gem 'rb-fsevent'
|
5
6
|
|
6
|
-
|
7
|
-
gem 'rake'
|
8
|
-
end
|
9
|
-
|
10
|
-
group :test do
|
11
|
-
gem 'rspec', '~> 1.3', :require => 'spec'
|
12
|
-
gem 'bourne'
|
13
|
-
gem 'heckle'
|
14
|
-
gem 'ruby2ruby', '1.2.2'
|
15
|
-
end
|
7
|
+
gemspec
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,32 +1,37 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
*
|
29
|
-
|
30
|
-
|
31
|
-
|
1
|
+
# webbed - take control of HTTP [](http://stillmaintained.com/CapnKernul/webbed) [](http://travis-ci.org/CapnKernul/webbed) #
|
2
|
+
|
3
|
+
Webbed provides useful abstractions on top of the basic parts of the HTTP
|
4
|
+
specification, most notably HTTP Requests and Responses. It is far more
|
5
|
+
full-featured than `Rack::Request` and `Rack::Response`, allowing you to easily
|
6
|
+
access the complex features of HTTP.
|
7
|
+
|
8
|
+
## Usage ##
|
9
|
+
|
10
|
+
Coming soon. Check out the tests and documentation in the meantime.
|
11
|
+
|
12
|
+
## Installation ##
|
13
|
+
|
14
|
+
Without bundler:
|
15
|
+
|
16
|
+
gem install webbed
|
17
|
+
|
18
|
+
With bundler:
|
19
|
+
|
20
|
+
gem 'webbed'
|
21
|
+
|
22
|
+
## Note on Patches/Pull Requests ##
|
23
|
+
|
24
|
+
* Fork the project.
|
25
|
+
* Make your feature addition or bug fix.
|
26
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
27
|
+
* Commit, but do not mess with the `Rakefile`. If you want to have your own version, that is fine but bump the version in a commit by itself in another branch so I can ignore it when I pull.
|
28
|
+
* Send me a pull request. Bonus points for git flow feature branches.
|
29
|
+
|
30
|
+
## Resources ##
|
31
|
+
|
32
|
+
* [GitHub Repository](https://github.com/CapnKernul/webbed)
|
33
|
+
* [Documentation](http://rubydoc.info/github/CapnKernul/webbed)
|
34
|
+
|
35
|
+
## License ##
|
36
|
+
|
32
37
|
Webbed is licensed under the MIT License. See `LICENSE` for details.
|
data/Rakefile
CHANGED
@@ -1,18 +1,11 @@
|
|
1
1
|
require 'bundler'
|
2
|
-
Bundler.
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
|
5
|
-
require 'spec/rake/spectask'
|
4
|
+
task :default => :test
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
task :build => "#{gemspec.full_name}.gem"
|
14
|
-
|
15
|
-
file "#{gemspec.full_name}.gem" => gemspec.files + ['webbed.gemspec'] do
|
16
|
-
system 'gem build webbed.gemspec'
|
17
|
-
system "gem install webbed-#{Webbed::VERSION}.gem"
|
6
|
+
require 'rake/testtask'
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.ruby_opts += ['-rubygems']
|
9
|
+
t.libs << 'test'
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
18
11
|
end
|
data/lib/webbed.rb
CHANGED
@@ -1,9 +1,31 @@
|
|
1
|
+
# Webbed provides two important abstraction on the HTTP specification (RFC
|
2
|
+
# 2616), {Request} and {Response}. Although the core classes have the
|
3
|
+
# bare-minimum of functionality for interacting with the two classes, they have
|
4
|
+
# been extended with a variety of helper modules, adding a significant amount of
|
5
|
+
# semantic meaning to the instances of each class.
|
6
|
+
#
|
7
|
+
# Webbed can be used with or without Rack, but it includes Rack helpers for
|
8
|
+
# convenience.
|
9
|
+
#
|
10
|
+
# @todo Add exmaples of webbed in action.
|
1
11
|
module Webbed
|
2
|
-
autoload :Headers,
|
3
|
-
autoload :HTTPVersion,
|
12
|
+
autoload :Headers, 'webbed/headers'
|
13
|
+
autoload :HTTPVersion, 'webbed/http_version'
|
4
14
|
autoload :GenericMessage, 'webbed/generic_message'
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
15
|
+
autoload :MediaType, 'webbed/media_type'
|
16
|
+
autoload :Method, 'webbed/method'
|
17
|
+
autoload :StatusCode, 'webbed/status_code'
|
18
|
+
autoload :Request, 'webbed/request'
|
19
|
+
autoload :Response, 'webbed/response'
|
20
|
+
|
21
|
+
module Helpers
|
22
|
+
autoload :MethodHelper, 'webbed/helpers/method_helper'
|
23
|
+
autoload :RackRequestHelper, 'webbed/helpers/rack_request_helper'
|
24
|
+
autoload :RackResponseHelper, 'webbed/helpers/rack_response_helper'
|
25
|
+
autoload :RequestURIHelper, 'webbed/helpers/request_uri_helper'
|
26
|
+
autoload :SchemeHelper, 'webbed/helpers/scheme_helper'
|
27
|
+
autoload :RequestHeadersHelper, 'webbed/helpers/request_headers_helper'
|
28
|
+
autoload :ResponseHeadersHelper, 'webbed/helpers/response_headers_helper'
|
29
|
+
autoload :EntityHeadersHelper, 'webbed/helpers/entity_headers_helper'
|
30
|
+
end
|
9
31
|
end
|
@@ -1,28 +1,40 @@
|
|
1
1
|
module Webbed
|
2
|
+
# Generic methods used for both {Request}'s and {Response}'s.
|
3
|
+
#
|
4
|
+
# @abstract Include and implement `#status_line`.
|
2
5
|
module GenericMessage
|
3
|
-
|
6
|
+
# The HTTP-Version of the message.
|
7
|
+
#
|
8
|
+
# The method automatically converts the new value to an instance of
|
9
|
+
# {HTTPVersion} if it is not already one.
|
10
|
+
#
|
11
|
+
# @return [HTTPVersion]
|
4
12
|
attr_reader :http_version
|
5
|
-
attr_accessor :entity_body
|
6
|
-
|
7
|
-
# This is purely for test/spec purposes.
|
8
|
-
def initialize
|
9
|
-
self.http_version = 'HTTP/1.1'
|
10
|
-
self.entity_body = ''
|
11
|
-
end
|
12
|
-
|
13
|
-
# Must be overridden!
|
14
|
-
def start_line
|
15
|
-
"Invalid Start Line\r\n"
|
16
|
-
end
|
17
13
|
|
18
14
|
def http_version=(http_version)
|
19
15
|
@http_version = Webbed::HTTPVersion.new(http_version)
|
20
16
|
end
|
21
17
|
|
22
|
-
|
23
|
-
|
18
|
+
# The Headers of the message.
|
19
|
+
#
|
20
|
+
# The method automatically converts the new value to an instance of
|
21
|
+
# {Headers} if it is not already one.
|
22
|
+
#
|
23
|
+
# @return [Headers]
|
24
|
+
attr_reader :headers
|
25
|
+
|
26
|
+
def headers=(headers)
|
27
|
+
@headers = Webbed::Headers.new(headers)
|
24
28
|
end
|
25
29
|
|
30
|
+
# The Entity Body of the message.
|
31
|
+
#
|
32
|
+
# @return [#to_s]
|
33
|
+
attr_accessor :entity_body
|
34
|
+
|
35
|
+
# Converts the message into an HTTP Message as per RFC 2616.
|
36
|
+
#
|
37
|
+
# @return [String]
|
26
38
|
def to_s
|
27
39
|
"#{start_line}#{headers.to_s}\r\n#{entity_body}"
|
28
40
|
end
|
data/lib/webbed/headers.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Webbed
|
2
|
+
module Helpers
|
3
|
+
# Request and Response helper for Entity Headers
|
4
|
+
module EntityHeadersHelper
|
5
|
+
# The Content-Length of the Entity (as defined in the Content-Length Header).
|
6
|
+
#
|
7
|
+
# @return [Fixnum, nil]
|
8
|
+
def content_length
|
9
|
+
headers['Content-Length'] ? headers['Content-Length'].to_i : nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the Content-Length of the Entity (as defined in the Content-Length Header).
|
13
|
+
#
|
14
|
+
# @param [#to_s] content_length
|
15
|
+
def content_length=(content_length)
|
16
|
+
headers['Content-Length'] = content_length.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
# The Content-Location of the Entity (as defined in the Content-Location Header).
|
20
|
+
#
|
21
|
+
# @return [Addressable::URI, nil]
|
22
|
+
def content_location
|
23
|
+
headers['Content-Location'] ? Addressable::URI.parse(headers['Content-Location']) : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets the Content-Location of the Entity (as defined in the Content-Location Header).
|
27
|
+
#
|
28
|
+
# @param [#to_s] content_location
|
29
|
+
def content_location=(content_location)
|
30
|
+
headers['Content-Location'] = content_location.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
# The Content-MD5 of the Entity (as defined in the Content-MD5 Header).
|
34
|
+
#
|
35
|
+
# @return [String, nil]
|
36
|
+
def content_md5
|
37
|
+
headers['Content-MD5']
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets the Content-MD5 of the Entity (as defined in the Content-MD5 Header).
|
41
|
+
#
|
42
|
+
# @param [String] content_md5
|
43
|
+
def content_md5=(content_md5)
|
44
|
+
headers['Content-MD5'] = content_md5
|
45
|
+
end
|
46
|
+
|
47
|
+
# The Content-Type of the Entity (as defined in the Content-Type Header).
|
48
|
+
#
|
49
|
+
# @return [MediaType, nil]
|
50
|
+
def content_type
|
51
|
+
headers['Content-Type'] ? Webbed::MediaType.new(headers['Content-Type']) : nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Sets the Content-Type of the Entity (as defined in the Content-Type Header).
|
55
|
+
#
|
56
|
+
# @param [#to_s] content_type
|
57
|
+
def content_type=(content_type)
|
58
|
+
headers['Content-Type'] = content_type.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Webbed
|
2
|
+
module Helpers
|
3
|
+
# Request helper for the Method
|
4
|
+
module MethodHelper
|
5
|
+
# Whether or not the Request is safe based on the Method
|
6
|
+
#
|
7
|
+
# @return [Boolean]
|
8
|
+
def safe?
|
9
|
+
method.safe?
|
10
|
+
end
|
11
|
+
|
12
|
+
# Whether or not the Request is idempotent based on the Method
|
13
|
+
#
|
14
|
+
# @return [Boolean]
|
15
|
+
def idempotent?
|
16
|
+
method.idempotent?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Whether or not the Request uses the OPTIONS Method
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
def options?
|
23
|
+
method == 'OPTIONS'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Whether or not the Request uses the GET Method
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
def get?
|
30
|
+
method == 'GET'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Whether or not the Request uses the HEAD Method
|
34
|
+
#
|
35
|
+
# @return [Boolean]
|
36
|
+
def head?
|
37
|
+
method == 'HEAD'
|
38
|
+
end
|
39
|
+
|
40
|
+
# Whether or not the Request uses the POST Method
|
41
|
+
#
|
42
|
+
# @return [Boolean]
|
43
|
+
def post?
|
44
|
+
method == 'POST'
|
45
|
+
end
|
46
|
+
|
47
|
+
# Whether or not the Request uses the PUT Method
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
def put?
|
51
|
+
method == 'PUT'
|
52
|
+
end
|
53
|
+
|
54
|
+
# Whether or not the Request uses the DELETE Method
|
55
|
+
#
|
56
|
+
# @return [Boolean]
|
57
|
+
def delete?
|
58
|
+
method == 'DELETE'
|
59
|
+
end
|
60
|
+
|
61
|
+
# Whether or not the Request uses the TRACE Method
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
def trace?
|
65
|
+
method == 'TRACE'
|
66
|
+
end
|
67
|
+
|
68
|
+
# Whether or not the Request uses the CONNECT Method
|
69
|
+
#
|
70
|
+
# @return [Boolean]
|
71
|
+
def connect?
|
72
|
+
method == 'CONNECT'
|
73
|
+
end
|
74
|
+
|
75
|
+
# Whether or not the Request uses the PATCH Method
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
def patch?
|
79
|
+
method == 'PATCH'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Webbed
|
2
|
+
module Helpers
|
3
|
+
# Request helper for converting Rack environments into Requests
|
4
|
+
module RackRequestHelper
|
5
|
+
module ClassMethods
|
6
|
+
# Create a new Request from a Rack environment
|
7
|
+
#
|
8
|
+
# This method will take the HTTP-Version, Method, Request-URI, Headers,
|
9
|
+
# Entity Body, and scheme from the Rack environment and create a new
|
10
|
+
# Request using that data. The Rack environment can be accessed through
|
11
|
+
# `#rack_env`.
|
12
|
+
#
|
13
|
+
# @param [Hash{String => String}] rack_env
|
14
|
+
# @return [Request]
|
15
|
+
# @note The Rack environment will never be modified.
|
16
|
+
def from_rack(rack_env)
|
17
|
+
env = rack_env.dup
|
18
|
+
method = env.delete('REQUEST_METHOD')
|
19
|
+
request_uri = "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
|
20
|
+
http_version = env.delete('HTTP_VERSION') || 'HTTP/1.1'
|
21
|
+
scheme = env.delete('rack.url_scheme')
|
22
|
+
entity_body = env.delete('rack.input')
|
23
|
+
|
24
|
+
headers = env.inject({}) do |memo, h|
|
25
|
+
memo[$1.gsub('_', '-')] = h[1] if h[0] =~ /^HTTP_(.*)/
|
26
|
+
memo
|
27
|
+
end
|
28
|
+
|
29
|
+
headers['Content-Type'] = env.delete('CONTENT_TYPE')
|
30
|
+
headers['Content-Length'] = env.delete('CONTENT_LENGTH')
|
31
|
+
|
32
|
+
request = new(method, request_uri, headers, entity_body, {
|
33
|
+
:http_version => http_version,
|
34
|
+
:scheme => scheme
|
35
|
+
})
|
36
|
+
|
37
|
+
request.rack_env = rack_env
|
38
|
+
|
39
|
+
request
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module InstanceMethods
|
44
|
+
# The Rack environment of the Request
|
45
|
+
#
|
46
|
+
# @return [Hash{String => String}]
|
47
|
+
attr_accessor :rack_env
|
48
|
+
|
49
|
+
# Generate the Rack enivronment equivalent of the Request.
|
50
|
+
#
|
51
|
+
# If the Request was created with an environment, the generated
|
52
|
+
# environment will be merged with the original environment. The original
|
53
|
+
# will not be modified.
|
54
|
+
#
|
55
|
+
# @return [Hash{String => String}]
|
56
|
+
def to_rack
|
57
|
+
env = rack_env ? rack_env.dup : {}
|
58
|
+
env['REQUEST_METHOD'] = method.to_s
|
59
|
+
env['PATH_INFO'] = request_uri.path
|
60
|
+
env['QUERY_STRING'] = request_uri.query
|
61
|
+
env['HTTP_VERSION'] = http_version.to_s
|
62
|
+
env['rack.url_scheme'] = scheme
|
63
|
+
env['rack.input'] = entity_body
|
64
|
+
|
65
|
+
rack_headers = headers.dup
|
66
|
+
env['CONTENT_TYPE'] = rack_headers.delete('Content-Type')
|
67
|
+
env['CONTENT_LENGTH'] = rack_headers.delete('Content-Length')
|
68
|
+
|
69
|
+
rack_headers.each do |header, value|
|
70
|
+
header = header.upcase.gsub('-', '_')
|
71
|
+
env["HTTP_#{header}"] = value
|
72
|
+
end
|
73
|
+
|
74
|
+
env
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @see ClassMethods
|
79
|
+
# @see InstanceMethods
|
80
|
+
def self.included(base)
|
81
|
+
base.send :include, InstanceMethods
|
82
|
+
base.send :extend, ClassMethods
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|