webbed 0.1.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/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'webbed', :path => File.expand_path("..", __FILE__)
4
+ gem 'addressable', '~> 2.1'
5
+
6
+ group :development do
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
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Alexander Kern and Artcentric Networks LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+ Bundler.setup :default, :development, :test
3
+
4
+ require 'webbed'
5
+ require 'spec/rake/spectask'
6
+
7
+ Spec::Rake::SpecTask.new do |t|
8
+ t.libs << 'lib'
9
+ end
10
+
11
+ gemspec = eval(File.read('webbed.gemspec'))
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"
18
+ end
@@ -0,0 +1,30 @@
1
+ module Webbed
2
+ module GenericMessage
3
+
4
+ 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
+
18
+ def http_version=(http_version)
19
+ @http_version = Webbed::HTTPVersion.new(http_version)
20
+ end
21
+
22
+ def headers
23
+ @headers ||= Webbed::Headers.new
24
+ end
25
+
26
+ def to_s
27
+ "#{start_line}#{headers.to_s}\r\n#{entity_body}"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,79 @@
1
+ module Webbed
2
+ # This class is stolen directly from Rack.
3
+ class Headers < Hash
4
+ def self.new(hash={})
5
+ Headers === hash ? hash : super(hash)
6
+ end
7
+
8
+ def initialize(hash={})
9
+ super()
10
+ @names = {}
11
+ hash.each { |k, v| self[k] = v }
12
+ end
13
+
14
+ def each
15
+ super do |k, v|
16
+ yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
17
+ end
18
+ end
19
+
20
+ def to_hash
21
+ inject({}) do |hash, (k,v)|
22
+ if v.respond_to? :to_ary
23
+ hash[k] = v.to_ary.join("\n")
24
+ else
25
+ hash[k] = v
26
+ end
27
+ hash
28
+ end
29
+ end
30
+
31
+ def [](k)
32
+ super(@names[k]) if @names[k]
33
+ super(@names[k.downcase])
34
+ end
35
+
36
+ def []=(k, v)
37
+ delete k
38
+ @names[k] = @names[k.downcase] = k
39
+ super k, v
40
+ end
41
+
42
+ def delete(k)
43
+ canonical = k.downcase
44
+ result = super @names.delete(canonical)
45
+ @names.delete_if { |name,| name.downcase == canonical }
46
+ result
47
+ end
48
+
49
+ def include?(k)
50
+ @names.include?(k) || @names.include?(k.downcase)
51
+ end
52
+
53
+ alias_method :has_key?, :include?
54
+ alias_method :member?, :include?
55
+ alias_method :key?, :include?
56
+
57
+ def merge!(other)
58
+ other.each { |k, v| self[k] = v }
59
+ self
60
+ end
61
+
62
+ def merge(other)
63
+ hash = dup
64
+ hash.merge! other
65
+ end
66
+
67
+ def replace(other)
68
+ clear
69
+ other.each { |k, v| self[k] = v }
70
+ self
71
+ end
72
+
73
+ def to_s
74
+ map do |field_name, field_content|
75
+ "#{field_name}: #{field_content}\r\n"
76
+ end.join
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,55 @@
1
+ module Webbed
2
+ class HTTPVersion
3
+
4
+ include Comparable
5
+ REGEX = /^HTTP\/(\d+\.\d+)$/
6
+
7
+ def self.new(http_version, dup = false)
8
+ unless dup
9
+ if ['HTTP/1.1', 1.1].include? http_version
10
+ return ONE_POINT_ONE
11
+ end
12
+
13
+ if ['HTTP/1.0', 1.0].include? http_version
14
+ return ONE_POINT_OH
15
+ end
16
+ end
17
+
18
+ super(http_version)
19
+ end
20
+
21
+ def initialize(http_version)
22
+ @http_version = to_f(http_version)
23
+ end
24
+
25
+ def to_s
26
+ "HTTP/#{to_f}"
27
+ end
28
+ alias :inspect :to_s
29
+
30
+ # This feels like a hack. Oh well.
31
+ def to_f(http_version = @http_version)
32
+ if http_version.respond_to? :match
33
+ REGEX.match(http_version)[1].to_f
34
+ else
35
+ http_version.to_f
36
+ end
37
+ end
38
+
39
+ def <=>(other_version)
40
+ to_f <=> to_f(other_version)
41
+ end
42
+
43
+ def major
44
+ to_f.floor
45
+ end
46
+
47
+ # TODO: Fix this ugly hack! :x
48
+ def minor
49
+ (to_f - to_f.floor).to_s[2..-1].to_i
50
+ end
51
+
52
+ ONE_POINT_ONE = HTTPVersion.new(1.1, true)
53
+ ONE_POINT_OH = HTTPVersion.new(1.0, true)
54
+ end
55
+ end
@@ -0,0 +1,64 @@
1
+ module Webbed
2
+ class Method
3
+
4
+ attr_reader :name
5
+ alias :to_s :name
6
+ alias :inspect :name
7
+
8
+ DEFAULTS = {
9
+ :safe => false,
10
+ :idempotent => false,
11
+ :entities => [:request, :response]
12
+ }
13
+
14
+ def self.new(name, options = {})
15
+ if const_defined? name
16
+ const_get(name)
17
+ else
18
+ super(name, options)
19
+ end
20
+ end
21
+
22
+ def initialize(name, options = {})
23
+ options = DEFAULTS.merge options
24
+ @name = name
25
+ @safe = options[:safe]
26
+ @idempotent = options[:safe] || options[:idempotent]
27
+ @has_request_entity = options[:entities].include? :request
28
+ @has_response_entity = options[:entities].include? :response
29
+ end
30
+
31
+ def safe?
32
+ @safe
33
+ end
34
+
35
+ def idempotent?
36
+ @idempotent
37
+ end
38
+
39
+ def has_request_entity?
40
+ @has_request_entity
41
+ end
42
+
43
+ def has_response_entity?
44
+ @has_response_entity
45
+ end
46
+
47
+ def ==(other_method)
48
+ name == other_method.to_s
49
+ end
50
+
51
+ # Common methods used and their settings. Most are defined in RFC 2616 with
52
+ # the exception of PATCH which is defined in RFC 5789. These are for caching
53
+ # purposes, so that new objects don't need to be created on each request.
54
+ OPTIONS = new 'OPTIONS', :safe => true, :idempotent => true, :entities => [:response]
55
+ GET = new 'GET', :safe => true, :idempotent => true, :entities => [:response]
56
+ HEAD = new 'HEAD', :safe => true, :idempotent => true, :entities => []
57
+ POST = new 'POST', :safe => false, :idempotent => false, :entities => [:request, :response]
58
+ PUT = new 'PUT', :safe => false, :idempotent => true, :entities => [:request, :response]
59
+ DELETE = new 'DELETE', :safe => false, :idempotent => true, :entities => [:response]
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]
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ require 'addressable/uri'
2
+
3
+ module Webbed
4
+ class Request
5
+
6
+ include Webbed::GenericMessage
7
+ attr_reader :request_uri
8
+ DEFAULTS = {
9
+ :method => 'GET',
10
+ :request_uri => '*',
11
+ :http_version => 'HTTP/1.1',
12
+ :headers => {},
13
+ :entity_body => ''
14
+ }
15
+
16
+ def initialize(request_hash = {})
17
+ request_hash = DEFAULTS.merge(request_hash)
18
+
19
+ self.method = request_hash[:method]
20
+ self.request_uri = request_hash[:request_uri]
21
+ self.http_version = request_hash[:http_version]
22
+ self.headers.merge!(request_hash[:headers])
23
+ self.entity_body = request_hash[:entity_body]
24
+ end
25
+
26
+ def method(*args)
27
+ return super(*args) unless args.empty?
28
+ @method
29
+ end
30
+
31
+ def method=(method)
32
+ @method = Webbed::Method.new(method)
33
+ end
34
+
35
+ def request_uri=(uri)
36
+ @request_uri = Addressable::URI.parse(uri)
37
+ end
38
+
39
+ def request_line
40
+ "#{method} #{request_uri} #{http_version}\r\n"
41
+ end
42
+ alias :start_line :request_line
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ module Webbed
2
+ class Response
3
+
4
+ include Webbed::GenericMessage
5
+ attr_reader :status_code
6
+ attr_writer :reason_phrase
7
+ DEFAULTS = {
8
+ :http_version => 'HTTP/1.1',
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]
23
+ end
24
+
25
+ def reason_phrase
26
+ @reason_phrase || default_reason_phrase
27
+ end
28
+
29
+ def default_reason_phrase
30
+ @status_code.default_reason_phrase
31
+ end
32
+
33
+ def status_code=(status_code)
34
+ @status_code = Webbed::StatusCode.new(status_code)
35
+ end
36
+
37
+ def status_line
38
+ "#{http_version} #{status_code} #{reason_phrase}\r\n"
39
+ end
40
+ alias :start_line :status_line
41
+ end
42
+ end
@@ -0,0 +1,102 @@
1
+ module Webbed
2
+ class StatusCode
3
+
4
+ include Comparable
5
+ attr_reader :status_code, :default_reason_phrase
6
+ CACHED = {}
7
+
8
+ UNKNOWN_REASON_PHRASE = 'Unknown Status Code'
9
+ REASON_PHRASES = {
10
+ 100 => 'Continue',
11
+ 101 => 'Switching Protocols',
12
+ 200 => 'OK',
13
+ 201 => 'Created',
14
+ 202 => 'Accepted',
15
+ 203 => 'Non-Authoritative Information',
16
+ 204 => 'No Content',
17
+ 205 => 'Reset Content',
18
+ 206 => 'Partial Content',
19
+ 300 => 'Multiple Choices',
20
+ 301 => 'Moved Permanently',
21
+ 302 => 'Found',
22
+ 303 => 'See Other',
23
+ 304 => 'Not Modified',
24
+ 305 => 'Use Proxy',
25
+ 307 => 'Temporary Redirect',
26
+ 400 => 'Bad Request',
27
+ 401 => 'Unauthorized',
28
+ 402 => 'Payment Required',
29
+ 403 => 'Forbidden',
30
+ 404 => 'Not Found',
31
+ 405 => 'Method Not Allowed',
32
+ 406 => 'Not Acceptable',
33
+ 407 => 'Proxy Authentication Required',
34
+ 408 => 'Request Time-out',
35
+ 409 => 'Conflict',
36
+ 410 => 'Gone',
37
+ 411 => 'Length Required',
38
+ 412 => 'Precondition Failed',
39
+ 413 => 'Request Entity Too Large',
40
+ 414 => 'Request-URI Too Large',
41
+ 415 => 'Unsupported Media Type',
42
+ 416 => 'Requested range not satisfiable',
43
+ 417 => 'Expectation Failed',
44
+ 500 => 'Internal Server Error',
45
+ 501 => 'Not Implemented',
46
+ 502 => 'Bad Gateway',
47
+ 503 => 'Service Unavailable',
48
+ 504 => 'Gateway Time-out',
49
+ 505 => 'HTTP Version not supported'
50
+ }
51
+
52
+ def self.new(status_code)
53
+ CACHED[status_code] ||= super(status_code)
54
+ end
55
+
56
+ def initialize(status_code)
57
+ status_code = status_code.to_i
58
+ @status_code = status_code
59
+ @default_reason_phrase = REASON_PHRASES[status_code] || UNKNOWN_REASON_PHRASE
60
+ end
61
+
62
+ def <=>(other_status_code)
63
+ status_code <=> other_status_code.to_i
64
+ end
65
+
66
+ def to_i
67
+ status_code
68
+ end
69
+
70
+ def to_s
71
+ '%03d' % status_code
72
+ end
73
+
74
+ def informational?
75
+ (100...200).include? status_code
76
+ end
77
+
78
+ def success?
79
+ (200...300).include? status_code
80
+ end
81
+
82
+ def redirection?
83
+ (300...400).include? status_code
84
+ end
85
+
86
+ def client_error?
87
+ (400...500).include? status_code
88
+ end
89
+
90
+ def server_error?
91
+ (500...600).include? status_code
92
+ end
93
+
94
+ def unknown?
95
+ !(100...600).include? status_code
96
+ end
97
+
98
+ def error?
99
+ (400...600).include? status_code
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,3 @@
1
+ module Webbed
2
+ VERSION = '0.1.0'
3
+ end
data/lib/webbed.rb ADDED
@@ -0,0 +1,9 @@
1
+ module Webbed
2
+ autoload :Headers, 'webbed/headers'
3
+ autoload :HTTPVersion, 'webbed/http_version'
4
+ autoload :GenericMessage, 'webbed/generic_message'
5
+ autoload :Method, 'webbed/method'
6
+ autoload :StatusCode, 'webbed/status_code'
7
+ autoload :Request, 'webbed/request'
8
+ autoload :Response, 'webbed/response'
9
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webbed
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Alexander Kern
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-27 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ hash: 1
28
+ segments:
29
+ - 2
30
+ - 1
31
+ version: "2.1"
32
+ requirement: *id001
33
+ name: addressable
34
+ prerelease: false
35
+ type: :runtime
36
+ - !ruby/object:Gem::Dependency
37
+ version_requirements: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ requirement: *id002
47
+ name: rake
48
+ prerelease: false
49
+ type: :development
50
+ description: Consistent library of manipulating HTTP messages.
51
+ email: alex@kernul.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - lib/webbed/generic_message.rb
60
+ - lib/webbed/headers.rb
61
+ - lib/webbed/http_version.rb
62
+ - lib/webbed/method.rb
63
+ - lib/webbed/request.rb
64
+ - lib/webbed/response.rb
65
+ - lib/webbed/status_code.rb
66
+ - lib/webbed/version.rb
67
+ - lib/webbed.rb
68
+ - LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - Gemfile
72
+ has_rdoc: true
73
+ homepage: http://github.com/CapnKernul/webbed
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 57
87
+ segments:
88
+ - 1
89
+ - 8
90
+ - 7
91
+ version: 1.8.7
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ hash: 3
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project: webbed
104
+ rubygems_version: 1.3.7
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Take control of HTTP.
108
+ test_files: []
109
+