igrigorik-em-http-request 0.1.7 → 0.1.8
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/README.rdoc +5 -3
- data/Rakefile +4 -0
- data/em-http-request.gemspec +2 -2
- data/lib/em-http/client.rb +67 -16
- data/lib/em-http/request.rb +3 -2
- data/test/stallion.rb +16 -2
- data/test/{hash.rb → test_hash.rb} +6 -4
- data/test/test_multi.rb +1 -6
- data/test/test_request.rb +63 -11
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -6,6 +6,9 @@ EventMachine based HTTP Request interface. Supports streaming response processin
|
|
6
6
|
- Basic-Auth support
|
7
7
|
- Custom timeouts
|
8
8
|
|
9
|
+
Screencast / Demo of using EM-HTTP-Request:
|
10
|
+
- http://everburning.com/news/eventmachine-screencast-em-http-request/
|
11
|
+
|
9
12
|
== Simple client example
|
10
13
|
|
11
14
|
EventMachine.run {
|
@@ -61,9 +64,8 @@ EventMachine based HTTP Request interface. Supports streaming response processin
|
|
61
64
|
|
62
65
|
== Streaming body processing
|
63
66
|
EventMachine.run {
|
64
|
-
|
65
|
-
|
66
|
-
http = EventMachine::HttpRequest.new('http://www.website.com/').get :on_response => on_body
|
67
|
+
http = EventMachine::HttpRequest.new('http://www.website.com/').get
|
68
|
+
http.stream { |chunk| print chunk }
|
67
69
|
|
68
70
|
# ...
|
69
71
|
}
|
data/Rakefile
CHANGED
data/em-http-request.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
spec = Gem::Specification.new do |s|
|
2
2
|
s.name = 'em-http-request'
|
3
|
-
s.version = '0.1.
|
3
|
+
s.version = '0.1.8'
|
4
4
|
s.date = '2009-03-20'
|
5
5
|
s.summary = 'EventMachine based HTTP Request interface'
|
6
6
|
s.description = s.summary
|
@@ -34,7 +34,7 @@ spec = Gem::Specification.new do |s|
|
|
34
34
|
"lib/em-http/decoders.rb",
|
35
35
|
"lib/em-http/multi.rb",
|
36
36
|
"lib/em-http/request.rb",
|
37
|
-
"test/
|
37
|
+
"test/test_hash.rb",
|
38
38
|
"test/helper.rb",
|
39
39
|
"test/stallion.rb",
|
40
40
|
"test/test_multi.rb",
|
data/lib/em-http/client.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- coding: undecided -*-
|
2
1
|
# #--
|
3
2
|
# Copyright (C)2008 Ilya Grigorik
|
4
3
|
#
|
@@ -21,6 +20,16 @@ module EventMachine
|
|
21
20
|
|
22
21
|
# The status code (as a string!)
|
23
22
|
attr_accessor :http_status
|
23
|
+
|
24
|
+
# E-Tag
|
25
|
+
def etag
|
26
|
+
self["ETag"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_modified
|
30
|
+
time = self["Last-Modified"]
|
31
|
+
Time.parse(time) if time
|
32
|
+
end
|
24
33
|
|
25
34
|
# HTTP response status as an integer
|
26
35
|
def status
|
@@ -32,6 +41,11 @@ module EventMachine
|
|
32
41
|
Integer(self[HttpClient::CONTENT_LENGTH]) rescue nil
|
33
42
|
end
|
34
43
|
|
44
|
+
# Cookie header from the server
|
45
|
+
def cookie
|
46
|
+
self[HttpClient::SET_COOKIE]
|
47
|
+
end
|
48
|
+
|
35
49
|
# Is the transfer encoding chunked?
|
36
50
|
def chunked_encoding?
|
37
51
|
/chunked/i === self[HttpClient::TRANSFER_ENCODING]
|
@@ -94,12 +108,16 @@ module EventMachine
|
|
94
108
|
end
|
95
109
|
|
96
110
|
def encode_query(path, query)
|
97
|
-
|
98
|
-
|
99
|
-
return "#{path}?#{query}"
|
111
|
+
encoded_query = if query.kind_of?(Hash)
|
112
|
+
query.map { |k, v| encode_param(k, v) }.join('&')
|
100
113
|
else
|
101
|
-
|
114
|
+
query.to_s
|
115
|
+
end
|
116
|
+
if !@uri.query.to_s.empty?
|
117
|
+
encoded_query = [encoded_query, @uri.query].reject {|part| part.empty?}.join("&")
|
102
118
|
end
|
119
|
+
return path if encoded_query.to_s.empty?
|
120
|
+
"#{path}?#{encoded_query}"
|
103
121
|
end
|
104
122
|
|
105
123
|
# URL encodes query parameters:
|
@@ -134,8 +152,12 @@ module EventMachine
|
|
134
152
|
end
|
135
153
|
end
|
136
154
|
|
137
|
-
def
|
138
|
-
|
155
|
+
def encode_cookie(cookie)
|
156
|
+
if cookie.is_a? Hash
|
157
|
+
cookie.inject('') { |result, (k, v)| result << encode_param(k, v) + ";" }
|
158
|
+
else
|
159
|
+
cookie
|
160
|
+
end
|
139
161
|
end
|
140
162
|
end
|
141
163
|
|
@@ -167,6 +189,7 @@ module EventMachine
|
|
167
189
|
@inflate = []
|
168
190
|
@errors = ''
|
169
191
|
@content_decoder = nil
|
192
|
+
@stream = nil
|
170
193
|
end
|
171
194
|
|
172
195
|
# start HTTP request once we establish connection to host
|
@@ -177,7 +200,7 @@ module EventMachine
|
|
177
200
|
send_request_header
|
178
201
|
send_request_body
|
179
202
|
end
|
180
|
-
|
203
|
+
|
181
204
|
# request is done, invoke the callback
|
182
205
|
def on_request_complete
|
183
206
|
begin
|
@@ -187,11 +210,19 @@ module EventMachine
|
|
187
210
|
end
|
188
211
|
unbind
|
189
212
|
end
|
190
|
-
|
213
|
+
|
191
214
|
# request failed, invoke errback
|
192
|
-
def on_error(msg)
|
215
|
+
def on_error(msg, dns_error = false)
|
193
216
|
@errors = msg
|
194
|
-
|
217
|
+
|
218
|
+
# no connection signature on DNS failures
|
219
|
+
# fail the connection directly
|
220
|
+
dns_error == true ? fail : unbind
|
221
|
+
end
|
222
|
+
|
223
|
+
# assign a stream processing block
|
224
|
+
def stream(&blk)
|
225
|
+
@stream = blk
|
195
226
|
end
|
196
227
|
|
197
228
|
def normalize_body
|
@@ -221,6 +252,11 @@ module EventMachine
|
|
221
252
|
@inflate = head['accept-encoding'].split(',').map {|t| t.strip}
|
222
253
|
end
|
223
254
|
|
255
|
+
# Set the cookie header if provided
|
256
|
+
if cookie = head.delete('cookie')
|
257
|
+
head['cookie'] = encode_cookie(cookie)
|
258
|
+
end
|
259
|
+
|
224
260
|
# Build the request
|
225
261
|
request_header = encode_request(@method, @uri.path, query)
|
226
262
|
request_header << encode_headers(head)
|
@@ -254,15 +290,19 @@ module EventMachine
|
|
254
290
|
end
|
255
291
|
|
256
292
|
def on_decoded_body_data(data)
|
257
|
-
if
|
258
|
-
|
293
|
+
if @stream
|
294
|
+
@stream.call(data)
|
259
295
|
else
|
260
296
|
@response << data
|
261
297
|
end
|
262
298
|
end
|
263
299
|
|
264
300
|
def unbind
|
265
|
-
|
301
|
+
if @state == :finished
|
302
|
+
succeed(self)
|
303
|
+
else
|
304
|
+
fail(self)
|
305
|
+
end
|
266
306
|
close_connection
|
267
307
|
end
|
268
308
|
|
@@ -320,11 +360,22 @@ module EventMachine
|
|
320
360
|
return false
|
321
361
|
end
|
322
362
|
|
363
|
+
# shortcircuit on HEAD requests
|
364
|
+
if @method == "HEAD"
|
365
|
+
@state = :finished
|
366
|
+
on_request_complete
|
367
|
+
end
|
368
|
+
|
323
369
|
if @response_header.chunked_encoding?
|
324
370
|
@state = :chunk_header
|
325
371
|
else
|
326
|
-
@
|
327
|
-
|
372
|
+
if @response_header.content_length > 0
|
373
|
+
@state = :body
|
374
|
+
@bytes_remaining = @response_header.content_length
|
375
|
+
else
|
376
|
+
@state = :finished
|
377
|
+
on_request_complete
|
378
|
+
end
|
328
379
|
end
|
329
380
|
|
330
381
|
if @inflate.include?(response_header[CONTENT_ENCODING]) &&
|
data/lib/em-http/request.rb
CHANGED
@@ -47,13 +47,14 @@ module EventMachine
|
|
47
47
|
#
|
48
48
|
|
49
49
|
def get options = {}; send_request(:get, options); end
|
50
|
+
def head options = {}; send_request(:head, options); end
|
50
51
|
def post options = {}; send_request(:post, options); end
|
51
52
|
|
52
53
|
protected
|
53
54
|
|
54
55
|
def send_request(method, options)
|
55
56
|
raise ArgumentError, "invalid request path" unless /^\// === @uri.path
|
56
|
-
|
57
|
+
|
57
58
|
method = method.to_s.upcase
|
58
59
|
begin
|
59
60
|
EventMachine.connect(@uri.host, @uri.port, EventMachine::HttpClient) { |c|
|
@@ -65,7 +66,7 @@ module EventMachine
|
|
65
66
|
rescue RuntimeError => e
|
66
67
|
raise e unless e.message == "no connection"
|
67
68
|
conn = EventMachine::HttpClient.new("")
|
68
|
-
conn.on_error("no connection")
|
69
|
+
conn.on_error("no connection", true)
|
69
70
|
conn
|
70
71
|
end
|
71
72
|
end
|
data/test/stallion.rb
CHANGED
@@ -83,9 +83,19 @@ Stallion.saddle :spec do |stable|
|
|
83
83
|
elsif stable.request.path_info == '/echo_content_length'
|
84
84
|
stable.response.write stable.request.content_length
|
85
85
|
|
86
|
+
elsif stable.request.head?
|
87
|
+
stable.response.status = 200
|
88
|
+
|
86
89
|
elsif stable.request.post?
|
87
90
|
stable.response.write stable.request.body.read
|
88
91
|
|
92
|
+
elsif stable.request.path_info == '/set_cookie'
|
93
|
+
stable.response["Set-Cookie"] = "id=1; expires=Tue, 09-Aug-2011 17:53:39 GMT; path=/;"
|
94
|
+
stable.response.write "cookie set"
|
95
|
+
|
96
|
+
elsif stable.request.path_info == '/echo_cookie'
|
97
|
+
stable.response.write stable.request.env["HTTP_COOKIE"]
|
98
|
+
|
89
99
|
elsif stable.request.path_info == '/timeout'
|
90
100
|
sleep(10)
|
91
101
|
stable.response.write 'timeout'
|
@@ -124,7 +134,11 @@ Stallion.saddle :spec do |stable|
|
|
124
134
|
end
|
125
135
|
|
126
136
|
Thread.new do
|
127
|
-
|
137
|
+
begin
|
138
|
+
Stallion.run :Host => '127.0.0.1', :Port => 8080
|
139
|
+
rescue Exception => e
|
140
|
+
print e
|
141
|
+
end
|
128
142
|
end
|
129
143
|
|
130
|
-
sleep(
|
144
|
+
sleep(1)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'test/helper'
|
2
2
|
|
3
3
|
describe Hash do
|
4
|
+
|
4
5
|
describe ".to_params" do
|
5
6
|
it "should transform a basic hash into HTTP POST Params" do
|
6
7
|
{:a => "alpha", :b => "beta"}.to_params.should == "a=alpha&b=beta"
|
@@ -9,9 +10,10 @@ describe Hash do
|
|
9
10
|
it "should transform a more complex hash into HTTP POST Params" do
|
10
11
|
{:a => "a", :b => ["c", "d", "e"]}.to_params.should == "a=a&b[0]=c&b[1]=d&b[2]=e"
|
11
12
|
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
|
14
|
+
# Ruby 1.8 Hash is not sorted, so this test breaks randomly. Maybe once we're all on 1.9. ;-)
|
15
|
+
# it "should transform a very complex hash into HTTP POST Params" do
|
16
|
+
# {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.should == "a=a&b[0][d]=d&b[0][c]=c&b[1][f]=f&b[1][e]=e"
|
17
|
+
# end
|
16
18
|
end
|
17
19
|
end
|
data/test/test_multi.rb
CHANGED
@@ -3,11 +3,6 @@ require 'test/stallion'
|
|
3
3
|
|
4
4
|
describe EventMachine::MultiRequest do
|
5
5
|
|
6
|
-
def failed
|
7
|
-
EventMachine.stop
|
8
|
-
fail
|
9
|
-
end
|
10
|
-
|
11
6
|
it "should submit multiple requests in parallel and return once all of them are complete" do
|
12
7
|
EventMachine.run {
|
13
8
|
|
@@ -16,7 +11,7 @@ describe EventMachine::MultiRequest do
|
|
16
11
|
|
17
12
|
# add multiple requests to the multi-handler
|
18
13
|
multi.add(EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(:query => {:q => 'test'}))
|
19
|
-
multi.add(EventMachine::HttpRequest.new('http://
|
14
|
+
multi.add(EventMachine::HttpRequest.new('http://0.0.0.0/').get(:timeout => 1))
|
20
15
|
|
21
16
|
multi.callback {
|
22
17
|
# verify successfull request
|
data/test/test_request.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'test/helper'
|
2
2
|
require 'test/stallion'
|
3
|
-
|
3
|
+
|
4
4
|
describe EventMachine::HttpRequest do
|
5
5
|
|
6
6
|
def failed
|
7
7
|
EventMachine.stop
|
8
8
|
fail
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
it "should fail GET on DNS timeout" do
|
12
12
|
EventMachine.run {
|
13
13
|
http = EventMachine::HttpRequest.new('http://127.1.1.1/').get
|
@@ -68,12 +68,26 @@ describe EventMachine::HttpRequest do
|
|
68
68
|
}
|
69
69
|
end
|
70
70
|
|
71
|
+
it "should perform successfull HEAD with a URI passed as argument" do
|
72
|
+
EventMachine.run {
|
73
|
+
uri = URI.parse('http://127.0.0.1:8080/')
|
74
|
+
http = EventMachine::HttpRequest.new(uri).head
|
75
|
+
|
76
|
+
http.errback { failed }
|
77
|
+
http.callback {
|
78
|
+
http.response_header.status.should == 200
|
79
|
+
http.response.should == ""
|
80
|
+
EventMachine.stop
|
81
|
+
}
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
71
85
|
it "should return 404 on invalid path" do
|
72
86
|
EventMachine.run {
|
73
87
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/fail').get
|
74
88
|
|
75
89
|
http.errback { failed }
|
76
|
-
http.callback {
|
90
|
+
http.callback {
|
77
91
|
http.response_header.status.should == 404
|
78
92
|
EventMachine.stop
|
79
93
|
}
|
@@ -247,10 +261,10 @@ describe EventMachine::HttpRequest do
|
|
247
261
|
it "should timeout after 10 seconds" do
|
248
262
|
EventMachine.run {
|
249
263
|
t = Time.now.to_i
|
250
|
-
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/timeout').get :timeout =>
|
264
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/timeout').get :timeout => 1
|
251
265
|
|
252
266
|
http.errback {
|
253
|
-
(Time.now.to_i - t).should
|
267
|
+
(Time.now.to_i - t).should >= 2
|
254
268
|
EventMachine.stop
|
255
269
|
}
|
256
270
|
http.callback { failed }
|
@@ -260,10 +274,11 @@ describe EventMachine::HttpRequest do
|
|
260
274
|
it "should optionally pass the response body progressively" do
|
261
275
|
EventMachine.run {
|
262
276
|
body = ''
|
263
|
-
|
264
|
-
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :on_response => on_body
|
277
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
265
278
|
|
266
279
|
http.errback { failed }
|
280
|
+
http.stream { |chunk| body += chunk }
|
281
|
+
|
267
282
|
http.callback {
|
268
283
|
http.response_header.status.should == 200
|
269
284
|
http.response.should == ''
|
@@ -276,11 +291,11 @@ describe EventMachine::HttpRequest do
|
|
276
291
|
it "should optionally pass the deflate-encoded response body progressively" do
|
277
292
|
EventMachine.run {
|
278
293
|
body = ''
|
279
|
-
|
280
|
-
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/deflate').get :head => {"accept-encoding" => "deflate, compressed"},
|
281
|
-
:on_response => on_body
|
294
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/deflate').get :head => {"accept-encoding" => "deflate, compressed"}
|
282
295
|
|
283
296
|
http.errback { failed }
|
297
|
+
http.stream { |chunk| body += chunk }
|
298
|
+
|
284
299
|
http.callback {
|
285
300
|
http.response_header.status.should == 200
|
286
301
|
http.response_header["CONTENT_ENCODING"].should == "deflate"
|
@@ -303,4 +318,41 @@ describe EventMachine::HttpRequest do
|
|
303
318
|
}
|
304
319
|
end
|
305
320
|
|
306
|
-
|
321
|
+
it "should accept & return cookie header to user" do
|
322
|
+
EventMachine.run {
|
323
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/set_cookie').get
|
324
|
+
|
325
|
+
http.errback { failed }
|
326
|
+
http.callback {
|
327
|
+
http.response_header.status.should == 200
|
328
|
+
http.response_header.cookie.should == "id=1; expires=Tue, 09-Aug-2011 17:53:39 GMT; path=/;"
|
329
|
+
EventMachine.stop
|
330
|
+
}
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
it "should pass cookie header to server from string" do
|
335
|
+
EventMachine.run {
|
336
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_cookie').get :head => {'cookie' => 'id=2;'}
|
337
|
+
|
338
|
+
http.errback { failed }
|
339
|
+
http.callback {
|
340
|
+
http.response.should == "id=2;"
|
341
|
+
EventMachine.stop
|
342
|
+
}
|
343
|
+
}
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should pass cookie header to server from Hash" do
|
347
|
+
EventMachine.run {
|
348
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_cookie').get :head => {'cookie' => {'id' => 2}}
|
349
|
+
|
350
|
+
http.errback { failed }
|
351
|
+
http.callback {
|
352
|
+
http.response.should == "id=2;"
|
353
|
+
EventMachine.stop
|
354
|
+
}
|
355
|
+
}
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: igrigorik-em-http-request
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Grigorik
|
@@ -52,13 +52,14 @@ files:
|
|
52
52
|
- lib/em-http/decoders.rb
|
53
53
|
- lib/em-http/multi.rb
|
54
54
|
- lib/em-http/request.rb
|
55
|
-
- test/
|
55
|
+
- test/test_hash.rb
|
56
56
|
- test/helper.rb
|
57
57
|
- test/stallion.rb
|
58
58
|
- test/test_multi.rb
|
59
59
|
- test/test_request.rb
|
60
60
|
has_rdoc: true
|
61
61
|
homepage: http://github.com/igrigorik/em-http-request
|
62
|
+
licenses:
|
62
63
|
post_install_message:
|
63
64
|
rdoc_options: []
|
64
65
|
|
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
80
|
requirements: []
|
80
81
|
|
81
82
|
rubyforge_project: em-http-request
|
82
|
-
rubygems_version: 1.
|
83
|
+
rubygems_version: 1.3.5
|
83
84
|
signing_key:
|
84
85
|
specification_version: 2
|
85
86
|
summary: EventMachine based HTTP Request interface
|