webbed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+