em-http-request 1.0.1 → 1.0.2

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.

data/spec/client_spec.rb CHANGED
@@ -1,734 +1,763 @@
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
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/authtest').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 perform basic auth via the URL" do
272
+ EventMachine.run {
273
+
274
+ http = EventMachine::HttpRequest.new('http://user:pass@127.0.0.1:8090/authtest').get
275
+
276
+ http.errback { failed(http) }
277
+ http.callback {
278
+ http.response_header.status.should == 200
279
+ EventMachine.stop
280
+ }
281
+ }
282
+ end
283
+
284
+ it "should return peer's IP address" do
285
+ EventMachine.run {
286
+
287
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
288
+ conn.peer.should be_nil
289
+
290
+ http = conn.get
291
+ http.peer.should be_nil
292
+
293
+ http.errback { failed(http) }
294
+ http.callback {
295
+ conn.peer.should == '127.0.0.1'
296
+ http.peer.should == '127.0.0.1'
297
+
298
+ EventMachine.stop
299
+ }
300
+ }
301
+ end
302
+
303
+ it "should remove all newlines from long basic auth header" do
304
+ EventMachine.run {
305
+ auth = {'authorization' => ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz']}
306
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => auth
307
+ http.errback { failed(http) }
308
+ http.callback {
309
+ http.response_header.status.should == 200
310
+ http.response.should == "Basic YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhOnp6enp6enp6enp6enp6enp6enp6enp6enp6enp6eg=="
311
+ EventMachine.stop
312
+ }
313
+ }
314
+ end
315
+
316
+ it "should send proper OAuth auth header" do
317
+ EventMachine.run {
318
+ oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
319
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/auth').get :head => {
320
+ 'authorization' => oauth_header
321
+ }
322
+
323
+ http.errback { failed(http) }
324
+ http.callback {
325
+ http.response_header.status.should == 200
326
+ http.response.should == oauth_header
327
+ EventMachine.stop
328
+ }
329
+ }
330
+ end
331
+
332
+ it "should return ETag and Last-Modified headers" do
333
+ EventMachine.run {
334
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_query').get
335
+
336
+ http.errback { failed(http) }
337
+ http.callback {
338
+ http.response_header.status.should == 200
339
+ http.response_header.etag.should match('abcdefg')
340
+ http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
341
+ EventMachine.stop
342
+ }
343
+ }
344
+ end
345
+
346
+ it "should detect deflate encoding" do
347
+ EventMachine.run {
348
+
349
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {"accept-encoding" => "deflate"}
350
+
351
+ http.errback { failed(http) }
352
+ http.callback {
353
+ http.response_header.status.should == 200
354
+ http.response_header["CONTENT_ENCODING"].should == "deflate"
355
+ http.response.should == "compressed"
356
+
357
+ EventMachine.stop
358
+ }
359
+ }
360
+ end
361
+
362
+ it "should detect gzip encoding" do
363
+ EventMachine.run {
364
+
365
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
366
+ "accept-encoding" => "gzip, compressed"
367
+ }
368
+
369
+ http.errback { failed(http) }
370
+ http.callback {
371
+ http.response_header.status.should == 200
372
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
373
+ http.response.should == "compressed"
374
+
375
+ EventMachine.stop
376
+ }
377
+ }
378
+ end
379
+
380
+ it "should not decode the response when configured so" do
381
+ EventMachine.run {
382
+
383
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
384
+ "accept-encoding" => "gzip, compressed"
385
+ }, :decoding => false
386
+
387
+ http.errback { failed(http) }
388
+ http.callback {
389
+ http.response_header.status.should == 200
390
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
391
+
392
+ raw = http.response
393
+ Zlib::GzipReader.new(StringIO.new(raw)).read.should == "compressed"
394
+
395
+ EventMachine.stop
396
+ }
397
+ }
398
+ end
399
+
400
+ it "should timeout after 0.1 seconds of inactivity" do
401
+ EventMachine.run {
402
+ t = Time.now.to_i
403
+ EventMachine.heartbeat_interval = 0.1
404
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/timeout', :inactivity_timeout => 0.1).get
405
+
406
+ http.errback {
407
+ (Time.now.to_i - t).should <= 1
408
+ EventMachine.stop
409
+ }
410
+ http.callback { failed(http) }
411
+ }
412
+ end
413
+
414
+ it "should complete a Location: with a relative path" do
415
+ EventMachine.run {
416
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/relative-location').get
417
+
418
+ http.errback { failed(http) }
419
+ http.callback {
420
+ http.response_header['LOCATION'].should == 'http://127.0.0.1:8090/forwarded'
421
+ EventMachine.stop
422
+ }
423
+ }
424
+ end
425
+
426
+ context "body content-type encoding" do
427
+ it "should not set content type on string in body" do
428
+ EventMachine.run {
429
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => "data"
430
+
431
+ http.errback { failed(http) }
432
+ http.callback {
433
+ http.response_header.status.should == 200
434
+ http.response.should be_empty
435
+ EventMachine.stop
436
+ }
437
+ }
438
+ end
439
+
440
+ it "should set content-type automatically when passed a ruby hash/array for body" do
441
+ EventMachine.run {
442
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post :body => {:a => :b}
443
+
444
+ http.errback { failed(http) }
445
+ http.callback {
446
+ http.response_header.status.should == 200
447
+ http.response.should match("application/x-www-form-urlencoded")
448
+ EventMachine.stop
449
+ }
450
+ }
451
+ end
452
+
453
+ it "should not override content-type when passing in ruby hash/array for body" do
454
+ EventMachine.run {
455
+ ct = 'text; charset=utf-8'
456
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
457
+ :body => {:a => :b}, :head => {'content-type' => ct}})
458
+
459
+ http.errback { failed(http) }
460
+ http.callback {
461
+ http.response_header.status.should == 200
462
+ http.content_charset.should == Encoding.find('utf-8') if defined? Encoding
463
+ http.response_header["CONTENT_TYPE"].should == ct
464
+ EventMachine.stop
465
+ }
466
+ }
467
+ end
468
+
469
+ it "should default to external encoding on invalid encoding" do
470
+ EventMachine.run {
471
+ ct = 'text/html; charset=utf-8lias'
472
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
473
+ :body => {:a => :b}, :head => {'content-type' => ct}})
474
+
475
+ http.errback { failed(http) }
476
+ http.callback {
477
+ http.response_header.status.should == 200
478
+ http.content_charset.should == Encoding.find('utf-8') if defined? Encoding
479
+ http.response_header["CONTENT_TYPE"].should == ct
480
+ EventMachine.stop
481
+ }
482
+ }
483
+ end
484
+
485
+ it "should processed escaped content-type" do
486
+ EventMachine.run {
487
+ ct = "text/html; charset=\"ISO-8859-4\""
488
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_content_type').post({
489
+ :body => {:a => :b}, :head => {'content-type' => ct}})
490
+
491
+ http.errback { failed(http) }
492
+ http.callback {
493
+ http.response_header.status.should == 200
494
+ http.content_charset.should == Encoding.find('ISO-8859-4') if defined? Encoding
495
+ http.response_header["CONTENT_TYPE"].should == ct
496
+ EventMachine.stop
497
+ }
498
+ }
499
+ end
500
+ end
501
+
502
+ context "optional header callback" do
503
+ it "should optionally pass the response headers" do
504
+ EventMachine.run {
505
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
506
+
507
+ http.errback { failed(http) }
508
+ http.headers { |hash|
509
+ hash.should be_an_kind_of Hash
510
+ hash.should include 'CONNECTION'
511
+ hash.should include 'CONTENT_LENGTH'
512
+ }
513
+
514
+ http.callback {
515
+ http.response_header.status.should == 200
516
+ http.response.should match(/Hello/)
517
+ EventMachine.stop
518
+ }
519
+ }
520
+ end
521
+
522
+ it "should allow to terminate current connection from header callback" do
523
+ EventMachine.run {
524
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
525
+
526
+ http.callback { failed(http) }
527
+ http.headers { |hash|
528
+ hash.should be_an_kind_of Hash
529
+ hash.should include 'CONNECTION'
530
+ hash.should include 'CONTENT_LENGTH'
531
+
532
+ http.close('header callback terminated connection')
533
+ }
534
+
535
+ http.errback { |e|
536
+ http.response_header.status.should == 200
537
+ http.error.should == 'header callback terminated connection'
538
+ http.response.should == ''
539
+ EventMachine.stop
540
+ }
541
+ }
542
+ end
543
+ end
544
+
545
+ it "should optionally pass the response body progressively" do
546
+ EventMachine.run {
547
+ body = ''
548
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
549
+
550
+ http.errback { failed(http) }
551
+ http.stream { |chunk| body += chunk }
552
+
553
+ http.callback {
554
+ http.response_header.status.should == 200
555
+ http.response.should == ''
556
+ body.should match(/Hello/)
557
+ EventMachine.stop
558
+ }
559
+ }
560
+ end
561
+
562
+ it "should optionally pass the deflate-encoded response body progressively" do
563
+ EventMachine.run {
564
+ body = ''
565
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/deflate').get :head => {
566
+ "accept-encoding" => "deflate, compressed"
567
+ }
568
+
569
+ http.errback { failed(http) }
570
+ http.stream { |chunk| body += chunk }
571
+
572
+ http.callback {
573
+ http.response_header.status.should == 200
574
+ http.response_header["CONTENT_ENCODING"].should == "deflate"
575
+ http.response.should == ''
576
+ body.should == "compressed"
577
+ EventMachine.stop
578
+ }
579
+ }
580
+ end
581
+
582
+ it "should accept & return cookie header to user" do
583
+ EventMachine.run {
584
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_cookie').get
585
+
586
+ http.errback { failed(http) }
587
+ http.callback {
588
+ http.response_header.status.should == 200
589
+ http.response_header.cookie.should == "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
590
+ EventMachine.stop
591
+ }
592
+ }
593
+ end
594
+
595
+ it "should return array of cookies on multiple Set-Cookie headers" do
596
+ EventMachine.run {
597
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/set_multiple_cookies').get
598
+
599
+ http.errback { failed(http) }
600
+ http.callback {
601
+ http.response_header.status.should == 200
602
+ http.response_header.cookie.size.should == 2
603
+ http.response_header.cookie.first.should == "id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;"
604
+ http.response_header.cookie.last.should == "id=2;"
605
+
606
+ EventMachine.stop
607
+ }
608
+ }
609
+ end
610
+
611
+ it "should pass cookie header to server from string" do
612
+ EventMachine.run {
613
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => 'id=2;'}
614
+
615
+ http.errback { failed(http) }
616
+ http.callback {
617
+ http.response.should == "id=2;"
618
+ EventMachine.stop
619
+ }
620
+ }
621
+ end
622
+
623
+ it "should pass cookie header to server from Hash" do
624
+ EventMachine.run {
625
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/echo_cookie').get :head => {'cookie' => {'id' => 2}}
626
+
627
+ http.errback { failed(http) }
628
+ http.callback {
629
+ http.response.should == "id=2;"
630
+ EventMachine.stop
631
+ }
632
+ }
633
+ end
634
+
635
+ it "should get the body without Content-Length" do
636
+ EventMachine.run {
637
+ @s = StubServer.new("HTTP/1.1 200 OK\r\n\r\nFoo")
638
+
639
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
640
+ http.errback { failed(http) }
641
+ http.callback {
642
+ http.response.should match(/Foo/)
643
+ http.response_header['CONTENT_LENGTH'].should be_nil
644
+
645
+ @s.stop
646
+ EventMachine.stop
647
+ }
648
+ }
649
+ end
650
+
651
+ context "when talking to a stub HTTP/1.0 server" do
652
+ it "should get the body without Content-Length" do
653
+
654
+ EventMachine.run {
655
+ @s = StubServer.new("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nFoo")
656
+
657
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
658
+ http.errback { failed(http) }
659
+ http.callback {
660
+ http.response.should match(/Foo/)
661
+ http.response_header['CONTENT_LENGTH'].should be_nil
662
+
663
+ @s.stop
664
+ EventMachine.stop
665
+ }
666
+ }
667
+ end
668
+
669
+ it "should work with \\n instead of \\r\\n" do
670
+ EventMachine.run {
671
+ @s = StubServer.new("HTTP/1.0 200 OK\nContent-Type: text/plain\nContent-Length: 3\nConnection: close\n\nFoo")
672
+
673
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
674
+ http.errback { failed(http) }
675
+ http.callback {
676
+ http.response_header.status.should == 200
677
+ http.response_header['CONTENT_TYPE'].should == 'text/plain'
678
+ http.response.should match(/Foo/)
679
+
680
+ @s.stop
681
+ EventMachine.stop
682
+ }
683
+ }
684
+ end
685
+
686
+ it "should handle invalid HTTP response" do
687
+ EventMachine.run {
688
+ @s = StubServer.new("<html></html>")
689
+
690
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
691
+ http.callback { failed(http) }
692
+ http.errback {
693
+ http.error.should_not be_nil
694
+ EM.stop
695
+ }
696
+ }
697
+ end
698
+ end
699
+
700
+ it "should stream a file off disk" do
701
+ EventMachine.run {
702
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').post :file => 'spec/fixtures/google.ca'
703
+
704
+ http.errback { failed(http) }
705
+ http.callback {
706
+ http.response.should match('google')
707
+ EventMachine.stop
708
+ }
709
+ }
710
+ end
711
+
712
+ it 'should handle malformed Content-Type header repetitions' do
713
+ EventMachine.run {
714
+ response =<<-HTTP.gsub(/^ +/, '').strip
715
+ HTTP/1.0 200 OK
716
+ Content-Type: text/plain; charset=iso-8859-1
717
+ Content-Type: text/plain; charset=utf-8
718
+ Content-Length: 5
719
+ Connection: close
720
+
721
+ Hello
722
+ HTTP
723
+
724
+ @s = StubServer.new(response)
725
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
726
+ http.errback { failed(http) }
727
+ http.callback {
728
+ http.content_charset.should == Encoding::ISO_8859_1 if defined? Encoding
729
+ EventMachine.stop
730
+ }
731
+ }
732
+ end
733
+
734
+ it "should allow indifferent access to headers" do
735
+ EventMachine.run {
736
+ response =<<-HTTP.gsub(/^ +/, '').strip
737
+ HTTP/1.0 200 OK
738
+ Content-Type: text/plain; charset=utf-8
739
+ X-Custom-Header: foo
740
+ Content-Length: 5
741
+ Connection: close
742
+
743
+ Hello
744
+ HTTP
745
+
746
+ @s = StubServer.new(response)
747
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get
748
+ http.errback { failed(http) }
749
+ http.callback {
750
+ http.response_header["Content-Type"].should == "text/plain; charset=utf-8"
751
+ http.response_header["CONTENT_TYPE"].should == "text/plain; charset=utf-8"
752
+
753
+ http.response_header["Content-Length"].should == "5"
754
+ http.response_header["CONTENT_LENGTH"].should == "5"
755
+
756
+ http.response_header["X-Custom-Header"].should == "foo"
757
+ http.response_header["X_CUSTOM_HEADER"].should == "foo"
758
+
759
+ EventMachine.stop
760
+ }
761
+ }
762
+ end
763
+ end