em-http-request 0.2.9 → 0.3.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/.gitignore +2 -1
- data/.rspec +0 -0
- data/Changelog.md +54 -0
- data/Gemfile +3 -0
- data/README.md +175 -0
- data/Rakefile +11 -37
- data/em-http-request.gemspec +25 -90
- data/examples/fetch.rb +30 -30
- data/examples/fibered-http.rb +38 -38
- data/examples/oauth-tweet.rb +49 -49
- data/examples/socks5.rb +26 -0
- data/examples/websocket-handler.rb +28 -28
- data/examples/websocket-server.rb +8 -8
- data/lib/em-http/client.rb +242 -207
- data/lib/em-http/http_encoding.rb +135 -0
- data/lib/em-http/http_header.rb +71 -0
- data/lib/em-http/http_options.rb +7 -4
- data/lib/em-http/mock.rb +90 -50
- data/lib/em-http/multi.rb +55 -51
- data/lib/em-http/request.rb +2 -4
- data/lib/em-http/version.rb +5 -0
- data/lib/em-http.rb +19 -19
- data/spec/encoding_spec.rb +40 -0
- data/spec/fixtures/google.ca +20 -21
- data/spec/helper.rb +5 -4
- data/spec/mock_spec.rb +85 -36
- data/spec/multi_spec.rb +68 -51
- data/spec/request_spec.rb +422 -108
- data/spec/stallion.rb +65 -3
- metadata +111 -28
- data/LICENSE +0 -58
- data/README.rdoc +0 -138
- data/VERSION +0 -1
- data/lib/em-http/core_ext/hash.rb +0 -53
- data/spec/hash_spec.rb +0 -24
data/spec/request_spec.rb
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
1
|
+
require 'helper'
|
|
2
|
+
require 'stallion'
|
|
3
|
+
require 'stub_server'
|
|
4
4
|
|
|
5
5
|
describe EventMachine::HttpRequest do
|
|
6
6
|
|
|
7
|
-
def failed
|
|
7
|
+
def failed(http=nil)
|
|
8
8
|
EventMachine.stop
|
|
9
|
-
fail
|
|
9
|
+
http ? fail(http.error) : fail
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
it "should fail GET on DNS timeout" do
|
|
13
13
|
EventMachine.run {
|
|
14
14
|
EventMachine.heartbeat_interval = 0.1
|
|
15
15
|
http = EventMachine::HttpRequest.new('http://127.1.1.1/').get :timeout => 1
|
|
16
|
-
http.callback { failed }
|
|
16
|
+
http.callback { failed(http) }
|
|
17
17
|
http.errback {
|
|
18
18
|
http.response_header.status.should == 0
|
|
19
19
|
EventMachine.stop
|
|
@@ -25,20 +25,31 @@ describe EventMachine::HttpRequest do
|
|
|
25
25
|
EventMachine.run {
|
|
26
26
|
EventMachine.heartbeat_interval = 0.1
|
|
27
27
|
http = EventMachine::HttpRequest.new('http://somethinglocal/').get :timeout => 1
|
|
28
|
-
http.callback { failed }
|
|
28
|
+
http.callback { failed(http) }
|
|
29
29
|
http.errback {
|
|
30
30
|
http.response_header.status.should == 0
|
|
31
31
|
http.error.should match(/unable to resolve server address/)
|
|
32
|
+
http.uri.to_s.should match('http://somethinglocal:80/')
|
|
32
33
|
EventMachine.stop
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
it "should
|
|
38
|
+
it "should raise error on invalid URL" do
|
|
38
39
|
EventMachine.run {
|
|
39
40
|
lambda {
|
|
40
|
-
EventMachine::HttpRequest.new('
|
|
41
|
-
}.should raise_error
|
|
41
|
+
EventMachine::HttpRequest.new('random?text').get
|
|
42
|
+
}.should raise_error
|
|
43
|
+
|
|
44
|
+
EM.stop
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should succeed GET on missing path" do
|
|
49
|
+
EventMachine.run {
|
|
50
|
+
lambda {
|
|
51
|
+
EventMachine::HttpRequest.new('http://127.0.0.1:8080').get
|
|
52
|
+
}.should_not raise_error(ArgumentError)
|
|
42
53
|
|
|
43
54
|
EventMachine.stop
|
|
44
55
|
}
|
|
@@ -48,7 +59,7 @@ describe EventMachine::HttpRequest do
|
|
|
48
59
|
EventMachine.run {
|
|
49
60
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
50
61
|
|
|
51
|
-
http.errback { failed }
|
|
62
|
+
http.errback { failed(http) }
|
|
52
63
|
http.callback {
|
|
53
64
|
http.response_header.status.should == 200
|
|
54
65
|
http.response.should match(/Hello/)
|
|
@@ -57,12 +68,90 @@ describe EventMachine::HttpRequest do
|
|
|
57
68
|
}
|
|
58
69
|
end
|
|
59
70
|
|
|
71
|
+
context "host override" do
|
|
72
|
+
|
|
73
|
+
it "should accept optional host" do
|
|
74
|
+
EventMachine.run {
|
|
75
|
+
http = EventMachine::HttpRequest.new('http://google.com:8080/').get :host => '127.0.0.1'
|
|
76
|
+
|
|
77
|
+
http.errback { failed(http) }
|
|
78
|
+
http.callback {
|
|
79
|
+
http.response_header.status.should == 200
|
|
80
|
+
http.response.should match(/Hello/)
|
|
81
|
+
EventMachine.stop
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should reset host on redirect" do
|
|
87
|
+
EventMachine.run {
|
|
88
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get :redirects => 1, :host => '127.0.0.1'
|
|
89
|
+
|
|
90
|
+
http.errback { failed(http) }
|
|
91
|
+
http.callback {
|
|
92
|
+
http.response_header.status.should == 200
|
|
93
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
|
94
|
+
http.response.should == "compressed"
|
|
95
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
96
|
+
http.redirects.should == 1
|
|
97
|
+
|
|
98
|
+
EM.stop
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it "should redirect with missing content-length" do
|
|
104
|
+
EventMachine.run {
|
|
105
|
+
@s = StubServer.new("HTTP/1.0 301 MOVED PERMANENTLY\r\nlocation: http://127.0.0.1:8080/redirect\r\n\r\n")
|
|
106
|
+
|
|
107
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get :redirects => 2
|
|
108
|
+
http.errback { failed(http) }
|
|
109
|
+
|
|
110
|
+
http.callback {
|
|
111
|
+
http.response_header.status.should == 200
|
|
112
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
|
113
|
+
http.response.should == "compressed"
|
|
114
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
115
|
+
http.redirects.should == 2
|
|
116
|
+
|
|
117
|
+
@s.stop
|
|
118
|
+
EM.stop
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "should follow redirects on HEAD method" do
|
|
124
|
+
EventMachine.run {
|
|
125
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/head').head :redirects => 1
|
|
126
|
+
http.errback { failed(http) }
|
|
127
|
+
http.callback {
|
|
128
|
+
http.response_header.status.should == 200
|
|
129
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
|
130
|
+
EM.stop
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should follow redirects on HEAD method (external)" do
|
|
136
|
+
|
|
137
|
+
EventMachine.run {
|
|
138
|
+
http = EventMachine::HttpRequest.new('http://www.google.com/').head :redirects => 1
|
|
139
|
+
http.errback { failed(http) }
|
|
140
|
+
http.callback {
|
|
141
|
+
http.response_header.status.should == 200
|
|
142
|
+
EM.stop
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
|
|
60
149
|
it "should perform successfull GET with a URI passed as argument" do
|
|
61
150
|
EventMachine.run {
|
|
62
151
|
uri = URI.parse('http://127.0.0.1:8080/')
|
|
63
152
|
http = EventMachine::HttpRequest.new(uri).get
|
|
64
153
|
|
|
65
|
-
http.errback { failed }
|
|
154
|
+
http.errback { failed(http) }
|
|
66
155
|
http.callback {
|
|
67
156
|
http.response_header.status.should == 200
|
|
68
157
|
http.response.should match(/Hello/)
|
|
@@ -76,7 +165,7 @@ describe EventMachine::HttpRequest do
|
|
|
76
165
|
uri = URI.parse('http://127.0.0.1:8080/')
|
|
77
166
|
http = EventMachine::HttpRequest.new(uri).head
|
|
78
167
|
|
|
79
|
-
http.errback {
|
|
168
|
+
http.errback { failed(http) }
|
|
80
169
|
http.callback {
|
|
81
170
|
http.response_header.status.should == 200
|
|
82
171
|
http.response.should == ""
|
|
@@ -91,7 +180,7 @@ describe EventMachine::HttpRequest do
|
|
|
91
180
|
uri = URI.parse('http://127.0.0.1:8080/')
|
|
92
181
|
http = EventMachine::HttpRequest.new(uri).delete
|
|
93
182
|
|
|
94
|
-
http.errback { failed }
|
|
183
|
+
http.errback { failed(http) }
|
|
95
184
|
http.callback {
|
|
96
185
|
http.response_header.status.should == 200
|
|
97
186
|
http.response.should == ""
|
|
@@ -104,7 +193,7 @@ describe EventMachine::HttpRequest do
|
|
|
104
193
|
EventMachine.run {
|
|
105
194
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/fail').get
|
|
106
195
|
|
|
107
|
-
http.errback { failed }
|
|
196
|
+
http.errback { failed(http) }
|
|
108
197
|
http.callback {
|
|
109
198
|
http.response_header.status.should == 404
|
|
110
199
|
EventMachine.stop
|
|
@@ -116,7 +205,7 @@ describe EventMachine::HttpRequest do
|
|
|
116
205
|
EventMachine.run {
|
|
117
206
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :query => {:q => 'test'}
|
|
118
207
|
|
|
119
|
-
http.errback { failed }
|
|
208
|
+
http.errback { failed(http) }
|
|
120
209
|
http.callback {
|
|
121
210
|
http.response_header.status.should == 200
|
|
122
211
|
http.response.should match(/test/)
|
|
@@ -129,7 +218,7 @@ describe EventMachine::HttpRequest do
|
|
|
129
218
|
EventMachine.run {
|
|
130
219
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :query => "q=test"
|
|
131
220
|
|
|
132
|
-
http.errback { failed }
|
|
221
|
+
http.errback { failed(http) }
|
|
133
222
|
http.callback {
|
|
134
223
|
http.response_header.status.should == 200
|
|
135
224
|
http.response.should match(/test/)
|
|
@@ -142,7 +231,7 @@ describe EventMachine::HttpRequest do
|
|
|
142
231
|
EventMachine.run {
|
|
143
232
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_query').get :query => {:hash => ['value1', 'value2']}
|
|
144
233
|
|
|
145
|
-
http.errback { failed }
|
|
234
|
+
http.errback { failed(http) }
|
|
146
235
|
http.callback {
|
|
147
236
|
http.response_header.status.should == 200
|
|
148
237
|
http.response.should match(/hash\[\]=value1&hash\[\]=value2/)
|
|
@@ -156,7 +245,7 @@ describe EventMachine::HttpRequest do
|
|
|
156
245
|
EventMachine.run {
|
|
157
246
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').put :body => "data"
|
|
158
247
|
|
|
159
|
-
http.errback { failed }
|
|
248
|
+
http.errback { failed(http) }
|
|
160
249
|
http.callback {
|
|
161
250
|
http.response_header.status.should == 200
|
|
162
251
|
http.response.should match(/data/)
|
|
@@ -169,7 +258,7 @@ describe EventMachine::HttpRequest do
|
|
|
169
258
|
EventMachine.run {
|
|
170
259
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => "data"
|
|
171
260
|
|
|
172
|
-
http.errback { failed }
|
|
261
|
+
http.errback { failed(http) }
|
|
173
262
|
http.callback {
|
|
174
263
|
http.response_header.status.should == 200
|
|
175
264
|
http.response.should match(/data/)
|
|
@@ -178,11 +267,24 @@ describe EventMachine::HttpRequest do
|
|
|
178
267
|
}
|
|
179
268
|
end
|
|
180
269
|
|
|
270
|
+
it "should escape body on POST" do
|
|
271
|
+
EventMachine.run {
|
|
272
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {:stuff => 'string&string'}
|
|
273
|
+
|
|
274
|
+
http.errback { failed(http) }
|
|
275
|
+
http.callback {
|
|
276
|
+
http.response_header.status.should == 200
|
|
277
|
+
http.response.should == "stuff=string%26string"
|
|
278
|
+
EventMachine.stop
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
end
|
|
282
|
+
|
|
181
283
|
it "should perform successfull POST with Ruby Hash/Array as params" do
|
|
182
284
|
EventMachine.run {
|
|
183
285
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {"key1" => 1, "key2" => [2,3]}
|
|
184
286
|
|
|
185
|
-
http.errback { failed }
|
|
287
|
+
http.errback { failed(http) }
|
|
186
288
|
http.callback {
|
|
187
289
|
http.response_header.status.should == 200
|
|
188
290
|
|
|
@@ -196,7 +298,7 @@ describe EventMachine::HttpRequest do
|
|
|
196
298
|
EventMachine.run {
|
|
197
299
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_length').post :body => {"key1" => "data1"}
|
|
198
300
|
|
|
199
|
-
http.errback { failed }
|
|
301
|
+
http.errback { failed(http) }
|
|
200
302
|
http.callback {
|
|
201
303
|
http.response_header.status.should == 200
|
|
202
304
|
|
|
@@ -210,7 +312,7 @@ describe EventMachine::HttpRequest do
|
|
|
210
312
|
EventMachine.run {
|
|
211
313
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :head => {'if-none-match' => 'evar!'}
|
|
212
314
|
|
|
213
|
-
http.errback { failed }
|
|
315
|
+
http.errback { failed(http) }
|
|
214
316
|
http.callback {
|
|
215
317
|
http.response_header.status.should == 304
|
|
216
318
|
EventMachine.stop
|
|
@@ -222,9 +324,9 @@ describe EventMachine::HttpRequest do
|
|
|
222
324
|
EventMachine.run {
|
|
223
325
|
|
|
224
326
|
# digg.com uses chunked encoding
|
|
225
|
-
http = EventMachine::HttpRequest.new('http://digg.com/').get
|
|
327
|
+
http = EventMachine::HttpRequest.new('http://digg.com/news').get
|
|
226
328
|
|
|
227
|
-
http.errback { failed }
|
|
329
|
+
http.errback { failed(http) }
|
|
228
330
|
http.callback {
|
|
229
331
|
http.response_header.status.should == 200
|
|
230
332
|
EventMachine.stop
|
|
@@ -237,7 +339,7 @@ describe EventMachine::HttpRequest do
|
|
|
237
339
|
|
|
238
340
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :head => {'authorization' => ['user', 'pass']}
|
|
239
341
|
|
|
240
|
-
http.errback { failed }
|
|
342
|
+
http.errback { failed(http) }
|
|
241
343
|
http.callback {
|
|
242
344
|
http.response_header.status.should == 200
|
|
243
345
|
EventMachine.stop
|
|
@@ -250,7 +352,7 @@ describe EventMachine::HttpRequest do
|
|
|
250
352
|
oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
|
|
251
353
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/oauth_auth').get :head => {'authorization' => oauth_header}
|
|
252
354
|
|
|
253
|
-
http.errback { failed }
|
|
355
|
+
http.errback { failed(http) }
|
|
254
356
|
http.callback {
|
|
255
357
|
http.response_header.status.should == 200
|
|
256
358
|
http.response.should == oauth_header
|
|
@@ -259,14 +361,43 @@ describe EventMachine::HttpRequest do
|
|
|
259
361
|
}
|
|
260
362
|
end
|
|
261
363
|
|
|
262
|
-
|
|
263
|
-
|
|
364
|
+
context "keepalive" do
|
|
365
|
+
it "should default to non-keepalive" do
|
|
366
|
+
EventMachine.run {
|
|
367
|
+
headers = {'If-Modified-Since' => 'Thu, 05 Aug 2010 22:54:44 GMT'}
|
|
368
|
+
http = EventMachine::HttpRequest.new('http://www.google.com/images/logos/ps_logo2.png').get :head => headers
|
|
264
369
|
|
|
265
|
-
|
|
370
|
+
http.errback { fail }
|
|
371
|
+
start = Time.now.to_i
|
|
372
|
+
http.callback {
|
|
373
|
+
(start - Time.now.to_i).should be_within(1).of(0)
|
|
374
|
+
EventMachine.stop
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "should work with keep-alive servers" do
|
|
380
|
+
EventMachine.run {
|
|
381
|
+
http = EventMachine::HttpRequest.new('http://mexicodiario.com/touch.public.json.php').get :keepalive => true
|
|
266
382
|
|
|
267
|
-
|
|
383
|
+
http.errback { failed(http) }
|
|
384
|
+
http.callback {
|
|
385
|
+
http.response_header.status.should == 200
|
|
386
|
+
EventMachine.stop
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it "should return ETag and Last-Modified headers" do
|
|
393
|
+
EventMachine.run {
|
|
394
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_query').get
|
|
395
|
+
|
|
396
|
+
http.errback { failed(http) }
|
|
268
397
|
http.callback {
|
|
269
398
|
http.response_header.status.should == 200
|
|
399
|
+
http.response_header.etag.should match('abcdefg')
|
|
400
|
+
http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
|
|
270
401
|
EventMachine.stop
|
|
271
402
|
}
|
|
272
403
|
}
|
|
@@ -277,7 +408,7 @@ describe EventMachine::HttpRequest do
|
|
|
277
408
|
|
|
278
409
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/deflate').get :head => {"accept-encoding" => "deflate"}
|
|
279
410
|
|
|
280
|
-
http.errback { failed }
|
|
411
|
+
http.errback { failed(http) }
|
|
281
412
|
http.callback {
|
|
282
413
|
http.response_header.status.should == 200
|
|
283
414
|
http.response_header["CONTENT_ENCODING"].should == "deflate"
|
|
@@ -293,7 +424,7 @@ describe EventMachine::HttpRequest do
|
|
|
293
424
|
|
|
294
425
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/gzip').get :head => {"accept-encoding" => "gzip, compressed"}
|
|
295
426
|
|
|
296
|
-
http.errback { failed }
|
|
427
|
+
http.errback { failed(http) }
|
|
297
428
|
http.callback {
|
|
298
429
|
http.response_header.status.should == 200
|
|
299
430
|
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
|
@@ -314,51 +445,98 @@ describe EventMachine::HttpRequest do
|
|
|
314
445
|
(Time.now.to_i - t).should <= 5
|
|
315
446
|
EventMachine.stop
|
|
316
447
|
}
|
|
317
|
-
http.callback { failed }
|
|
448
|
+
http.callback { failed(http) }
|
|
318
449
|
}
|
|
319
450
|
end
|
|
320
451
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
http.
|
|
327
|
-
|
|
452
|
+
context "redirect" do
|
|
453
|
+
it "should report last_effective_url" do
|
|
454
|
+
EventMachine.run {
|
|
455
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
456
|
+
http.errback { failed(http) }
|
|
457
|
+
http.callback {
|
|
458
|
+
http.response_header.status.should == 200
|
|
459
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
|
328
460
|
|
|
329
|
-
|
|
461
|
+
EM.stop
|
|
462
|
+
}
|
|
330
463
|
}
|
|
331
|
-
|
|
332
|
-
end
|
|
464
|
+
end
|
|
333
465
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
466
|
+
it "should follow location redirects" do
|
|
467
|
+
EventMachine.run {
|
|
468
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get :redirects => 1
|
|
469
|
+
http.errback { failed(http) }
|
|
470
|
+
http.callback {
|
|
471
|
+
http.response_header.status.should == 200
|
|
472
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
|
473
|
+
http.response.should == "compressed"
|
|
474
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
475
|
+
http.redirects.should == 1
|
|
344
476
|
|
|
345
|
-
|
|
477
|
+
EM.stop
|
|
478
|
+
}
|
|
346
479
|
}
|
|
347
|
-
|
|
348
|
-
end
|
|
480
|
+
end
|
|
349
481
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
482
|
+
it "should default to 0 redirects" do
|
|
483
|
+
EventMachine.run {
|
|
484
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get
|
|
485
|
+
http.errback { failed(http) }
|
|
486
|
+
http.callback {
|
|
487
|
+
http.response_header.status.should == 301
|
|
488
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
489
|
+
http.redirects.should == 0
|
|
358
490
|
|
|
359
|
-
|
|
491
|
+
EM.stop
|
|
492
|
+
}
|
|
360
493
|
}
|
|
361
|
-
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it "should not invoke redirect logic on failed(http) connections" do
|
|
497
|
+
EventMachine.run {
|
|
498
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get :timeout => 0.1, :redirects => 5
|
|
499
|
+
http.callback { failed(http) }
|
|
500
|
+
http.errback {
|
|
501
|
+
http.redirects.should == 0
|
|
502
|
+
EM.stop
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
it "should normalize redirect urls" do
|
|
508
|
+
EventMachine.run {
|
|
509
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/bad').get :redirects => 1
|
|
510
|
+
http.errback { failed(http) }
|
|
511
|
+
http.callback {
|
|
512
|
+
http.last_effective_url.to_s.should match('http://127.0.0.1:8080/')
|
|
513
|
+
http.response.should match('Hello, World!')
|
|
514
|
+
EM.stop
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
it "should fail gracefully on a missing host in absolute Location header" do
|
|
520
|
+
EventMachine.run {
|
|
521
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/nohost').get :redirects => 1
|
|
522
|
+
http.callback { failed(http) }
|
|
523
|
+
http.errback {
|
|
524
|
+
http.error.should == 'Location header format error'
|
|
525
|
+
EM.stop
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
it "should fail gracefully on an invalid host in Location header" do
|
|
531
|
+
EventMachine.run {
|
|
532
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/badhost').get :redirects => 1
|
|
533
|
+
http.callback { failed(http) }
|
|
534
|
+
http.errback {
|
|
535
|
+
http.error.should == 'unable to resolve server address'
|
|
536
|
+
EM.stop
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
end
|
|
362
540
|
end
|
|
363
541
|
|
|
364
542
|
it "should optionally pass the response body progressively" do
|
|
@@ -366,7 +544,7 @@ describe EventMachine::HttpRequest do
|
|
|
366
544
|
body = ''
|
|
367
545
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
368
546
|
|
|
369
|
-
http.errback { failed }
|
|
547
|
+
http.errback { failed(http) }
|
|
370
548
|
http.stream { |chunk| body += chunk }
|
|
371
549
|
|
|
372
550
|
http.callback {
|
|
@@ -378,12 +556,55 @@ describe EventMachine::HttpRequest do
|
|
|
378
556
|
}
|
|
379
557
|
end
|
|
380
558
|
|
|
559
|
+
context "optional header callback" do
|
|
560
|
+
it "should optionally pass the response headers" do
|
|
561
|
+
EventMachine.run {
|
|
562
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
563
|
+
|
|
564
|
+
http.errback { failed(http) }
|
|
565
|
+
http.headers { |hash|
|
|
566
|
+
hash.should be_an_kind_of Hash
|
|
567
|
+
hash.should include 'CONNECTION'
|
|
568
|
+
hash.should include 'CONTENT_LENGTH'
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
http.callback {
|
|
572
|
+
http.response_header.status.should == 200
|
|
573
|
+
http.response.should match(/Hello/)
|
|
574
|
+
EventMachine.stop
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
it "should allow to terminate current connection from header callback" do
|
|
580
|
+
EventMachine.run {
|
|
581
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
582
|
+
|
|
583
|
+
http.callback { failed(http) }
|
|
584
|
+
http.headers { |hash|
|
|
585
|
+
hash.should be_an_kind_of Hash
|
|
586
|
+
hash.should include 'CONNECTION'
|
|
587
|
+
hash.should include 'CONTENT_LENGTH'
|
|
588
|
+
|
|
589
|
+
http.close('header callback terminated connection')
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
http.errback { |e|
|
|
593
|
+
http.response_header.status.should == 200
|
|
594
|
+
http.error.should == 'header callback terminated connection'
|
|
595
|
+
http.response.should == ''
|
|
596
|
+
EventMachine.stop
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
381
602
|
it "should optionally pass the deflate-encoded response body progressively" do
|
|
382
603
|
EventMachine.run {
|
|
383
604
|
body = ''
|
|
384
605
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/deflate').get :head => {"accept-encoding" => "deflate, compressed"}
|
|
385
606
|
|
|
386
|
-
http.errback { failed }
|
|
607
|
+
http.errback { failed(http) }
|
|
387
608
|
http.stream { |chunk| body += chunk }
|
|
388
609
|
|
|
389
610
|
http.callback {
|
|
@@ -400,7 +621,7 @@ describe EventMachine::HttpRequest do
|
|
|
400
621
|
EventMachine.run {
|
|
401
622
|
http = EventMachine::HttpRequest.new('https://mail.google.com:443/mail/').get
|
|
402
623
|
|
|
403
|
-
http.errback { failed }
|
|
624
|
+
http.errback { failed(http) }
|
|
404
625
|
http.callback {
|
|
405
626
|
http.response_header.status.should == 302
|
|
406
627
|
EventMachine.stop
|
|
@@ -412,7 +633,7 @@ describe EventMachine::HttpRequest do
|
|
|
412
633
|
EventMachine.run {
|
|
413
634
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/set_cookie').get
|
|
414
635
|
|
|
415
|
-
http.errback { failed }
|
|
636
|
+
http.errback { failed(http) }
|
|
416
637
|
http.callback {
|
|
417
638
|
http.response_header.status.should == 200
|
|
418
639
|
http.response_header.cookie.should == "id=1; expires=Tue, 09-Aug-2011 17:53:39 GMT; path=/;"
|
|
@@ -425,7 +646,7 @@ describe EventMachine::HttpRequest do
|
|
|
425
646
|
EventMachine.run {
|
|
426
647
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_cookie').get :head => {'cookie' => 'id=2;'}
|
|
427
648
|
|
|
428
|
-
http.errback { failed }
|
|
649
|
+
http.errback { failed(http) }
|
|
429
650
|
http.callback {
|
|
430
651
|
http.response.should == "id=2;"
|
|
431
652
|
EventMachine.stop
|
|
@@ -437,7 +658,7 @@ describe EventMachine::HttpRequest do
|
|
|
437
658
|
EventMachine.run {
|
|
438
659
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_cookie').get :head => {'cookie' => {'id' => 2}}
|
|
439
660
|
|
|
440
|
-
http.errback { failed }
|
|
661
|
+
http.errback { failed(http) }
|
|
441
662
|
http.callback {
|
|
442
663
|
http.response.should == "id=2;"
|
|
443
664
|
EventMachine.stop
|
|
@@ -451,7 +672,7 @@ describe EventMachine::HttpRequest do
|
|
|
451
672
|
@s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo")
|
|
452
673
|
|
|
453
674
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
|
|
454
|
-
http.errback { failed }
|
|
675
|
+
http.errback { failed(http) }
|
|
455
676
|
http.callback {
|
|
456
677
|
http.response.should match(/Foo/)
|
|
457
678
|
http.response_header['CONTENT_LENGTH'].should_not == 0
|
|
@@ -467,7 +688,7 @@ describe EventMachine::HttpRequest do
|
|
|
467
688
|
@s = StubServer.new("HTTP/1.0 200 OK\nContent-Type: text/plain\nContent-Length: 3\nConnection: close\n\nFoo")
|
|
468
689
|
|
|
469
690
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
|
|
470
|
-
http.errback { failed }
|
|
691
|
+
http.errback { failed(http) }
|
|
471
692
|
http.callback {
|
|
472
693
|
http.response_header.status.should == 200
|
|
473
694
|
http.response_header['CONTENT_TYPE'].should == 'text/plain'
|
|
@@ -485,7 +706,7 @@ describe EventMachine::HttpRequest do
|
|
|
485
706
|
EventMachine.run {
|
|
486
707
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post :body => "data"
|
|
487
708
|
|
|
488
|
-
http.errback { failed }
|
|
709
|
+
http.errback { failed(http) }
|
|
489
710
|
http.callback {
|
|
490
711
|
http.response_header.status.should == 200
|
|
491
712
|
http.response.should be_empty
|
|
@@ -498,7 +719,7 @@ describe EventMachine::HttpRequest do
|
|
|
498
719
|
EventMachine.run {
|
|
499
720
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post :body => {:a => :b}
|
|
500
721
|
|
|
501
|
-
http.errback { failed }
|
|
722
|
+
http.errback { failed(http) }
|
|
502
723
|
http.callback {
|
|
503
724
|
http.response_header.status.should == 200
|
|
504
725
|
http.response.should match("application/x-www-form-urlencoded")
|
|
@@ -509,24 +730,59 @@ describe EventMachine::HttpRequest do
|
|
|
509
730
|
|
|
510
731
|
it "should not override content-type when passing in ruby hash/array for body" do
|
|
511
732
|
EventMachine.run {
|
|
733
|
+
ct = 'text; charset=utf-8'
|
|
734
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post({
|
|
735
|
+
:body => {:a => :b}, :head => {'content-type' => ct}})
|
|
736
|
+
|
|
737
|
+
http.errback { failed(http) }
|
|
738
|
+
http.callback {
|
|
739
|
+
http.response_header.status.should == 200
|
|
740
|
+
http.content_charset.should == Encoding.find('utf-8')
|
|
741
|
+
http.response_header["CONTENT_TYPE"].should == ct
|
|
742
|
+
EventMachine.stop
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
it "should default to external encoding on invalid encoding" do
|
|
748
|
+
EventMachine.run {
|
|
749
|
+
ct = 'text/html; charset=utf-8lias'
|
|
512
750
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post({
|
|
513
|
-
|
|
751
|
+
:body => {:a => :b}, :head => {'content-type' => ct}})
|
|
514
752
|
|
|
515
|
-
http.errback { failed }
|
|
753
|
+
http.errback { failed(http) }
|
|
516
754
|
http.callback {
|
|
517
755
|
http.response_header.status.should == 200
|
|
518
|
-
http.
|
|
756
|
+
http.content_charset.should == Encoding.find('utf-8')
|
|
757
|
+
http.response_header["CONTENT_TYPE"].should == ct
|
|
519
758
|
EventMachine.stop
|
|
520
759
|
}
|
|
521
760
|
}
|
|
522
761
|
end
|
|
762
|
+
|
|
763
|
+
it "should processed escaped content-type" do
|
|
764
|
+
EventMachine.run {
|
|
765
|
+
ct = "text/html; charset=\"ISO-8859-4\""
|
|
766
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post({
|
|
767
|
+
:body => {:a => :b}, :head => {'content-type' => ct}})
|
|
768
|
+
|
|
769
|
+
http.errback { failed(http) }
|
|
770
|
+
http.callback {
|
|
771
|
+
http.response_header.status.should == 200
|
|
772
|
+
http.content_charset.should == Encoding.find('ISO-8859-4')
|
|
773
|
+
http.response_header["CONTENT_TYPE"].should == ct
|
|
774
|
+
EventMachine.stop
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
end
|
|
778
|
+
|
|
523
779
|
end
|
|
524
780
|
|
|
525
781
|
it "should complete a Location: with a relative path" do
|
|
526
782
|
EventMachine.run {
|
|
527
783
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/relative-location').get
|
|
528
784
|
|
|
529
|
-
http.errback { failed }
|
|
785
|
+
http.errback { failed(http) }
|
|
530
786
|
http.callback {
|
|
531
787
|
http.response_header['LOCATION'].should == 'http://127.0.0.1:8080/forwarded'
|
|
532
788
|
EventMachine.stop
|
|
@@ -538,7 +794,7 @@ describe EventMachine::HttpRequest do
|
|
|
538
794
|
EventMachine.run {
|
|
539
795
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :file => 'spec/fixtures/google.ca'
|
|
540
796
|
|
|
541
|
-
http.errback { failed }
|
|
797
|
+
http.errback { failed(http) }
|
|
542
798
|
http.callback {
|
|
543
799
|
http.response.should match('google')
|
|
544
800
|
EventMachine.stop
|
|
@@ -554,7 +810,7 @@ describe EventMachine::HttpRequest do
|
|
|
554
810
|
c.options[:body] = {:callback_run => 'yes'}
|
|
555
811
|
client = c
|
|
556
812
|
}
|
|
557
|
-
http.errback { failed }
|
|
813
|
+
http.errback { failed(http) }
|
|
558
814
|
http.callback {
|
|
559
815
|
client.should be_kind_of(EventMachine::HttpClient)
|
|
560
816
|
http.response_header.status.should == 200
|
|
@@ -571,8 +827,8 @@ describe EventMachine::HttpRequest do
|
|
|
571
827
|
http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
|
|
572
828
|
http.errback { fail }
|
|
573
829
|
http.callback {
|
|
574
|
-
c1 = "PREF=ID=
|
|
575
|
-
c2 = "NID=
|
|
830
|
+
c1 = "PREF=ID=11955ae9690fd292:TM=1281823106:LM=1281823106:S=wHdloFqGQ_OLSE92; expires=Mon, 13-Aug-2012 21:58:26 GMT; path=/; domain=.google.ca"
|
|
831
|
+
c2 = "NID=37=USTdOsxOSMbLjphkJ3S5Ueua3Yc23COXuK_pbztcHx7JoyhomwQySrvebCf3_u8eyrBiLWssVzaZcEOiKGEJbNdy8lRhnq_mfrdz693LaMjNPh__ccW4sgn1ZO6nQltE; expires=Sun, 13-Feb-2011 21:58:26 GMT; path=/; domain=.google.ca; HttpOnly"
|
|
576
832
|
http.response_header.cookie.should == [c1, c2]
|
|
577
833
|
|
|
578
834
|
EventMachine.stop
|
|
@@ -582,35 +838,93 @@ describe EventMachine::HttpRequest do
|
|
|
582
838
|
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
|
583
839
|
end
|
|
584
840
|
|
|
585
|
-
context "connections via
|
|
841
|
+
context "connections via" do
|
|
842
|
+
context "direct proxy" do
|
|
843
|
+
it "should default to skip CONNECT" do
|
|
844
|
+
EventMachine.run {
|
|
586
845
|
|
|
587
|
-
|
|
588
|
-
|
|
846
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
|
847
|
+
:host => '127.0.0.1', :port => 8083
|
|
848
|
+
}
|
|
589
849
|
|
|
590
|
-
|
|
850
|
+
http.errback { failed(http) }
|
|
851
|
+
http.callback {
|
|
852
|
+
http.response_header.status.should == 200
|
|
853
|
+
http.response.should match('test')
|
|
854
|
+
EventMachine.stop
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
end
|
|
591
858
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
http.
|
|
596
|
-
|
|
859
|
+
it "should send absolute URIs to the proxy server" do
|
|
860
|
+
EventMachine.run {
|
|
861
|
+
|
|
862
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
|
863
|
+
:host => '127.0.0.1', :port => 8083
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
http.errback { failed(http) }
|
|
867
|
+
http.callback {
|
|
868
|
+
http.response_header.status.should == 200
|
|
869
|
+
# The test proxy server gives the requested uri back in this header
|
|
870
|
+
http.response_header['X_THE_REQUESTED_URI'].should == 'http://127.0.0.1:8080/?q=test'
|
|
871
|
+
http.response_header['X_THE_REQUESTED_URI'].should_not == '/?q=test'
|
|
872
|
+
http.response.should match('test')
|
|
873
|
+
EventMachine.stop
|
|
874
|
+
}
|
|
597
875
|
}
|
|
598
|
-
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
it "should include query parameters specified in the options" do
|
|
879
|
+
EventMachine.run {
|
|
880
|
+
|
|
881
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(
|
|
882
|
+
:proxy => { :host => '127.0.0.1', :port => 8083 },
|
|
883
|
+
:query => { 'q' => 'test' }
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
http.errback { failed(http) }
|
|
887
|
+
http.callback {
|
|
888
|
+
http.response_header.status.should == 200
|
|
889
|
+
http.response.should match('test')
|
|
890
|
+
EventMachine.stop
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
end
|
|
599
894
|
end
|
|
600
895
|
|
|
601
|
-
|
|
602
|
-
|
|
896
|
+
context "CONNECT proxy" do
|
|
897
|
+
it "should work with CONNECT proxy servers" do
|
|
898
|
+
EventMachine.run {
|
|
603
899
|
|
|
604
|
-
|
|
605
|
-
|
|
900
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get({
|
|
901
|
+
:proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
|
902
|
+
})
|
|
606
903
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
904
|
+
http.errback { failed(http) }
|
|
905
|
+
http.callback {
|
|
906
|
+
http.response_header.status.should == 200
|
|
907
|
+
http.response.should == 'Hello, World!'
|
|
908
|
+
EventMachine.stop
|
|
909
|
+
}
|
|
612
910
|
}
|
|
613
|
-
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
it "should proxy POST data" do
|
|
914
|
+
EventMachine.run {
|
|
915
|
+
|
|
916
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
|
|
917
|
+
:body => "data", :proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
http.errback { failed(http) }
|
|
921
|
+
http.callback {
|
|
922
|
+
http.response_header.status.should == 200
|
|
923
|
+
http.response.should match(/data/)
|
|
924
|
+
EventMachine.stop
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
end
|
|
614
928
|
end
|
|
615
929
|
end
|
|
616
930
|
|
|
@@ -626,7 +940,7 @@ describe EventMachine::HttpRequest do
|
|
|
626
940
|
EventMachine.run {
|
|
627
941
|
http = EventMachine::HttpRequest.new('ws://127.0.0.1:8080/').get :timeout => 0
|
|
628
942
|
|
|
629
|
-
http.callback { failed }
|
|
943
|
+
http.callback { failed(http) }
|
|
630
944
|
http.errback {
|
|
631
945
|
http.response_header.status.should == 200
|
|
632
946
|
EventMachine.stop
|
|
@@ -643,7 +957,7 @@ describe EventMachine::HttpRequest do
|
|
|
643
957
|
end
|
|
644
958
|
|
|
645
959
|
http = EventMachine::HttpRequest.new('ws://127.0.0.1:8085/').get :timeout => 1
|
|
646
|
-
http.errback { failed }
|
|
960
|
+
http.errback { failed(http) }
|
|
647
961
|
http.callback {
|
|
648
962
|
http.response_header.status.should == 101
|
|
649
963
|
http.response_header['CONNECTION'].should match(/Upgrade/)
|
|
@@ -674,7 +988,7 @@ describe EventMachine::HttpRequest do
|
|
|
674
988
|
|
|
675
989
|
EventMachine.add_timer(0.1) do
|
|
676
990
|
http = EventMachine::HttpRequest.new('ws://127.0.0.1:8085/').get :timeout => 0
|
|
677
|
-
http.errback { failed }
|
|
991
|
+
http.errback { failed(http) }
|
|
678
992
|
http.callback { http.response_header.status.should == 101 }
|
|
679
993
|
http.stream {|msg|
|
|
680
994
|
msg.should == messages[recieved.size]
|