em-http-request 0.3.0 → 1.0.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

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