righttp 0.0.2 → 0.1.1
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 +2 -0
- data/README.textile +58 -0
- data/VERSION +1 -1
- data/doc/mission.textile +49 -0
- data/lib/rig/http.rb +91 -140
- data/lib/rig/http_body.rb +98 -0
- data/lib/rig/http_exceptions.rb +1 -0
- data/lib/rig/http_header.rb +36 -0
- data/lib/rig/http_response.rb +54 -0
- data/righttp.gemspec +10 -4
- data/test/fixtures/yay.gif +0 -0
- data/test/test_http.rb +199 -52
- metadata +12 -6
- data/README.rdoc +0 -17
data/.gitignore
CHANGED
data/README.textile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
h1. righttp
|
2
|
+
|
3
|
+
righttp is a lightweigt http client library which is intended to simplify http requests.
|
4
|
+
|
5
|
+
It is in a very early stage and patches, suggestions and ideas how to improve it are highly appreciated.
|
6
|
+
|
7
|
+
h2. Usage
|
8
|
+
|
9
|
+
Currently there is only one way to perform a request:
|
10
|
+
|
11
|
+
h3. Minimal version
|
12
|
+
|
13
|
+
bc. request = Rig::HTTP.new( :host => "ruby-lang.org" )
|
14
|
+
response = request.send
|
15
|
+
|
16
|
+
h3. More elaborate version
|
17
|
+
|
18
|
+
bc. request = Rig::HTTP.new(
|
19
|
+
:host => "ruby-lang.org",
|
20
|
+
:path => "/en/documentation/",
|
21
|
+
:params => {"love" => "true"}
|
22
|
+
:method => "GET",
|
23
|
+
:header => "x-CUSTOM-HEADER" => "Awesome"
|
24
|
+
)
|
25
|
+
response = request.send
|
26
|
+
|
27
|
+
There will be wrapper methods for the different HTTP methods in the future.
|
28
|
+
It is also possible to do multipart form POST/PUT requests with multiple
|
29
|
+
files attached.
|
30
|
+
|
31
|
+
h3. Multipart Post
|
32
|
+
|
33
|
+
bc. request = Rig::HTTP.new(
|
34
|
+
:host => "example.org",
|
35
|
+
:path => "/posts",
|
36
|
+
:params => { "title" => "Oh Hai", "upload" => File.open("/path/to/file.jpg") }
|
37
|
+
)
|
38
|
+
response = request.send
|
39
|
+
|
40
|
+
The file will be closed once the multipart is created.
|
41
|
+
|
42
|
+
h3. Responses
|
43
|
+
|
44
|
+
When a request is send it returns a Rig::HTTPResponse which has two attributes: header and body
|
45
|
+
|
46
|
+
h2. Note on Patches/Pull Requests
|
47
|
+
|
48
|
+
* Fork the project.
|
49
|
+
* Make your feature addition or bug fix.
|
50
|
+
* Add tests for it. This is important so I don't break it in a
|
51
|
+
future version unintentionally.
|
52
|
+
* Commit, do not mess with rakefile, version, or history.
|
53
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
54
|
+
* Send me a pull request. Bonus points for topic branches.
|
55
|
+
|
56
|
+
h2. Copyright
|
57
|
+
|
58
|
+
Copyright (c) 2010 hukl. See LICENSE for details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
data/doc/mission.textile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
h1. righttp
|
2
|
+
|
3
|
+
h2. What righttp wants to achieve
|
4
|
+
|
5
|
+
righttp is intended to provide a simple and clean interface for HTTP requests while allowing to override almost every parameter of the request if it is neccessary.
|
6
|
+
|
7
|
+
It is not just a wrapper of net/http
|
8
|
+
|
9
|
+
Its philosophy is to get out of your way. You want to make a quick custom HTTP request? Then just do it like you think it should work.
|
10
|
+
|
11
|
+
Below you can see the various ways to make a request:
|
12
|
+
|
13
|
+
h2. Interface
|
14
|
+
|
15
|
+
bc.. HTTP.get( 'http://foobar.com' )
|
16
|
+
|
17
|
+
HTTP.get( 'http://foobar.com?foo=bar&baz=bang')
|
18
|
+
|
19
|
+
HTTP.get( 'http://foobar.com:3000?foo=bar&baz=bang')
|
20
|
+
|
21
|
+
HTTP.get( 'http://foobar.com', :params => {"foo" => "bar", :baz => "bang"} )
|
22
|
+
|
23
|
+
HTTP.get(
|
24
|
+
'http://foobar.com',
|
25
|
+
:params => {"foo" => "bar", :baz => "bang"}
|
26
|
+
)
|
27
|
+
|
28
|
+
HTTP.get(
|
29
|
+
:host => 'foobar.com',
|
30
|
+
:path => "/posts",
|
31
|
+
:port => 3000
|
32
|
+
:params => {"foo" => "bar", :baz => "bang"},
|
33
|
+
)
|
34
|
+
|
35
|
+
HTTP.post( 'foobar.com', :params => {"foo" => "bar"} )
|
36
|
+
|
37
|
+
HTTP.post(
|
38
|
+
'foobar.com',
|
39
|
+
:params => {"foo" => "bar", :attachment => File.open("path/to/file.jpg")}
|
40
|
+
)
|
41
|
+
|
42
|
+
h2. Components
|
43
|
+
|
44
|
+
* request preparation
|
45
|
+
* sending the request
|
46
|
+
* parsing the response
|
47
|
+
|
48
|
+
|
49
|
+
{ uri = URI::Generic }
|
data/lib/rig/http.rb
CHANGED
@@ -1,178 +1,129 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'uri'
|
3
|
+
require 'rig/http_response'
|
4
|
+
require 'rig/http_header'
|
5
|
+
require 'rig/http_body'
|
6
|
+
require 'rig/http_exceptions'
|
2
7
|
|
3
8
|
module Rig
|
4
9
|
|
5
10
|
CRLF = "\r\n"
|
11
|
+
HTTPMethods = %w(GET POST PUT DELETE)
|
6
12
|
|
7
13
|
class HTTP
|
14
|
+
attr_reader :options, :header, :body
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@host = options[:host] || raise(ArgumentError, "No host specified")
|
13
|
-
@port = options[:port] || 80
|
14
|
-
@params = options[:params] || {}
|
15
|
-
@method = options[:method] || "GET"
|
16
|
-
@path = options[:path] || "/"
|
17
|
-
@header = HTTPHeader.new( "" => "#{@method} #{@path} HTTP/1.1" )
|
18
|
-
@custom_header = HTTPHeader.new( options[:header] || {} )
|
19
|
-
@body = HTTPBody.new
|
20
|
-
@tcp_socket = TCPSocket.new( @host, @port )
|
21
|
-
end
|
16
|
+
def initialize *options
|
17
|
+
@options = normalize_options( options )
|
18
|
+
@body = HTTPBody.new( @options )
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
update_header
|
20
|
+
@options.merge!(
|
21
|
+
:content_type => @body.content_type,
|
22
|
+
:content_length => @body.content_length
|
23
|
+
)
|
24
|
+
|
25
|
+
@header = HTTPHeader.new( @options )
|
30
26
|
end
|
31
27
|
|
32
28
|
def send
|
33
29
|
begin
|
34
|
-
|
35
|
-
|
36
|
-
response =
|
30
|
+
tcp_socket = TCPSocket.new( @options[:host], @options[:port] )
|
31
|
+
tcp_socket.write( @header.to_s)# + @body.to_s )
|
32
|
+
response = tcp_socket.read
|
37
33
|
rescue => exception
|
38
34
|
puts exception.message
|
39
35
|
ensure
|
40
|
-
|
36
|
+
tcp_socket.close
|
41
37
|
end
|
42
38
|
|
43
|
-
response || exception.message
|
44
|
-
end
|
45
|
-
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
39
|
+
HTTPResponse.new( response ) || exception.message
|
40
|
+
end
|
41
|
+
|
42
|
+
def with_body?
|
43
|
+
%w(POST PUT).include?( options[:http_method] ) && options[:body]
|
44
|
+
end
|
45
|
+
|
46
|
+
def http_method
|
47
|
+
@options[:http_method] || "GET"
|
48
|
+
end
|
49
|
+
|
50
|
+
def normalize_options options
|
51
|
+
options.flatten!
|
52
|
+
|
53
|
+
options = case HTTP.options_mode( options )
|
54
|
+
when :simple
|
55
|
+
uri = URI.parse( options.first )
|
56
|
+
{
|
57
|
+
:host => uri.host,
|
58
|
+
:port => uri.port,
|
59
|
+
:path => uri.path,
|
60
|
+
:query => uri.query
|
61
|
+
}
|
62
|
+
when :mixed
|
63
|
+
uri = URI.parse( options.first )
|
64
|
+
uri_options = {
|
65
|
+
:host => uri.host,
|
66
|
+
:port => uri.port,
|
67
|
+
:path => uri.path,
|
68
|
+
:query => uri.query
|
69
|
+
}
|
70
|
+
uri_options.merge( options.last )
|
71
|
+
when :advanced
|
72
|
+
{
|
73
|
+
:http_method => options.first[:http_method] || "GET",
|
74
|
+
:host => options.first[:host],
|
75
|
+
:port => options.first[:port] || 80,
|
76
|
+
:path => options.first[:path] || "/",
|
77
|
+
:query => options.first[:query]
|
78
|
+
}
|
74
79
|
else
|
75
|
-
|
80
|
+
raise ArgumentError
|
76
81
|
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def new_text_multipart field_name, text
|
80
|
-
part = ""
|
81
|
-
part += "--#{boundary}"
|
82
|
-
part += CRLF
|
83
|
-
part += "Content-Disposition: form-data; name=\"#{field_name}\""
|
84
|
-
part += CRLF
|
85
|
-
part += CRLF
|
86
|
-
part += text
|
87
|
-
part += CRLF
|
88
|
-
end
|
89
|
-
|
90
|
-
def new_file_multipart field_name, file
|
91
|
-
content_type = %x[file --mime-type -b #{file.path}].chomp
|
92
|
-
|
93
|
-
part = ""
|
94
|
-
part += "--#{boundary}"
|
95
|
-
part += CRLF
|
96
|
-
part += "Content-Disposition: form-data; name=\"#{field_name}\"; "
|
97
|
-
part += "filename=\"#{File.basename( file )}\""
|
98
|
-
part += CRLF
|
99
|
-
part += "Content-Type: #{content_type}"
|
100
|
-
part += CRLF
|
101
|
-
part += CRLF
|
102
|
-
part += file.read
|
103
|
-
file.close
|
104
|
-
part += CRLF
|
105
|
-
end
|
106
82
|
|
107
|
-
|
108
|
-
|
109
|
-
if value.is_a?( File )
|
110
|
-
@body << new_file_multipart( key, value )
|
111
|
-
elsif value.is_a?( String ) || value.responds_to?( :to_s )
|
112
|
-
@body << new_text_multipart( key, value )
|
113
|
-
else
|
114
|
-
raise ArgumentError, "Invalid Parameter Value"
|
115
|
-
end
|
83
|
+
if options[:path].nil? || options[:path].empty?
|
84
|
+
options[:path] = "/"
|
116
85
|
end
|
117
86
|
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
def create_simple_body
|
122
|
-
@body << @params.map {|key, value| "#{key}=#{value}"}.join("&")
|
87
|
+
options
|
123
88
|
end
|
124
89
|
|
125
|
-
def
|
126
|
-
if
|
127
|
-
|
90
|
+
def method_missing name, *args, &block
|
91
|
+
if options[name]
|
92
|
+
return options[name]
|
128
93
|
else
|
129
|
-
|
130
|
-
"application/x-www-form-urlencoded; charset=UTF-8"
|
131
|
-
else
|
132
|
-
"text/plain"
|
133
|
-
end
|
94
|
+
super
|
134
95
|
end
|
135
96
|
end
|
136
97
|
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
98
|
+
def self.options_mode options
|
99
|
+
if options.length == 1 && options.first.is_a?( String )
|
100
|
+
:simple
|
101
|
+
elsif options.length == 2 && options.map(&:class) == [String, Hash]
|
102
|
+
:mixed
|
103
|
+
elsif options.length == 1 && options.first.is_a?( Hash )
|
104
|
+
:advanced
|
105
|
+
else
|
106
|
+
:error
|
107
|
+
end
|
147
108
|
end
|
148
|
-
end
|
149
109
|
|
150
|
-
|
110
|
+
def self.method_missing name, *args, &block
|
111
|
+
name = name.to_s.upcase
|
151
112
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
"#{field_name}: #{value}"
|
113
|
+
if HTTPMethods.include?( name )
|
114
|
+
case options_mode( args )
|
115
|
+
when :simple
|
116
|
+
self.new( args.push( :http_method => name ) )
|
117
|
+
when :mixed
|
118
|
+
args.last.merge!( :http_method => name )
|
119
|
+
self.new( args )
|
120
|
+
when :advanced
|
121
|
+
self.new( args.first.merge( :http_method => name ) )
|
162
122
|
end
|
123
|
+
else
|
124
|
+
super
|
163
125
|
end
|
164
|
-
|
165
|
-
header_string.join(CRLF) + CRLF + CRLF
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
|
170
|
-
class HTTPBody < Array
|
171
|
-
|
172
|
-
def to_s
|
173
|
-
join
|
174
126
|
end
|
175
|
-
|
176
127
|
end
|
177
128
|
|
178
129
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Rig
|
2
|
+
class HTTPBody < Array
|
3
|
+
|
4
|
+
def initialize options
|
5
|
+
@options = options[:body] || {}
|
6
|
+
@http_method = options[:http_method]
|
7
|
+
|
8
|
+
if multipart?
|
9
|
+
create_multipart_body
|
10
|
+
else
|
11
|
+
create_simple_body
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def content_length
|
16
|
+
join.bytes.to_a.length
|
17
|
+
end
|
18
|
+
|
19
|
+
def content_type
|
20
|
+
if multipart?
|
21
|
+
"multipart/form-data; boundary=#{boundary}"
|
22
|
+
else
|
23
|
+
if %w(POST PUT).include?( @http_method )
|
24
|
+
"application/x-www-form-urlencoded; charset=UTF-8"
|
25
|
+
else
|
26
|
+
"text/plain"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def boundary
|
32
|
+
@boundary ||= "----rigHTTPmultipart#{rand(2**32)}XZWCFOOBAR"
|
33
|
+
end
|
34
|
+
|
35
|
+
def multipart?
|
36
|
+
if defined? @multipart
|
37
|
+
@multipart
|
38
|
+
elsif @options
|
39
|
+
@multipart = @options.values.any? do |element|
|
40
|
+
element.respond_to?( :read )
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@multipart = false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_simple_body
|
48
|
+
push @options.map {|key, value| "#{key}=#{value}"}.join("&")
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_multipart_body
|
52
|
+
@options.each do |key, value|
|
53
|
+
if value.respond_to?( :read )
|
54
|
+
push new_file_multipart( key, value )
|
55
|
+
elsif value.is_a?( String ) || value.respond_to?( :to_s )
|
56
|
+
push new_text_multipart( key, value )
|
57
|
+
else
|
58
|
+
raise ArgumentError, "Invalid Parameter Value"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
push "--#{boundary}--\r\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
def new_text_multipart field_name, text
|
66
|
+
part = ""
|
67
|
+
part += "--#{boundary}"
|
68
|
+
part += CRLF
|
69
|
+
part += "Content-Disposition: form-data; name=\"#{field_name}\""
|
70
|
+
part += CRLF
|
71
|
+
part += CRLF
|
72
|
+
part += text
|
73
|
+
part += CRLF
|
74
|
+
end
|
75
|
+
|
76
|
+
def new_file_multipart field_name, file
|
77
|
+
content_type = %x[file --mime-type -b #{file.path}].chomp
|
78
|
+
|
79
|
+
part = ""
|
80
|
+
part += "--#{boundary}"
|
81
|
+
part += CRLF
|
82
|
+
part += "Content-Disposition: form-data; name=\"#{field_name}\"; "
|
83
|
+
part += "filename=\"#{File.basename( file )}\""
|
84
|
+
part += CRLF
|
85
|
+
part += "Content-Type: #{content_type}"
|
86
|
+
part += CRLF
|
87
|
+
part += CRLF
|
88
|
+
part += file.read
|
89
|
+
file.close
|
90
|
+
part += CRLF
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_s
|
94
|
+
join
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
class NoHostProvided < ArgumentError; end;
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Rig
|
2
|
+
class HTTPHeader < Hash
|
3
|
+
|
4
|
+
def initialize options
|
5
|
+
http_method = options[:http_method]
|
6
|
+
path = options[:path]
|
7
|
+
|
8
|
+
header = {
|
9
|
+
"" => "#{http_method} #{path} HTTP/1.1",
|
10
|
+
"Host" => options[:host],
|
11
|
+
"Origin" => "localhost",
|
12
|
+
"Content-Length" => options[:content_length],
|
13
|
+
"Content-Type" => options[:content_type]
|
14
|
+
}.merge(
|
15
|
+
(options[:custom_header] || {})
|
16
|
+
).merge(
|
17
|
+
"Connection" => "close"
|
18
|
+
)
|
19
|
+
|
20
|
+
merge!( header )
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
header_string = map do |field_name, value|
|
25
|
+
if field_name == ""
|
26
|
+
value
|
27
|
+
else
|
28
|
+
"#{field_name}: #{value}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
header_string.join(CRLF) + CRLF + CRLF
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ruby-debug'
|
2
|
+
module Rig
|
3
|
+
class HTTPResponse
|
4
|
+
|
5
|
+
attr_reader :header, :body
|
6
|
+
|
7
|
+
def initialize response
|
8
|
+
parts = response.split(CRLF + CRLF)
|
9
|
+
@header = parts.delete_at( 0 )
|
10
|
+
@status = @header.match(/HTTP\/\d.\d\s(\d\d\d)/)[1]
|
11
|
+
@body = parts.join
|
12
|
+
|
13
|
+
parse_header
|
14
|
+
|
15
|
+
if @header["Transfer-Encoding"] == "chunked"
|
16
|
+
parsed_body = ""
|
17
|
+
@body = StringIO.new( @body )
|
18
|
+
read_chunked( parsed_body )
|
19
|
+
|
20
|
+
@body = parsed_body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def status
|
25
|
+
@status ? @status.to_i : 666
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_header
|
29
|
+
status_line = @header[/HTTP\/\d\.\d\s\d\d\d.+\r\n/]
|
30
|
+
@header = @header.gsub(status_line, "Status: #{status_line}")
|
31
|
+
@header = @header.split(CRLF)
|
32
|
+
@header = @header.map { |element| element.split(": ") }
|
33
|
+
@header = @header.inject({}) do |result, element|
|
34
|
+
result[element.first] = element.last
|
35
|
+
result
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def read_chunked(dest)
|
40
|
+
len = nil
|
41
|
+
total = 0
|
42
|
+
while true
|
43
|
+
line = @body.readline
|
44
|
+
hexlen = line.slice(/[0-9a-fA-F]+/) or
|
45
|
+
raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
46
|
+
len = hexlen.hex
|
47
|
+
break if len == 0
|
48
|
+
@body.read len, dest; total += len
|
49
|
+
@body.read 2 # \r\n
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/righttp.gemspec
CHANGED
@@ -5,27 +5,33 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{righttp}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["hukl"]
|
12
|
-
s.date = %q{2010-08-
|
12
|
+
s.date = %q{2010-08-15}
|
13
13
|
s.description = %q{Why? Because it has to be done!}
|
14
14
|
s.email = %q{contact@smyck.org}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.
|
17
|
+
"README.textile"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
".document",
|
21
21
|
".gitignore",
|
22
22
|
"LICENSE",
|
23
|
-
"README.
|
23
|
+
"README.textile",
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
|
+
"doc/mission.textile",
|
26
27
|
"lib/rig/http.rb",
|
28
|
+
"lib/rig/http_body.rb",
|
29
|
+
"lib/rig/http_exceptions.rb",
|
30
|
+
"lib/rig/http_header.rb",
|
31
|
+
"lib/rig/http_response.rb",
|
27
32
|
"lib/righttp.rb",
|
28
33
|
"righttp.gemspec",
|
34
|
+
"test/fixtures/yay.gif",
|
29
35
|
"test/helper.rb",
|
30
36
|
"test/test_http.rb"
|
31
37
|
]
|
Binary file
|
data/test/test_http.rb
CHANGED
@@ -2,54 +2,195 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class TestHttp < Test::Unit::TestCase
|
4
4
|
|
5
|
-
test "create
|
6
|
-
|
5
|
+
test "cannot create HTTP object without any parameters" do
|
6
|
+
assert_raise( ArgumentError) { HTTP.new }
|
7
7
|
end
|
8
8
|
|
9
|
-
test "
|
10
|
-
|
9
|
+
test "request can be built with one argument" do
|
10
|
+
assert_not_nil HTTP.new( "http://foobar.com" )
|
11
11
|
end
|
12
12
|
|
13
|
-
test "
|
14
|
-
|
15
|
-
assert_not_nil get.tcp_socket
|
13
|
+
test "request can be built with two or more arguments" do
|
14
|
+
assert_not_nil HTTP.new( "http://fooobar.com", :params => { 1 => 2} )
|
16
15
|
end
|
17
16
|
|
18
|
-
test "
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
test "method get" do
|
18
|
+
assert_not_nil request = HTTP.get( "http://foobar.com" )
|
19
|
+
expected = {
|
20
|
+
:host=>"foobar.com",
|
21
|
+
:port=>80,
|
22
|
+
:path=>"/",
|
23
|
+
:http_method=>"GET",
|
24
|
+
:query => nil,
|
25
|
+
:content_type=>"text/plain",
|
26
|
+
:content_length=>0
|
27
|
+
}
|
28
|
+
assert_equal expected, request.options
|
22
29
|
end
|
23
30
|
|
24
|
-
test "
|
25
|
-
|
26
|
-
|
27
|
-
|
31
|
+
test "method post" do
|
32
|
+
assert_not_nil request = HTTP.post( "http://foobar.com" )
|
33
|
+
expected = {
|
34
|
+
:host=>"foobar.com",
|
35
|
+
:port=>80,
|
36
|
+
:path=>"/",
|
37
|
+
:http_method=>"POST",
|
38
|
+
:query => nil,
|
39
|
+
:content_type=>"application/x-www-form-urlencoded; charset=UTF-8",
|
40
|
+
:content_length=>0
|
41
|
+
}
|
42
|
+
assert_equal expected, request.options
|
43
|
+
end
|
44
|
+
|
45
|
+
test "method put" do
|
46
|
+
assert_not_nil request = HTTP.put( "http://foobar.com" )
|
47
|
+
expected = {
|
48
|
+
:host=>"foobar.com",
|
49
|
+
:port=>80,
|
50
|
+
:path=>"/",
|
51
|
+
:http_method=>"PUT",
|
52
|
+
:query => nil,
|
53
|
+
:content_type=>"application/x-www-form-urlencoded; charset=UTF-8",
|
54
|
+
:content_length=>0
|
55
|
+
}
|
56
|
+
assert_equal expected, request.options
|
57
|
+
end
|
58
|
+
|
59
|
+
test "method delete" do
|
60
|
+
assert_not_nil request = HTTP.delete( "http://foobar.com" )
|
61
|
+
expected = {
|
62
|
+
:host=>"foobar.com",
|
63
|
+
:port=>80,
|
64
|
+
:path=>"/",
|
65
|
+
:http_method=>"DELETE",
|
66
|
+
:query => nil,
|
67
|
+
:content_type=>"text/plain",
|
68
|
+
:content_length=>0
|
69
|
+
}
|
70
|
+
assert_equal expected, request.options
|
71
|
+
end
|
72
|
+
|
73
|
+
test "get request with advanced mode" do
|
74
|
+
assert_not_nil request = HTTP.new(:host => "foobar.com")
|
75
|
+
expected = {
|
76
|
+
:host=>"foobar.com",
|
77
|
+
:port=>80,
|
78
|
+
:path=>"/",
|
79
|
+
:http_method=>"GET",
|
80
|
+
:query => nil,
|
81
|
+
:content_type=>"text/plain",
|
82
|
+
:content_length=>0
|
83
|
+
}
|
84
|
+
|
85
|
+
assert_equal expected, request.options
|
86
|
+
end
|
87
|
+
|
88
|
+
test "other missing methods are delegated to super" do
|
89
|
+
assert_raise( ArgumentError ) { HTTP.foobar( "http://foobar.com" ) }
|
90
|
+
end
|
91
|
+
|
92
|
+
test "mixed mode get" do
|
93
|
+
request = HTTP.get("http://foobar.com", :port => 3000)
|
94
|
+
expected = {
|
95
|
+
:host=>"foobar.com",
|
96
|
+
:port=>3000,
|
97
|
+
:path=>"/",
|
98
|
+
:http_method=>"GET",
|
99
|
+
:query => nil,
|
100
|
+
:content_type=>"text/plain",
|
101
|
+
:content_length=>0
|
102
|
+
}
|
103
|
+
assert_equal expected, request.options
|
104
|
+
end
|
105
|
+
|
106
|
+
test "mixed mode get with query params" do
|
107
|
+
request = HTTP.get("http://foobar.com?foo=bar", :port => 3000)
|
108
|
+
expected = {
|
109
|
+
:host=>"foobar.com",
|
110
|
+
:port=>3000,
|
111
|
+
:path=>"/",
|
112
|
+
:http_method=>"GET",
|
113
|
+
:query => "foo=bar",
|
114
|
+
:content_type=>"text/plain",
|
115
|
+
:content_length=>0
|
116
|
+
}
|
117
|
+
assert_equal expected, request.options
|
118
|
+
end
|
119
|
+
|
120
|
+
test "mixed mode get with query paramsi and inline port" do
|
121
|
+
request = HTTP.get("http://foobar.com:3000?foo=bar")
|
122
|
+
expected = {
|
123
|
+
:host=>"foobar.com",
|
124
|
+
:port=>3000,
|
125
|
+
:path=>"/",
|
126
|
+
:http_method=>"GET",
|
127
|
+
:query => "foo=bar",
|
128
|
+
:content_type=>"text/plain",
|
129
|
+
:content_length=>0
|
130
|
+
}
|
131
|
+
assert_equal expected, request.options
|
132
|
+
end
|
133
|
+
|
134
|
+
test "mixed mode get with query params and overriding port" do
|
135
|
+
request = HTTP.get("http://foobar.com:2323?foo=bar", :port => 3000)
|
136
|
+
expected = {
|
137
|
+
:host=>"foobar.com",
|
138
|
+
:port=>3000,
|
139
|
+
:path=>"/",
|
140
|
+
:http_method=>"GET",
|
141
|
+
:query => "foo=bar",
|
142
|
+
:content_type=>"text/plain",
|
143
|
+
:content_length=>0
|
144
|
+
}
|
145
|
+
assert_equal expected, request.options
|
146
|
+
end
|
147
|
+
|
148
|
+
test "if multipart" do
|
149
|
+
File.open(File.join(File.dirname(__FILE__), "fixtures", "yay.gif")) do |f|
|
150
|
+
request = HTTP.post(
|
151
|
+
"http://foo.com",
|
152
|
+
:body => {:upload => f}
|
153
|
+
)
|
154
|
+
assert request.body.multipart?, "Should be multipart"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
test "if not multipart" do
|
159
|
+
request = HTTP.post(
|
160
|
+
"http://foo.com",
|
161
|
+
:body => {:abstract => "bla", :title => "foo"}
|
162
|
+
)
|
163
|
+
assert !request.body.multipart?, "Should not be multipart"
|
164
|
+
end
|
165
|
+
|
166
|
+
test "http object has accessible options" do
|
167
|
+
get = HTTP.new( {:host => "localhost", :query => {"foo" => "bar"}} )
|
168
|
+
assert_not_nil get.options
|
169
|
+
assert_equal ({"foo" => "bar"}), get.options[:query]
|
28
170
|
end
|
29
171
|
|
30
172
|
test "simple http object defaults to method GET" do
|
31
173
|
get = HTTP.new( {:host => "localhost"} )
|
32
|
-
assert_equal "GET", get.
|
174
|
+
assert_equal "GET", get.http_method
|
33
175
|
end
|
34
176
|
|
35
177
|
test "method of http object can be overridden" do
|
36
|
-
post = HTTP.new( {:host => "localhost", :
|
37
|
-
assert_equal "POST", post.
|
178
|
+
post = HTTP.new( {:host => "localhost", :http_method => "POST"} )
|
179
|
+
assert_equal "POST", post.http_method
|
38
180
|
end
|
39
181
|
|
40
182
|
test "path of a http object defaults to index" do
|
41
183
|
get = HTTP.new( {:host => "localhost"} )
|
42
|
-
assert_equal "/", get.path
|
184
|
+
assert_equal "/", get.options[:path]
|
43
185
|
end
|
44
186
|
|
45
187
|
test "path of http object can be set" do
|
46
188
|
get = HTTP.new( {:host => "localhost", :path => "/posts"} )
|
47
|
-
assert_equal "/posts", get.path
|
189
|
+
assert_equal "/posts", get.options[:path]
|
48
190
|
end
|
49
191
|
|
50
192
|
test "generate_header_and_body" do
|
51
193
|
get = HTTP.new( {:host => "localhost", :path => "/posts"} )
|
52
|
-
assert_not_nil get.generate_header_and_body
|
53
194
|
|
54
195
|
expected = "GET /posts HTTP/1.1\r\n" \
|
55
196
|
"Host: localhost\r\n" \
|
@@ -58,39 +199,45 @@ class TestHttp < Test::Unit::TestCase
|
|
58
199
|
"Content-Type: text/plain\r\n" \
|
59
200
|
"Connection: close\r\n\r\n"
|
60
201
|
|
61
|
-
assert_equal expected, get.header
|
202
|
+
assert_equal expected, get.header.to_s
|
62
203
|
end
|
63
204
|
|
64
|
-
test "
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
:method => "POST",
|
69
|
-
:params => {
|
70
|
-
"photo[title]" => "Hello World",
|
71
|
-
"photo[image]" => File.open("/Users/hukl/Desktop/file1.png"),
|
72
|
-
"photo[picture]" => File.open("/Users/hukl/Desktop/file2.png")
|
73
|
-
}
|
74
|
-
)
|
75
|
-
post.generate_header_and_body
|
76
|
-
|
77
|
-
post.tcp_socket.write (post.header + post.body)
|
78
|
-
response = post.tcp_socket.recvfrom(2**16)
|
79
|
-
status = response.first.match(/HTTP\/1\.1\s(\d\d\d).+$/)[1]
|
80
|
-
|
81
|
-
assert_equal 302, status.to_i
|
205
|
+
test "actual get request" do
|
206
|
+
request = HTTP.get( "http://www.spiegel.de" )
|
207
|
+
response = request.send
|
208
|
+
assert_equal 200, response.status
|
82
209
|
end
|
83
210
|
|
84
|
-
test "
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
211
|
+
#test "multipart body gets properly created" do
|
212
|
+
# post = HTTP.new(
|
213
|
+
# :host => "localhost",
|
214
|
+
# :path => "/photos",
|
215
|
+
# :http_method => "POST",
|
216
|
+
# :params => {
|
217
|
+
# "photo[title]" => "Hello World",
|
218
|
+
# "photo[image]" => File.open("/Users/hukl/Desktop/file1.png"),
|
219
|
+
# "photo[picture]" => File.open("/Users/hukl/Desktop/file2.png")
|
220
|
+
# }
|
221
|
+
# )
|
222
|
+
# post.generate_header_and_body
|
223
|
+
|
224
|
+
# post.tcp_socket.write (post.header + post.body)
|
225
|
+
# response = post.tcp_socket.recvfrom(2**16)
|
226
|
+
# status = response.first.match(/HTTP\/1\.1\s(\d\d\d).+$/)[1]
|
227
|
+
|
228
|
+
# assert_equal 302, status.to_i
|
229
|
+
#end
|
230
|
+
|
231
|
+
#test "real get request" do
|
232
|
+
# get = HTTP.new(
|
233
|
+
# :host => "localhost",
|
234
|
+
# :path => "/photos"
|
235
|
+
# )
|
236
|
+
|
237
|
+
# get.tcp_socket.write (get.header + get.body)
|
238
|
+
# response = get.tcp_socket.recvfrom(2**16)
|
239
|
+
# status = response.first.match(/HTTP\/1\.1\s(\d\d\d).+$/)[1]
|
240
|
+
|
241
|
+
# assert_equal 200, status.to_i
|
242
|
+
#end
|
96
243
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- hukl
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-08-
|
17
|
+
date: 2010-08-15 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -26,17 +26,23 @@ extensions: []
|
|
26
26
|
|
27
27
|
extra_rdoc_files:
|
28
28
|
- LICENSE
|
29
|
-
- README.
|
29
|
+
- README.textile
|
30
30
|
files:
|
31
31
|
- .document
|
32
32
|
- .gitignore
|
33
33
|
- LICENSE
|
34
|
-
- README.
|
34
|
+
- README.textile
|
35
35
|
- Rakefile
|
36
36
|
- VERSION
|
37
|
+
- doc/mission.textile
|
37
38
|
- lib/rig/http.rb
|
39
|
+
- lib/rig/http_body.rb
|
40
|
+
- lib/rig/http_exceptions.rb
|
41
|
+
- lib/rig/http_header.rb
|
42
|
+
- lib/rig/http_response.rb
|
38
43
|
- lib/righttp.rb
|
39
44
|
- righttp.gemspec
|
45
|
+
- test/fixtures/yay.gif
|
40
46
|
- test/helper.rb
|
41
47
|
- test/test_http.rb
|
42
48
|
has_rdoc: true
|
data/README.rdoc
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
= righttp
|
2
|
-
|
3
|
-
Description goes here.
|
4
|
-
|
5
|
-
== Note on Patches/Pull Requests
|
6
|
-
|
7
|
-
* Fork the project.
|
8
|
-
* Make your feature addition or bug fix.
|
9
|
-
* Add tests for it. This is important so I don't break it in a
|
10
|
-
future version unintentionally.
|
11
|
-
* Commit, do not mess with rakefile, version, or history.
|
12
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
-
* Send me a pull request. Bonus points for topic branches.
|
14
|
-
|
15
|
-
== Copyright
|
16
|
-
|
17
|
-
Copyright (c) 2010 hukl. See LICENSE for details.
|