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

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