em-http-request 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.

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