em-http-request 1.0.0 → 1.0.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.

Potentially problematic release.


This version of em-http-request might be problematic. Click here for more details.

@@ -29,12 +29,14 @@ module EventMachine
29
29
  attr_reader :requests, :responses
30
30
 
31
31
  def initialize
32
- @requests = []
32
+ @requests = {}
33
33
  @responses = {:callback => {}, :errback => {}}
34
34
  end
35
35
 
36
36
  def add(name, conn)
37
- @requests.push(conn)
37
+ raise 'Duplicate Multi key' if @requests.key? name
38
+
39
+ @requests[name] = conn
38
40
 
39
41
  conn.callback { @responses[:callback][name] = conn; check_progress }
40
42
  conn.errback { @responses[:errback][name] = conn; check_progress }
@@ -1,5 +1,5 @@
1
- module EventMachine
2
- class HttpRequest
3
- VERSION = "1.0.0"
4
- end
5
- end
1
+ module EventMachine
2
+ class HttpRequest
3
+ VERSION = "1.0.1"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'helper'
2
+ require 'fiber'
3
+
4
+ describe EventMachine::HttpRequest do
5
+ context "with fibers" do
6
+ it "should be transparent to connexion errors" do
7
+ EventMachine.run do
8
+ Fiber.new do
9
+ f = Fiber.current
10
+ http = EventMachine::HttpRequest.new('http://non-existing.domain/').get
11
+ http.callback {failed(http)}
12
+ http.errback {f.resume}
13
+ Fiber.yield
14
+ EventMachine.stop
15
+ end.resume
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -1,669 +1,734 @@
1
- require 'helper'
2
-
3
- describe EventMachine::HttpRequest do
4
-
5
- def failed(http=nil)
6
- EventMachine.stop
7
- http ? fail(http.error) : fail
8
- end
9
-
10
- it "should perform successful GET" do
11
- EventMachine.run {
12
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
13
-
14
- http.errback { failed(http) }
15
- http.callback {
16
- http.response_header.status.should == 200
17
- http.response.should match(/Hello/)
18
- EventMachine.stop
19
- }
20
- }
21
- end
22
-
23
- it "should perform successful GET with a URI passed as argument" do
24
- EventMachine.run {
25
- uri = URI.parse('http://127.0.0.1:8090/')
26
- http = EventMachine::HttpRequest.new(uri).get
27
-
28
- http.errback { failed(http) }
29
- http.callback {
30
- http.response_header.status.should == 200
31
- http.response.should match(/Hello/)
32
- EventMachine.stop
33
- }
34
- }
35
- end
36
-
37
- it "should succeed GET on missing path" do
38
- EventMachine.run {
39
- lambda {
40
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090').get
41
- http.callback {
42
- http.response.should match(/Hello/)
43
- EventMachine.stop
44
- }
45
- }.should_not raise_error(ArgumentError)
46
-
47
- }
48
- end
49
-
50
- it "should raise error on invalid URL" do
51
- EventMachine.run {
52
- lambda {
53
- EventMachine::HttpRequest.new('random?text').get
54
- }.should raise_error
55
-
56
- EM.stop
57
- }
58
- end
59
-
60
- it "should perform successful HEAD with a URI passed as argument" do
61
- EventMachine.run {
62
- uri = URI.parse('http://127.0.0.1:8090/')
63
- http = EventMachine::HttpRequest.new(uri).head
64
-
65
- http.errback { failed(http) }
66
- http.callback {
67
- http.response_header.status.should == 200
68
- http.response.should == ""
69
- EventMachine.stop
70
- }
71
- }
72
- end
73
-
74
- it "should perform successful DELETE with a URI passed as argument" do
75
- EventMachine.run {
76
- uri = URI.parse('http://127.0.0.1:8090/')
77
- http = EventMachine::HttpRequest.new(uri).delete
78
-
79
- http.errback { failed(http) }
80
- http.callback {
81
- http.response_header.status.should == 200
82
- http.response.should == ""
83
- EventMachine.stop
84
- }
85
- }
86
- end
87
-
88
- it "should return 404 on invalid path" do
89
- EventMachine.run {
90
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get
91
-
92
- http.errback { failed(http) }
93
- http.callback {
94
- http.response_header.status.should == 404
95
- EventMachine.stop
96
- }
97
- }
98
- end
99
-
100
- it "should return HTTP reason" do
101
- EventMachine.run {
102
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get
103
-
104
- http.errback { failed(http) }
105
- http.callback {
106
- http.response_header.status.should == 404
107
- http.response_header.http_reason.should == 'Not Found'
108
- EventMachine.stop
109
- }
110
- }
111
- end
112
-
113
- it "should return HTTP reason 'unknown' on a non-standard status code" do
114
- EventMachine.run {
115
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail_with_nonstandard_response').get
116
-
117
- http.errback { failed(http) }
118
- http.callback {
119
- http.response_header.status.should == 420
120
- http.response_header.http_reason.should == 'unknown'
121
- EventMachine.stop
122
- }
123
- }
124
- end
125
-
126
- it "should build query parameters from Hash" do
127
- EventMachine.run {
128
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => {:q => 'test'}
129
-
130
- http.errback { failed(http) }
131
- http.callback {
132
- http.response_header.status.should == 200
133
- http.response.should match(/test/)
134
- EventMachine.stop
135
- }
136
- }
137
- end
138
-
139
- it "should pass query parameters string" do
140
- EventMachine.run {
141
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => "q=test"
142
-
143
- http.errback { failed(http) }
144
- http.callback {
145
- http.response_header.status.should == 200
146
- http.response.should match(/test/)
147
- EventMachine.stop
148
- }
149
- }
150
- end
151
-
152
- it "should encode an array of query parameters" do
153
- EventMachine.run {
154
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get :query => {:hash =>['value1','value2']}
155
-
156
- http.errback { failed(http) }
157
- http.callback {
158
- http.response_header.status.should == 200
159
- http.response.should match(/hash\[\]=value1&hash\[\]=value2/)
160
- EventMachine.stop
161
- }
162
- }
163
- end
164
-
165
- it "should perform successful PUT" do
166
- EventMachine.run {
167
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').put :body => "data"
168
-
169
- http.errback { failed(http) }
170
- http.callback {
171
- http.response_header.status.should == 200
172
- http.response.should match(/data/)
173
- EventMachine.stop
174
- }
175
- }
176
- end
177
-
178
- it "should perform successful POST" do
179
- EventMachine.run {
180
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => "data"
181
-
182
- http.errback { failed(http) }
183
- http.callback {
184
- http.response_header.status.should == 200
185
- http.response.should match(/data/)
186
- EventMachine.stop
187
- }
188
- }
189
- end
190
-
191
- it "should escape body on POST" do
192
- EventMachine.run {
193
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {:stuff => 'string&string'}
194
-
195
- http.errback { failed(http) }
196
- http.callback {
197
- http.response_header.status.should == 200
198
- http.response.should == "stuff=string%26string"
199
- EventMachine.stop
200
- }
201
- }
202
- end
203
-
204
- it "should perform successful POST with Ruby Hash/Array as params" do
205
- EventMachine.run {
206
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {"key1" => 1, "key2" => [2,3]}
207
-
208
- http.errback { failed(http) }
209
- http.callback {
210
- http.response_header.status.should == 200
211
-
212
- http.response.should match(/key1=1&key2\[0\]=2&key2\[1\]=3/)
213
- EventMachine.stop
214
- }
215
- }
216
- end
217
-
218
- it "should perform successful POST with Ruby Hash/Array as params and with the correct content length" do
219
- EventMachine.run {
220
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_length').post :body => {"key1" => "data1"}
221
-
222
- http.errback { failed(http) }
223
- http.callback {
224
- http.response_header.status.should == 200
225
-
226
- http.response.to_i.should == 10
227
- EventMachine.stop
228
- }
229
- }
230
- end
231
-
232
- it "should perform successful GET with custom header" do
233
- EventMachine.run {
234
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'if-none-match' => 'evar!'}
235
-
236
- http.errback { p http; failed(http) }
237
- http.callback {
238
- http.response_header.status.should == 304
239
- EventMachine.stop
240
- }
241
- }
242
- end
243
-
244
- it "should perform basic auth" do
245
- EventMachine.run {
246
-
247
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'authorization' => ['user', 'pass']}
248
-
249
- http.errback { failed(http) }
250
- http.callback {
251
- http.response_header.status.should == 200
252
- EventMachine.stop
253
- }
254
- }
255
- end
256
-
257
- it "should return peer's IP address" do
258
- EventMachine.run {
259
-
260
- conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
261
- conn.peer.should be_nil
262
-
263
- http = conn.get
264
- http.peer.should be_nil
265
-
266
- http.errback { failed(http) }
267
- http.callback {
268
- conn.peer.should == '127.0.0.1'
269
- http.peer.should == '127.0.0.1'
270
-
271
- EventMachine.stop
272
- }
273
- }
274
- end
275
-
276
- it "should remove all newlines from long basic auth header" do
277
- EventMachine.run {
278
- auth = {'authorization' => ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz']}
279
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => auth
280
- http.errback { failed(http) }
281
- http.callback {
282
- http.response_header.status.should == 200
283
- http.response.should == "Basic YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhOnp6enp6enp6enp6enp6enp6enp6enp6enp6enp6eg=="
284
- EventMachine.stop
285
- }
286
- }
287
- end
288
-
289
- it "should send proper OAuth auth header" do
290
- EventMachine.run {
291
- oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
292
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => {
293
- 'authorization' => oauth_header
294
- }
295
-
296
- http.errback { failed(http) }
297
- http.callback {
298
- http.response_header.status.should == 200
299
- http.response.should == oauth_header
300
- EventMachine.stop
301
- }
302
- }
303
- end
304
-
305
- it "should return ETag and Last-Modified headers" do
306
- EventMachine.run {
307
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get
308
-
309
- http.errback { failed(http) }
310
- http.callback {
311
- http.response_header.status.should == 200
312
- http.response_header.etag.should match('abcdefg')
313
- http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
314
- EventMachine.stop
315
- }
316
- }
317
- end
318
-
319
- it "should detect deflate encoding" do
320
- EventMachine.run {
321
-
322
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {"accept-encoding" => "deflate"}
323
-
324
- http.errback { failed(http) }
325
- http.callback {
326
- http.response_header.status.should == 200
327
- http.response_header["CONTENT_ENCODING"].should == "deflate"
328
- http.response.should == "compressed"
329
-
330
- EventMachine.stop
331
- }
332
- }
333
- end
334
-
335
- it "should detect gzip encoding" do
336
- EventMachine.run {
337
-
338
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
339
- "accept-encoding" => "gzip, compressed"
340
- }
341
-
342
- http.errback { failed(http) }
343
- http.callback {
344
- http.response_header.status.should == 200
345
- http.response_header["CONTENT_ENCODING"].should == "gzip"
346
- http.response.should == "compressed"
347
-
348
- EventMachine.stop
349
- }
350
- }
351
- end
352
-
353
- it "should not decode the response when configured so" do
354
- EventMachine.run {
355
-
356
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
357
- "accept-encoding" => "gzip, compressed"
358
- }, :decoding => false
359
-
360
- http.errback { failed(http) }
361
- http.callback {
362
- http.response_header.status.should == 200
363
- http.response_header["CONTENT_ENCODING"].should == "gzip"
364
-
365
- raw = http.response
366
- Zlib::GzipReader.new(StringIO.new(raw)).read.should == "compressed"
367
-
368
- EventMachine.stop
369
- }
370
- }
371
- end
372
-
373
- it "should timeout after 0.1 seconds of inactivity" do
374
- EventMachine.run {
375
- t = Time.now.to_i
376
- EventMachine.heartbeat_interval = 0.1
377
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get
378
-
379
- http.errback {
380
- (Time.now.to_i - t).should <= 1
381
- EventMachine.stop
382
- }
383
- http.callback { failed(http) }
384
- }
385
- end
386
-
387
- it "should complete a Location: with a relative path" do
388
- EventMachine.run {
389
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/relative-location').get
390
-
391
- http.errback { failed(http) }
392
- http.callback {
393
- http.response_header['LOCATION'].should == 'http://127.0.0.1:8090/forwarded'
394
- EventMachine.stop
395
- }
396
- }
397
- end
398
-
399
- context "body content-type encoding" do
400
- it "should not set content type on string in body" do
401
- EventMachine.run {
402
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => "data"
403
-
404
- http.errback { failed(http) }
405
- http.callback {
406
- http.response_header.status.should == 200
407
- http.response.should be_empty
408
- EventMachine.stop
409
- }
410
- }
411
- end
412
-
413
- it "should set content-type automatically when passed a ruby hash/array for body" do
414
- EventMachine.run {
415
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => {:a => :b}
416
-
417
- http.errback { failed(http) }
418
- http.callback {
419
- http.response_header.status.should == 200
420
- http.response.should match("application/x-www-form-urlencoded")
421
- EventMachine.stop
422
- }
423
- }
424
- end
425
-
426
- it "should not override content-type when passing in ruby hash/array for body" do
427
- EventMachine.run {
428
- ct = 'text; charset=utf-8'
429
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
430
- :body => {:a => :b}, :head => {'content-type' => ct}})
431
-
432
- http.errback { failed(http) }
433
- http.callback {
434
- http.response_header.status.should == 200
435
- http.content_charset.should == Encoding.find('utf-8')
436
- http.response_header["CONTENT_TYPE"].should == ct
437
- EventMachine.stop
438
- }
439
- }
440
- end
441
-
442
- it "should default to external encoding on invalid encoding" do
443
- EventMachine.run {
444
- ct = 'text/html; charset=utf-8lias'
445
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
446
- :body => {:a => :b}, :head => {'content-type' => ct}})
447
-
448
- http.errback { failed(http) }
449
- http.callback {
450
- http.response_header.status.should == 200
451
- http.content_charset.should == Encoding.find('utf-8')
452
- http.response_header["CONTENT_TYPE"].should == ct
453
- EventMachine.stop
454
- }
455
- }
456
- end
457
-
458
- it "should processed escaped content-type" do
459
- EventMachine.run {
460
- ct = "text/html; charset=\"ISO-8859-4\""
461
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
462
- :body => {:a => :b}, :head => {'content-type' => ct}})
463
-
464
- http.errback { failed(http) }
465
- http.callback {
466
- http.response_header.status.should == 200
467
- http.content_charset.should == Encoding.find('ISO-8859-4')
468
- http.response_header["CONTENT_TYPE"].should == ct
469
- EventMachine.stop
470
- }
471
- }
472
- end
473
- end
474
-
475
- context "optional header callback" do
476
- it "should optionally pass the response headers" do
477
- EventMachine.run {
478
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
479
-
480
- http.errback { failed(http) }
481
- http.headers { |hash|
482
- hash.should be_an_kind_of Hash
483
- hash.should include 'CONNECTION'
484
- hash.should include 'CONTENT_LENGTH'
485
- }
486
-
487
- http.callback {
488
- http.response_header.status.should == 200
489
- http.response.should match(/Hello/)
490
- EventMachine.stop
491
- }
492
- }
493
- end
494
-
495
- it "should allow to terminate current connection from header callback" do
496
- EventMachine.run {
497
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
498
-
499
- http.callback { failed(http) }
500
- http.headers { |hash|
501
- hash.should be_an_kind_of Hash
502
- hash.should include 'CONNECTION'
503
- hash.should include 'CONTENT_LENGTH'
504
-
505
- http.close('header callback terminated connection')
506
- }
507
-
508
- http.errback { |e|
509
- http.response_header.status.should == 200
510
- http.error.should == 'header callback terminated connection'
511
- http.response.should == ''
512
- EventMachine.stop
513
- }
514
- }
515
- end
516
- end
517
-
518
- it "should optionally pass the response body progressively" do
519
- EventMachine.run {
520
- body = ''
521
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
522
-
523
- http.errback { failed(http) }
524
- http.stream { |chunk| body += chunk }
525
-
526
- http.callback {
527
- http.response_header.status.should == 200
528
- http.response.should == ''
529
- body.should match(/Hello/)
530
- EventMachine.stop
531
- }
532
- }
533
- end
534
-
535
- it "should optionally pass the deflate-encoded response body progressively" do
536
- EventMachine.run {
537
- body = ''
538
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {
539
- "accept-encoding" => "deflate, compressed"
540
- }
541
-
542
- http.errback { failed(http) }
543
- http.stream { |chunk| body += chunk }
544
-
545
- http.callback {
546
- http.response_header.status.should == 200
547
- http.response_header["CONTENT_ENCODING"].should == "deflate"
548
- http.response.should == ''
549
- body.should == "compressed"
550
- EventMachine.stop
551
- }
552
- }
553
- end
554
-
555
- it "should accept & return cookie header to user" do
556
- EventMachine.run {
557
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_cookie').get
558
-
559
- http.errback { failed(http) }
560
- http.callback {
561
- http.response_header.status.should == 200
562
- http.response_header.cookie.should == "id=1; expires=Tue, 09-Aug-2011 17:53:39 GMT; path=/;"
563
- EventMachine.stop
564
- }
565
- }
566
- end
567
-
568
- it "should return array of cookies on multiple Set-Cookie headers" do
569
- EventMachine.run {
570
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_multiple_cookies').get
571
-
572
- http.errback { failed(http) }
573
- http.callback {
574
- http.response_header.status.should == 200
575
- http.response_header.cookie.size.should == 2
576
- http.response_header.cookie.first.should == "id=1; expires=Tue, 09-Aug-2011 17:53:39 GMT; path=/;"
577
- http.response_header.cookie.last.should == "id=2;"
578
-
579
- EventMachine.stop
580
- }
581
- }
582
- end
583
-
584
- it "should pass cookie header to server from string" do
585
- EventMachine.run {
586
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => 'id=2;'}
587
-
588
- http.errback { failed(http) }
589
- http.callback {
590
- http.response.should == "id=2;"
591
- EventMachine.stop
592
- }
593
- }
594
- end
595
-
596
- it "should pass cookie header to server from Hash" do
597
- EventMachine.run {
598
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => {'id' => 2}}
599
-
600
- http.errback { failed(http) }
601
- http.callback {
602
- http.response.should == "id=2;"
603
- EventMachine.stop
604
- }
605
- }
606
- end
607
-
608
- context "when talking to a stub HTTP/1.0 server" do
609
- it "should get the body without Content-Length" do
610
-
611
- EventMachine.run {
612
- @s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo")
613
-
614
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
615
- http.errback { failed(http) }
616
- http.callback {
617
- http.response.should match(/Foo/)
618
- http.response_header['CONTENT_LENGTH'].should_not == 0
619
-
620
- @s.stop
621
- EventMachine.stop
622
- }
623
- }
624
- end
625
-
626
- it "should work with \\n instead of \\r\\n" do
627
- EventMachine.run {
628
- @s = StubServer.new("HTTP/1.0 200 OK\nContent-Type: text/plain\nContent-Length: 3\nConnection: close\n\nFoo")
629
-
630
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
631
- http.errback { failed(http) }
632
- http.callback {
633
- http.response_header.status.should == 200
634
- http.response_header['CONTENT_TYPE'].should == 'text/plain'
635
- http.response.should match(/Foo/)
636
-
637
- @s.stop
638
- EventMachine.stop
639
- }
640
- }
641
- end
642
-
643
- it "should handle invalid HTTP response" do
644
- EventMachine.run {
645
- @s = StubServer.new("<html></html>")
646
-
647
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
648
- http.callback { failed(http) }
649
- http.errback {
650
- http.error.should_not be_nil
651
- EM.stop
652
- }
653
- }
654
- end
655
- end
656
-
657
- it "should stream a file off disk" do
658
- EventMachine.run {
659
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :file => 'spec/fixtures/google.ca'
660
-
661
- http.errback { failed(http) }
662
- http.callback {
663
- http.response.should match('google')
664
- EventMachine.stop
665
- }
666
- }
667
- end
668
-
669
- end
1
+ require 'helper'
2
+
3
+ describe EventMachine::HttpRequest do
4
+
5
+ def failed(http=nil)
6
+ EventMachine.stop
7
+ http ? fail(http.error) : fail
8
+ end
9
+
10
+ it "should perform successful GET" do
11
+ EventMachine.run {
12
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
13
+
14
+ http.errback { failed(http) }
15
+ http.callback {
16
+ http.response_header.status.should == 200
17
+ http.response.should match(/Hello/)
18
+ EventMachine.stop
19
+ }
20
+ }
21
+ end
22
+
23
+ it "should perform successful GET with a URI passed as argument" do
24
+ EventMachine.run {
25
+ uri = URI.parse('http://127.0.0.1:8090/')
26
+ http = EventMachine::HttpRequest.new(uri).get
27
+
28
+ http.errback { failed(http) }
29
+ http.callback {
30
+ http.response_header.status.should == 200
31
+ http.response.should match(/Hello/)
32
+ EventMachine.stop
33
+ }
34
+ }
35
+ end
36
+
37
+ it "should succeed GET on missing path" do
38
+ EventMachine.run {
39
+ lambda {
40
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090').get
41
+ http.callback {
42
+ http.response.should match(/Hello/)
43
+ EventMachine.stop
44
+ }
45
+ }.should_not raise_error(ArgumentError)
46
+
47
+ }
48
+ end
49
+
50
+ it "should raise error on invalid URL" do
51
+ EventMachine.run {
52
+ lambda {
53
+ EventMachine::HttpRequest.new('random?text').get
54
+ }.should raise_error
55
+
56
+ EM.stop
57
+ }
58
+ end
59
+
60
+ it "should perform successful HEAD with a URI passed as argument" do
61
+ EventMachine.run {
62
+ uri = URI.parse('http://127.0.0.1:8090/')
63
+ http = EventMachine::HttpRequest.new(uri).head
64
+
65
+ http.errback { failed(http) }
66
+ http.callback {
67
+ http.response_header.status.should == 200
68
+ http.response.should == ""
69
+ EventMachine.stop
70
+ }
71
+ }
72
+ end
73
+
74
+ it "should perform successful DELETE with a URI passed as argument" do
75
+ EventMachine.run {
76
+ uri = URI.parse('http://127.0.0.1:8090/')
77
+ http = EventMachine::HttpRequest.new(uri).delete
78
+
79
+ http.errback { failed(http) }
80
+ http.callback {
81
+ http.response_header.status.should == 200
82
+ http.response.should == ""
83
+ EventMachine.stop
84
+ }
85
+ }
86
+ end
87
+
88
+ it "should return 404 on invalid path" do
89
+ EventMachine.run {
90
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get
91
+
92
+ http.errback { failed(http) }
93
+ http.callback {
94
+ http.response_header.status.should == 404
95
+ EventMachine.stop
96
+ }
97
+ }
98
+ end
99
+
100
+ it "should return HTTP reason" do
101
+ EventMachine.run {
102
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail').get
103
+
104
+ http.errback { failed(http) }
105
+ http.callback {
106
+ http.response_header.status.should == 404
107
+ http.response_header.http_reason.should == 'Not Found'
108
+ EventMachine.stop
109
+ }
110
+ }
111
+ end
112
+
113
+ it "should return HTTP reason 'unknown' on a non-standard status code" do
114
+ EventMachine.run {
115
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/fail_with_nonstandard_response').get
116
+
117
+ http.errback { failed(http) }
118
+ http.callback {
119
+ http.response_header.status.should == 420
120
+ http.response_header.http_reason.should == 'unknown'
121
+ EventMachine.stop
122
+ }
123
+ }
124
+ end
125
+
126
+ it "should build query parameters from Hash" do
127
+ EventMachine.run {
128
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => {:q => 'test'}
129
+
130
+ http.errback { failed(http) }
131
+ http.callback {
132
+ http.response_header.status.should == 200
133
+ http.response.should match(/test/)
134
+ EventMachine.stop
135
+ }
136
+ }
137
+ end
138
+
139
+ it "should pass query parameters string" do
140
+ EventMachine.run {
141
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :query => "q=test"
142
+
143
+ http.errback { failed(http) }
144
+ http.callback {
145
+ http.response_header.status.should == 200
146
+ http.response.should match(/test/)
147
+ EventMachine.stop
148
+ }
149
+ }
150
+ end
151
+
152
+ it "should encode an array of query parameters" do
153
+ EventMachine.run {
154
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get :query => {:hash =>['value1','value2']}
155
+
156
+ http.errback { failed(http) }
157
+ http.callback {
158
+ http.response_header.status.should == 200
159
+ http.response.should match(/hash\[\]=value1&hash\[\]=value2/)
160
+ EventMachine.stop
161
+ }
162
+ }
163
+ end
164
+
165
+ it "should perform successful PUT" do
166
+ EventMachine.run {
167
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').put :body => "data"
168
+
169
+ http.errback { failed(http) }
170
+ http.callback {
171
+ http.response_header.status.should == 200
172
+ http.response.should match(/data/)
173
+ EventMachine.stop
174
+ }
175
+ }
176
+ end
177
+
178
+ it "should perform successful POST" do
179
+ EventMachine.run {
180
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => "data"
181
+
182
+ http.errback { failed(http) }
183
+ http.callback {
184
+ http.response_header.status.should == 200
185
+ http.response.should match(/data/)
186
+ EventMachine.stop
187
+ }
188
+ }
189
+ end
190
+
191
+ it "should escape body on POST" do
192
+ EventMachine.run {
193
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {:stuff => 'string&string'}
194
+
195
+ http.errback { failed(http) }
196
+ http.callback {
197
+ http.response_header.status.should == 200
198
+ http.response.should == "stuff=string%26string"
199
+ EventMachine.stop
200
+ }
201
+ }
202
+ end
203
+
204
+ it "should perform successful POST with Ruby Hash/Array as params" do
205
+ EventMachine.run {
206
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :body => {"key1" => 1, "key2" => [2,3]}
207
+
208
+ http.errback { failed(http) }
209
+ http.callback {
210
+ http.response_header.status.should == 200
211
+
212
+ http.response.should match(/key1=1&key2\[0\]=2&key2\[1\]=3/)
213
+ EventMachine.stop
214
+ }
215
+ }
216
+ end
217
+
218
+ it "should set content-length to 0 on posts with empty bodies" do
219
+ EventMachine.run {
220
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_length_from_header').post
221
+
222
+ http.errback { failed(http) }
223
+ http.callback {
224
+ http.response_header.status.should == 200
225
+
226
+ http.response.strip.split(':')[1].should == '0'
227
+ EventMachine.stop
228
+ }
229
+ }
230
+ end
231
+
232
+ it "should perform successful POST with Ruby Hash/Array as params and with the correct content length" do
233
+ EventMachine.run {
234
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_length').post :body => {"key1" => "data1"}
235
+
236
+ http.errback { failed(http) }
237
+ http.callback {
238
+ http.response_header.status.should == 200
239
+
240
+ http.response.to_i.should == 10
241
+ EventMachine.stop
242
+ }
243
+ }
244
+ end
245
+
246
+ it "should perform successful GET with custom header" do
247
+ EventMachine.run {
248
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'if-none-match' => 'evar!'}
249
+
250
+ http.errback { p http; failed(http) }
251
+ http.callback {
252
+ http.response_header.status.should == 304
253
+ EventMachine.stop
254
+ }
255
+ }
256
+ end
257
+
258
+ it "should perform basic auth" do
259
+ EventMachine.run {
260
+
261
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get :head => {'authorization' => ['user', 'pass']}
262
+
263
+ http.errback { failed(http) }
264
+ http.callback {
265
+ http.response_header.status.should == 200
266
+ EventMachine.stop
267
+ }
268
+ }
269
+ end
270
+
271
+ it "should return peer's IP address" do
272
+ EventMachine.run {
273
+
274
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
275
+ conn.peer.should be_nil
276
+
277
+ http = conn.get
278
+ http.peer.should be_nil
279
+
280
+ http.errback { failed(http) }
281
+ http.callback {
282
+ conn.peer.should == '127.0.0.1'
283
+ http.peer.should == '127.0.0.1'
284
+
285
+ EventMachine.stop
286
+ }
287
+ }
288
+ end
289
+
290
+ it "should remove all newlines from long basic auth header" do
291
+ EventMachine.run {
292
+ auth = {'authorization' => ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz']}
293
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => auth
294
+ http.errback { failed(http) }
295
+ http.callback {
296
+ http.response_header.status.should == 200
297
+ http.response.should == "Basic YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhOnp6enp6enp6enp6enp6enp6enp6enp6enp6enp6eg=="
298
+ EventMachine.stop
299
+ }
300
+ }
301
+ end
302
+
303
+ it "should send proper OAuth auth header" do
304
+ EventMachine.run {
305
+ oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
306
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => {
307
+ 'authorization' => oauth_header
308
+ }
309
+
310
+ http.errback { failed(http) }
311
+ http.callback {
312
+ http.response_header.status.should == 200
313
+ http.response.should == oauth_header
314
+ EventMachine.stop
315
+ }
316
+ }
317
+ end
318
+
319
+ it "should return ETag and Last-Modified headers" do
320
+ EventMachine.run {
321
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get
322
+
323
+ http.errback { failed(http) }
324
+ http.callback {
325
+ http.response_header.status.should == 200
326
+ http.response_header.etag.should match('abcdefg')
327
+ http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
328
+ EventMachine.stop
329
+ }
330
+ }
331
+ end
332
+
333
+ it "should detect deflate encoding" do
334
+ EventMachine.run {
335
+
336
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {"accept-encoding" => "deflate"}
337
+
338
+ http.errback { failed(http) }
339
+ http.callback {
340
+ http.response_header.status.should == 200
341
+ http.response_header["CONTENT_ENCODING"].should == "deflate"
342
+ http.response.should == "compressed"
343
+
344
+ EventMachine.stop
345
+ }
346
+ }
347
+ end
348
+
349
+ it "should detect gzip encoding" do
350
+ EventMachine.run {
351
+
352
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
353
+ "accept-encoding" => "gzip, compressed"
354
+ }
355
+
356
+ http.errback { failed(http) }
357
+ http.callback {
358
+ http.response_header.status.should == 200
359
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
360
+ http.response.should == "compressed"
361
+
362
+ EventMachine.stop
363
+ }
364
+ }
365
+ end
366
+
367
+ it "should not decode the response when configured so" do
368
+ EventMachine.run {
369
+
370
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
371
+ "accept-encoding" => "gzip, compressed"
372
+ }, :decoding => false
373
+
374
+ http.errback { failed(http) }
375
+ http.callback {
376
+ http.response_header.status.should == 200
377
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
378
+
379
+ raw = http.response
380
+ Zlib::GzipReader.new(StringIO.new(raw)).read.should == "compressed"
381
+
382
+ EventMachine.stop
383
+ }
384
+ }
385
+ end
386
+
387
+ it "should timeout after 0.1 seconds of inactivity" do
388
+ EventMachine.run {
389
+ t = Time.now.to_i
390
+ EventMachine.heartbeat_interval = 0.1
391
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get
392
+
393
+ http.errback {
394
+ (Time.now.to_i - t).should <= 1
395
+ EventMachine.stop
396
+ }
397
+ http.callback { failed(http) }
398
+ }
399
+ end
400
+
401
+ it "should complete a Location: with a relative path" do
402
+ EventMachine.run {
403
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/relative-location').get
404
+
405
+ http.errback { failed(http) }
406
+ http.callback {
407
+ http.response_header['LOCATION'].should == 'http://127.0.0.1:8090/forwarded'
408
+ EventMachine.stop
409
+ }
410
+ }
411
+ end
412
+
413
+ context "body content-type encoding" do
414
+ it "should not set content type on string in body" do
415
+ EventMachine.run {
416
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => "data"
417
+
418
+ http.errback { failed(http) }
419
+ http.callback {
420
+ http.response_header.status.should == 200
421
+ http.response.should be_empty
422
+ EventMachine.stop
423
+ }
424
+ }
425
+ end
426
+
427
+ it "should set content-type automatically when passed a ruby hash/array for body" do
428
+ EventMachine.run {
429
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => {:a => :b}
430
+
431
+ http.errback { failed(http) }
432
+ http.callback {
433
+ http.response_header.status.should == 200
434
+ http.response.should match("application/x-www-form-urlencoded")
435
+ EventMachine.stop
436
+ }
437
+ }
438
+ end
439
+
440
+ it "should not override content-type when passing in ruby hash/array for body" do
441
+ EventMachine.run {
442
+ ct = 'text; charset=utf-8'
443
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
444
+ :body => {:a => :b}, :head => {'content-type' => ct}})
445
+
446
+ http.errback { failed(http) }
447
+ http.callback {
448
+ http.response_header.status.should == 200
449
+ http.content_charset.should == Encoding.find('utf-8') if defined? Encoding
450
+ http.response_header["CONTENT_TYPE"].should == ct
451
+ EventMachine.stop
452
+ }
453
+ }
454
+ end
455
+
456
+ it "should default to external encoding on invalid encoding" do
457
+ EventMachine.run {
458
+ ct = 'text/html; charset=utf-8lias'
459
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
460
+ :body => {:a => :b}, :head => {'content-type' => ct}})
461
+
462
+ http.errback { failed(http) }
463
+ http.callback {
464
+ http.response_header.status.should == 200
465
+ http.content_charset.should == Encoding.find('utf-8') if defined? Encoding
466
+ http.response_header["CONTENT_TYPE"].should == ct
467
+ EventMachine.stop
468
+ }
469
+ }
470
+ end
471
+
472
+ it "should processed escaped content-type" do
473
+ EventMachine.run {
474
+ ct = "text/html; charset=\"ISO-8859-4\""
475
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
476
+ :body => {:a => :b}, :head => {'content-type' => ct}})
477
+
478
+ http.errback { failed(http) }
479
+ http.callback {
480
+ http.response_header.status.should == 200
481
+ http.content_charset.should == Encoding.find('ISO-8859-4') if defined? Encoding
482
+ http.response_header["CONTENT_TYPE"].should == ct
483
+ EventMachine.stop
484
+ }
485
+ }
486
+ end
487
+ end
488
+
489
+ context "optional header callback" do
490
+ it "should optionally pass the response headers" do
491
+ EventMachine.run {
492
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
493
+
494
+ http.errback { failed(http) }
495
+ http.headers { |hash|
496
+ hash.should be_an_kind_of Hash
497
+ hash.should include 'CONNECTION'
498
+ hash.should include 'CONTENT_LENGTH'
499
+ }
500
+
501
+ http.callback {
502
+ http.response_header.status.should == 200
503
+ http.response.should match(/Hello/)
504
+ EventMachine.stop
505
+ }
506
+ }
507
+ end
508
+
509
+ it "should allow to terminate current connection from header callback" do
510
+ EventMachine.run {
511
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
512
+
513
+ http.callback { failed(http) }
514
+ http.headers { |hash|
515
+ hash.should be_an_kind_of Hash
516
+ hash.should include 'CONNECTION'
517
+ hash.should include 'CONTENT_LENGTH'
518
+
519
+ http.close('header callback terminated connection')
520
+ }
521
+
522
+ http.errback { |e|
523
+ http.response_header.status.should == 200
524
+ http.error.should == 'header callback terminated connection'
525
+ http.response.should == ''
526
+ EventMachine.stop
527
+ }
528
+ }
529
+ end
530
+ end
531
+
532
+ it "should optionally pass the response body progressively" do
533
+ EventMachine.run {
534
+ body = ''
535
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
536
+
537
+ http.errback { failed(http) }
538
+ http.stream { |chunk| body += chunk }
539
+
540
+ http.callback {
541
+ http.response_header.status.should == 200
542
+ http.response.should == ''
543
+ body.should match(/Hello/)
544
+ EventMachine.stop
545
+ }
546
+ }
547
+ end
548
+
549
+ it "should optionally pass the deflate-encoded response body progressively" do
550
+ EventMachine.run {
551
+ body = ''
552
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {
553
+ "accept-encoding" => "deflate, compressed"
554
+ }
555
+
556
+ http.errback { failed(http) }
557
+ http.stream { |chunk| body += chunk }
558
+
559
+ http.callback {
560
+ http.response_header.status.should == 200
561
+ http.response_header["CONTENT_ENCODING"].should == "deflate"
562
+ http.response.should == ''
563
+ body.should == "compressed"
564
+ EventMachine.stop
565
+ }
566
+ }
567
+ end
568
+
569
+ it "should accept & return cookie header to user" do
570
+ EventMachine.run {
571
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_cookie').get
572
+
573
+ http.errback { failed(http) }
574
+ http.callback {
575
+ http.response_header.status.should == 200
576
+ http.response_header.cookie.should == "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
577
+ EventMachine.stop
578
+ }
579
+ }
580
+ end
581
+
582
+ it "should return array of cookies on multiple Set-Cookie headers" do
583
+ EventMachine.run {
584
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_multiple_cookies').get
585
+
586
+ http.errback { failed(http) }
587
+ http.callback {
588
+ http.response_header.status.should == 200
589
+ http.response_header.cookie.size.should == 2
590
+ http.response_header.cookie.first.should == "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
591
+ http.response_header.cookie.last.should == "id=2;"
592
+
593
+ EventMachine.stop
594
+ }
595
+ }
596
+ end
597
+
598
+ it "should pass cookie header to server from string" do
599
+ EventMachine.run {
600
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => 'id=2;'}
601
+
602
+ http.errback { failed(http) }
603
+ http.callback {
604
+ http.response.should == "id=2;"
605
+ EventMachine.stop
606
+ }
607
+ }
608
+ end
609
+
610
+ it "should pass cookie header to server from Hash" do
611
+ EventMachine.run {
612
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => {'id' => 2}}
613
+
614
+ http.errback { failed(http) }
615
+ http.callback {
616
+ http.response.should == "id=2;"
617
+ EventMachine.stop
618
+ }
619
+ }
620
+ end
621
+
622
+ context "when talking to a stub HTTP/1.0 server" do
623
+ it "should get the body without Content-Length" do
624
+
625
+ EventMachine.run {
626
+ @s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo")
627
+
628
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
629
+ http.errback { failed(http) }
630
+ http.callback {
631
+ http.response.should match(/Foo/)
632
+ http.response_header['CONTENT_LENGTH'].should_not == 0
633
+
634
+ @s.stop
635
+ EventMachine.stop
636
+ }
637
+ }
638
+ end
639
+
640
+ it "should work with \\n instead of \\r\\n" do
641
+ EventMachine.run {
642
+ @s = StubServer.new("HTTP/1.0 200 OK\nContent-Type: text/plain\nContent-Length: 3\nConnection: close\n\nFoo")
643
+
644
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
645
+ http.errback { failed(http) }
646
+ http.callback {
647
+ http.response_header.status.should == 200
648
+ http.response_header['CONTENT_TYPE'].should == 'text/plain'
649
+ http.response.should match(/Foo/)
650
+
651
+ @s.stop
652
+ EventMachine.stop
653
+ }
654
+ }
655
+ end
656
+
657
+ it "should handle invalid HTTP response" do
658
+ EventMachine.run {
659
+ @s = StubServer.new("<html></html>")
660
+
661
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
662
+ http.callback { failed(http) }
663
+ http.errback {
664
+ http.error.should_not be_nil
665
+ EM.stop
666
+ }
667
+ }
668
+ end
669
+ end
670
+
671
+ it "should stream a file off disk" do
672
+ EventMachine.run {
673
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :file => 'spec/fixtures/google.ca'
674
+
675
+ http.errback { failed(http) }
676
+ http.callback {
677
+ http.response.should match('google')
678
+ EventMachine.stop
679
+ }
680
+ }
681
+ end
682
+
683
+ it 'should handle malformed Content-Type header repetitions' do
684
+ EventMachine.run {
685
+ response =<<-HTTP.gsub(/^ +/, '').strip
686
+ HTTP/1.0 200 OK
687
+ Content-Type: text/plain; charset=iso-8859-1
688
+ Content-Type: text/plain; charset=utf-8
689
+ Content-Length: 5
690
+ Connection: close
691
+
692
+ Hello
693
+ HTTP
694
+
695
+ @s = StubServer.new(response)
696
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
697
+ http.errback { failed(http) }
698
+ http.callback {
699
+ http.content_charset.should == Encoding::ISO_8859_1 if defined? Encoding
700
+ EventMachine.stop
701
+ }
702
+ }
703
+ end
704
+
705
+ it "should allow indifferent access to headers" do
706
+ EventMachine.run {
707
+ response =<<-HTTP.gsub(/^ +/, '').strip
708
+ HTTP/1.0 200 OK
709
+ Content-Type: text/plain; charset=utf-8
710
+ X-Custom-Header: foo
711
+ Content-Length: 5
712
+ Connection: close
713
+
714
+ Hello
715
+ HTTP
716
+
717
+ @s = StubServer.new(response)
718
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
719
+ http.errback { failed(http) }
720
+ http.callback {
721
+ http.response_header["Content-Type"].should == "text/plain; charset=utf-8"
722
+ http.response_header["CONTENT_TYPE"].should == "text/plain; charset=utf-8"
723
+
724
+ http.response_header["Content-Length"].should == "5"
725
+ http.response_header["CONTENT_LENGTH"].should == "5"
726
+
727
+ http.response_header["X-Custom-Header"].should == "foo"
728
+ http.response_header["X_CUSTOM_HEADER"].should == "foo"
729
+
730
+ EventMachine.stop
731
+ }
732
+ }
733
+ end
734
+ end