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