em-http-request 0.2.10 → 0.2.14
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.
- data/Changelog.md +36 -0
- data/LICENSE +58 -58
- data/README.md +168 -0
- data/Rakefile +9 -5
- data/VERSION +1 -1
- data/em-http-request.gemspec +12 -9
- data/examples/fetch.rb +30 -30
- data/examples/fibered-http.rb +38 -38
- data/examples/oauth-tweet.rb +49 -49
- data/examples/socks5.rb +26 -0
- data/examples/websocket-handler.rb +28 -28
- data/examples/websocket-server.rb +8 -8
- data/ext/buffer/extconf.rb +53 -53
- data/ext/http11_client/ext_help.h +14 -14
- data/ext/http11_client/extconf.rb +6 -6
- data/ext/http11_client/http11_client.c +328 -328
- data/ext/http11_client/http11_parser.c +418 -418
- data/ext/http11_client/http11_parser.h +48 -48
- data/ext/http11_client/http11_parser.rl +170 -170
- data/lib/em-http/client.rb +268 -42
- data/lib/em-http/http_options.rb +4 -2
- data/lib/em-http/mock.rb +90 -50
- data/lib/em-http/multi.rb +21 -17
- data/lib/em-http/request.rb +1 -0
- data/lib/em-http.rb +20 -19
- data/spec/encoding_spec.rb +34 -0
- data/spec/fixtures/google.ca +20 -21
- data/spec/helper.rb +3 -2
- data/spec/mock_spec.rb +79 -34
- data/spec/multi_spec.rb +27 -10
- data/spec/request_spec.rb +301 -68
- data/spec/spec.opts +7 -0
- data/spec/stallion.rb +64 -3
- data/spec/stub_server.rb +22 -22
- metadata +16 -9
- data/README.rdoc +0 -138
- data/lib/em-http/core_ext/hash.rb +0 -53
- data/spec/hash_spec.rb +0 -24
data/spec/request_spec.rb
CHANGED
|
@@ -29,16 +29,27 @@ describe EventMachine::HttpRequest do
|
|
|
29
29
|
http.errback {
|
|
30
30
|
http.response_header.status.should == 0
|
|
31
31
|
http.error.should match(/unable to resolve server address/)
|
|
32
|
+
http.uri.to_s.should match('http://somethinglocal:80/')
|
|
32
33
|
EventMachine.stop
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
end
|
|
36
37
|
|
|
37
|
-
it "should
|
|
38
|
+
it "should raise error on invalid URL" do
|
|
38
39
|
EventMachine.run {
|
|
39
40
|
lambda {
|
|
40
|
-
EventMachine::HttpRequest.new('
|
|
41
|
-
}.should raise_error
|
|
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)
|
|
42
53
|
|
|
43
54
|
EventMachine.stop
|
|
44
55
|
}
|
|
@@ -57,6 +68,64 @@ describe EventMachine::HttpRequest do
|
|
|
57
68
|
}
|
|
58
69
|
end
|
|
59
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 }
|
|
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 }
|
|
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 follow redirects on HEAD method" do
|
|
104
|
+
EventMachine.run {
|
|
105
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/head').head :redirects => 1
|
|
106
|
+
http.errback { failed }
|
|
107
|
+
http.callback {
|
|
108
|
+
http.response_header.status.should == 200
|
|
109
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
|
110
|
+
EM.stop
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "should follow redirects on HEAD method (external)" do
|
|
116
|
+
|
|
117
|
+
EventMachine.run {
|
|
118
|
+
http = EventMachine::HttpRequest.new('http://www.google.com/').head :redirects => 1
|
|
119
|
+
http.errback { failed }
|
|
120
|
+
http.callback {
|
|
121
|
+
http.response_header.status.should == 200
|
|
122
|
+
EM.stop
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
60
129
|
it "should perform successfull GET with a URI passed as argument" do
|
|
61
130
|
EventMachine.run {
|
|
62
131
|
uri = URI.parse('http://127.0.0.1:8080/')
|
|
@@ -178,6 +247,19 @@ describe EventMachine::HttpRequest do
|
|
|
178
247
|
}
|
|
179
248
|
end
|
|
180
249
|
|
|
250
|
+
it "should escape body on POST" do
|
|
251
|
+
EventMachine.run {
|
|
252
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {:stuff => 'string&string'}
|
|
253
|
+
|
|
254
|
+
http.errback { failed }
|
|
255
|
+
http.callback {
|
|
256
|
+
http.response_header.status.should == 200
|
|
257
|
+
http.response.should == "stuff=string%26string"
|
|
258
|
+
EventMachine.stop
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
|
|
181
263
|
it "should perform successfull POST with Ruby Hash/Array as params" do
|
|
182
264
|
EventMachine.run {
|
|
183
265
|
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => {"key1" => 1, "key2" => [2,3]}
|
|
@@ -222,7 +304,7 @@ describe EventMachine::HttpRequest do
|
|
|
222
304
|
EventMachine.run {
|
|
223
305
|
|
|
224
306
|
# digg.com uses chunked encoding
|
|
225
|
-
http = EventMachine::HttpRequest.new('http://digg.com/').get
|
|
307
|
+
http = EventMachine::HttpRequest.new('http://digg.com/news').get
|
|
226
308
|
|
|
227
309
|
http.errback { failed }
|
|
228
310
|
http.callback {
|
|
@@ -272,6 +354,20 @@ describe EventMachine::HttpRequest do
|
|
|
272
354
|
}
|
|
273
355
|
end
|
|
274
356
|
|
|
357
|
+
it "should return ETag and Last-Modified headers" do
|
|
358
|
+
EventMachine.run {
|
|
359
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_query').get
|
|
360
|
+
|
|
361
|
+
http.errback { failed }
|
|
362
|
+
http.callback {
|
|
363
|
+
http.response_header.status.should == 200
|
|
364
|
+
http.response_header.etag.should match('abcdefg')
|
|
365
|
+
http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
|
|
366
|
+
EventMachine.stop
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
end
|
|
370
|
+
|
|
275
371
|
it "should detect deflate encoding" do
|
|
276
372
|
EventMachine.run {
|
|
277
373
|
|
|
@@ -318,58 +414,94 @@ describe EventMachine::HttpRequest do
|
|
|
318
414
|
}
|
|
319
415
|
end
|
|
320
416
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
http.
|
|
327
|
-
|
|
417
|
+
context "redirect" do
|
|
418
|
+
it "should report last_effective_url" do
|
|
419
|
+
EventMachine.run {
|
|
420
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
421
|
+
http.errback { failed }
|
|
422
|
+
http.callback {
|
|
423
|
+
http.response_header.status.should == 200
|
|
424
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
|
328
425
|
|
|
329
|
-
|
|
426
|
+
EM.stop
|
|
427
|
+
}
|
|
330
428
|
}
|
|
331
|
-
|
|
332
|
-
end
|
|
429
|
+
end
|
|
333
430
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
431
|
+
it "should follow location redirects" do
|
|
432
|
+
EventMachine.run {
|
|
433
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get :redirects => 1
|
|
434
|
+
http.errback { failed }
|
|
435
|
+
http.callback {
|
|
436
|
+
http.response_header.status.should == 200
|
|
437
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
|
438
|
+
http.response.should == "compressed"
|
|
439
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
440
|
+
http.redirects.should == 1
|
|
344
441
|
|
|
345
|
-
|
|
442
|
+
EM.stop
|
|
443
|
+
}
|
|
346
444
|
}
|
|
347
|
-
|
|
348
|
-
end
|
|
445
|
+
end
|
|
349
446
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
447
|
+
it "should default to 0 redirects" do
|
|
448
|
+
EventMachine.run {
|
|
449
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get
|
|
450
|
+
http.errback { failed }
|
|
451
|
+
http.callback {
|
|
452
|
+
http.response_header.status.should == 301
|
|
453
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
|
454
|
+
http.redirects.should == 0
|
|
358
455
|
|
|
359
|
-
|
|
456
|
+
EM.stop
|
|
457
|
+
}
|
|
360
458
|
}
|
|
361
|
-
|
|
362
|
-
end
|
|
459
|
+
end
|
|
363
460
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
461
|
+
it "should not invoke redirect logic on failed connections" do
|
|
462
|
+
EventMachine.run {
|
|
463
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get :timeout => 0.1, :redirects => 5
|
|
464
|
+
http.callback { failed }
|
|
465
|
+
http.errback {
|
|
466
|
+
http.redirects.should == 0
|
|
467
|
+
EM.stop
|
|
468
|
+
}
|
|
371
469
|
}
|
|
372
|
-
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
it "should normalize redirect urls" do
|
|
473
|
+
EventMachine.run {
|
|
474
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/bad').get :redirects => 1
|
|
475
|
+
http.errback { failed }
|
|
476
|
+
http.callback {
|
|
477
|
+
http.last_effective_url.to_s.should match('http://127.0.0.1:8080/')
|
|
478
|
+
http.response.should match('Hello, World!')
|
|
479
|
+
EM.stop
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
it "should fail gracefully on a missing host in absolute Location header" do
|
|
485
|
+
EventMachine.run {
|
|
486
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/nohost').get :redirects => 1
|
|
487
|
+
http.callback { failed }
|
|
488
|
+
http.errback {
|
|
489
|
+
http.error.should == 'Location header format error'
|
|
490
|
+
EM.stop
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
it "should fail gracefully on an invalid host in Location header" do
|
|
496
|
+
EventMachine.run {
|
|
497
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/badhost').get :redirects => 1
|
|
498
|
+
http.callback { failed }
|
|
499
|
+
http.errback {
|
|
500
|
+
http.error.should == 'unable to resolve server address'
|
|
501
|
+
EM.stop
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
end
|
|
373
505
|
end
|
|
374
506
|
|
|
375
507
|
it "should optionally pass the response body progressively" do
|
|
@@ -389,6 +521,49 @@ describe EventMachine::HttpRequest do
|
|
|
389
521
|
}
|
|
390
522
|
end
|
|
391
523
|
|
|
524
|
+
context "optional header callback" do
|
|
525
|
+
it "should optionally pass the response headers" do
|
|
526
|
+
EventMachine.run {
|
|
527
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
528
|
+
|
|
529
|
+
http.errback { failed }
|
|
530
|
+
http.headers { |hash|
|
|
531
|
+
hash.should be_an_kind_of Hash
|
|
532
|
+
hash.should include 'CONNECTION'
|
|
533
|
+
hash.should include 'CONTENT_LENGTH'
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
http.callback {
|
|
537
|
+
http.response_header.status.should == 200
|
|
538
|
+
http.response.should match(/Hello/)
|
|
539
|
+
EventMachine.stop
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
it "should allow to terminate current connection from header callback" do
|
|
545
|
+
EventMachine.run {
|
|
546
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
|
547
|
+
|
|
548
|
+
http.callback { failed }
|
|
549
|
+
http.headers { |hash|
|
|
550
|
+
hash.should be_an_kind_of Hash
|
|
551
|
+
hash.should include 'CONNECTION'
|
|
552
|
+
hash.should include 'CONTENT_LENGTH'
|
|
553
|
+
|
|
554
|
+
http.close('header callback terminated connection')
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
http.errback { |e|
|
|
558
|
+
http.response_header.status.should == 200
|
|
559
|
+
http.error.should == 'header callback terminated connection'
|
|
560
|
+
http.response.should == ''
|
|
561
|
+
EventMachine.stop
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
392
567
|
it "should optionally pass the deflate-encoded response body progressively" do
|
|
393
568
|
EventMachine.run {
|
|
394
569
|
body = ''
|
|
@@ -582,8 +757,8 @@ describe EventMachine::HttpRequest do
|
|
|
582
757
|
http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
|
|
583
758
|
http.errback { fail }
|
|
584
759
|
http.callback {
|
|
585
|
-
c1 = "PREF=ID=
|
|
586
|
-
c2 = "NID=
|
|
760
|
+
c1 = "PREF=ID=11955ae9690fd292:TM=1281823106:LM=1281823106:S=wHdloFqGQ_OLSE92; expires=Mon, 13-Aug-2012 21:58:26 GMT; path=/; domain=.google.ca"
|
|
761
|
+
c2 = "NID=37=USTdOsxOSMbLjphkJ3S5Ueua3Yc23COXuK_pbztcHx7JoyhomwQySrvebCf3_u8eyrBiLWssVzaZcEOiKGEJbNdy8lRhnq_mfrdz693LaMjNPh__ccW4sgn1ZO6nQltE; expires=Sun, 13-Feb-2011 21:58:26 GMT; path=/; domain=.google.ca; HttpOnly"
|
|
587
762
|
http.response_header.cookie.should == [c1, c2]
|
|
588
763
|
|
|
589
764
|
EventMachine.stop
|
|
@@ -593,35 +768,93 @@ describe EventMachine::HttpRequest do
|
|
|
593
768
|
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
|
594
769
|
end
|
|
595
770
|
|
|
596
|
-
context "connections via
|
|
771
|
+
context "connections via" do
|
|
772
|
+
context "direct proxy" do
|
|
773
|
+
it "should default to skip CONNECT" do
|
|
774
|
+
EventMachine.run {
|
|
597
775
|
|
|
598
|
-
|
|
599
|
-
|
|
776
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
|
777
|
+
:host => '127.0.0.1', :port => 8083
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
http.errback { p http.inspect; failed }
|
|
781
|
+
http.callback {
|
|
782
|
+
http.response_header.status.should == 200
|
|
783
|
+
http.response.should match('test')
|
|
784
|
+
EventMachine.stop
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
end
|
|
600
788
|
|
|
601
|
-
|
|
789
|
+
it "should send absolute URIs to the proxy server" do
|
|
790
|
+
EventMachine.run {
|
|
602
791
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
792
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
|
793
|
+
:host => '127.0.0.1', :port => 8083
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
http.errback { p http.inspect; failed }
|
|
797
|
+
http.callback {
|
|
798
|
+
http.response_header.status.should == 200
|
|
799
|
+
# The test proxy server gives the requested uri back in this header
|
|
800
|
+
http.response_header['X_THE_REQUESTED_URI'].should == 'http://127.0.0.1:8080/?q=test'
|
|
801
|
+
http.response_header['X_THE_REQUESTED_URI'].should_not == '/?q=test'
|
|
802
|
+
http.response.should match('test')
|
|
803
|
+
EventMachine.stop
|
|
804
|
+
}
|
|
608
805
|
}
|
|
609
|
-
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
it "should include query parameters specified in the options" do
|
|
809
|
+
EventMachine.run {
|
|
810
|
+
|
|
811
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(
|
|
812
|
+
:proxy => { :host => '127.0.0.1', :port => 8083 },
|
|
813
|
+
:query => { 'q' => 'test' }
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
http.errback { p http.inspect; failed }
|
|
817
|
+
http.callback {
|
|
818
|
+
http.response_header.status.should == 200
|
|
819
|
+
http.response.should match('test')
|
|
820
|
+
EventMachine.stop
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
end
|
|
610
824
|
end
|
|
611
825
|
|
|
612
|
-
|
|
613
|
-
|
|
826
|
+
context "CONNECT proxy" do
|
|
827
|
+
it "should work with CONNECT proxy servers" do
|
|
828
|
+
EventMachine.run {
|
|
614
829
|
|
|
615
|
-
|
|
616
|
-
|
|
830
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get({
|
|
831
|
+
:proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
|
832
|
+
})
|
|
617
833
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
834
|
+
http.errback { p http.inspect; failed }
|
|
835
|
+
http.callback {
|
|
836
|
+
http.response_header.status.should == 200
|
|
837
|
+
http.response.should == 'Hello, World!'
|
|
838
|
+
EventMachine.stop
|
|
839
|
+
}
|
|
623
840
|
}
|
|
624
|
-
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
it "should proxy POST data" do
|
|
844
|
+
EventMachine.run {
|
|
845
|
+
|
|
846
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
|
|
847
|
+
:body => "data", :proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
|
848
|
+
})
|
|
849
|
+
|
|
850
|
+
http.errback { failed }
|
|
851
|
+
http.callback {
|
|
852
|
+
http.response_header.status.should == 200
|
|
853
|
+
http.response.should match(/data/)
|
|
854
|
+
EventMachine.stop
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
end
|
|
625
858
|
end
|
|
626
859
|
end
|
|
627
860
|
|
data/spec/spec.opts
ADDED
data/spec/stallion.rb
CHANGED
|
@@ -78,12 +78,14 @@ Stallion.saddle :spec do |stable|
|
|
|
78
78
|
stable.response.write 'test'
|
|
79
79
|
|
|
80
80
|
elsif stable.request.path_info == '/echo_query'
|
|
81
|
+
stable.response["ETag"] = "abcdefg"
|
|
82
|
+
stable.response["Last-Modified"] = "Fri, 13 Aug 2010 17:31:21 GMT"
|
|
81
83
|
stable.response.write stable.request.query_string
|
|
82
84
|
|
|
83
85
|
elsif stable.request.path_info == '/echo_content_length'
|
|
84
86
|
stable.response.write stable.request.content_length
|
|
85
87
|
|
|
86
|
-
elsif stable.request.head?
|
|
88
|
+
elsif stable.request.head? && stable.request.path_info == '/'
|
|
87
89
|
stable.response.status = 200
|
|
88
90
|
|
|
89
91
|
elsif stable.request.delete?
|
|
@@ -113,6 +115,23 @@ Stallion.saddle :spec do |stable|
|
|
|
113
115
|
elsif stable.request.path_info == '/redirect'
|
|
114
116
|
stable.response.status = 301
|
|
115
117
|
stable.response["Location"] = "/gzip"
|
|
118
|
+
stable.response.write 'redirect'
|
|
119
|
+
|
|
120
|
+
elsif stable.request.path_info == '/redirect/bad'
|
|
121
|
+
stable.response.status = 301
|
|
122
|
+
stable.response["Location"] = "http://127.0.0.1:8080"
|
|
123
|
+
|
|
124
|
+
elsif stable.request.path_info == '/redirect/head'
|
|
125
|
+
stable.response.status = 301
|
|
126
|
+
stable.response["Location"] = "/"
|
|
127
|
+
|
|
128
|
+
elsif stable.request.path_info == '/redirect/nohost'
|
|
129
|
+
stable.response.status = 301
|
|
130
|
+
stable.response["Location"] = "http:/"
|
|
131
|
+
|
|
132
|
+
elsif stable.request.path_info == '/redirect/badhost'
|
|
133
|
+
stable.response.status = 301
|
|
134
|
+
stable.response["Location"] = "http://$$$@$!%&^"
|
|
116
135
|
|
|
117
136
|
elsif stable.request.path_info == '/gzip'
|
|
118
137
|
io = StringIO.new
|
|
@@ -164,7 +183,7 @@ Thread.new do
|
|
|
164
183
|
end
|
|
165
184
|
|
|
166
185
|
#
|
|
167
|
-
# HTTP Proxy server
|
|
186
|
+
# Tunneling HTTP Proxy server
|
|
168
187
|
#
|
|
169
188
|
Thread.new do
|
|
170
189
|
server = TCPServer.new('127.0.0.1', 8082)
|
|
@@ -220,4 +239,46 @@ Thread.new do
|
|
|
220
239
|
end
|
|
221
240
|
end
|
|
222
241
|
|
|
223
|
-
|
|
242
|
+
#
|
|
243
|
+
# CONNECT-less HTTP Proxy server
|
|
244
|
+
#
|
|
245
|
+
Thread.new do
|
|
246
|
+
server = TCPServer.new('127.0.0.1', 8083)
|
|
247
|
+
loop do
|
|
248
|
+
session = server.accept
|
|
249
|
+
request = ""
|
|
250
|
+
while (data = session.gets) != "\r\n"
|
|
251
|
+
request << data
|
|
252
|
+
end
|
|
253
|
+
parts = request.split("\r\n")
|
|
254
|
+
method, destination, http_version = parts.first.split(' ')
|
|
255
|
+
if destination =~ /^http:/
|
|
256
|
+
uri = Addressable::URI.parse(destination)
|
|
257
|
+
absolute_path = uri.path + (uri.query ? "?#{uri.query}" : "")
|
|
258
|
+
client = TCPSocket.open(uri.host, uri.port || 80)
|
|
259
|
+
client.write "#{method} #{absolute_path} #{http_version}\r\n"
|
|
260
|
+
parts[1..-1].each do |part|
|
|
261
|
+
client.write "#{part}\r\n"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
client.write "\r\n"
|
|
265
|
+
client.flush
|
|
266
|
+
client.close_write
|
|
267
|
+
|
|
268
|
+
# Take the initial line from the upstream response
|
|
269
|
+
session.write client.gets
|
|
270
|
+
|
|
271
|
+
# What (absolute) uri was requested? Send it back in a header
|
|
272
|
+
session.write "X-The-Requested-URI: #{destination}\r\n"
|
|
273
|
+
|
|
274
|
+
while data = client.gets
|
|
275
|
+
session.write data
|
|
276
|
+
end
|
|
277
|
+
session.flush
|
|
278
|
+
client.close
|
|
279
|
+
end
|
|
280
|
+
session.close
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
sleep(1)
|
data/spec/stub_server.rb
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
class StubServer
|
|
2
|
-
module Server
|
|
3
|
-
def receive_data(data)
|
|
4
|
-
send_data @response
|
|
5
|
-
close_connection_after_writing
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def response=(response)
|
|
9
|
-
@response = response
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def initialize(response, port=8081)
|
|
14
|
-
@sig = EventMachine::start_server("127.0.0.1", port, Server) { |s|
|
|
15
|
-
s.response = response
|
|
16
|
-
}
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def stop
|
|
20
|
-
EventMachine.stop_server @sig
|
|
21
|
-
end
|
|
22
|
-
end
|
|
1
|
+
class StubServer
|
|
2
|
+
module Server
|
|
3
|
+
def receive_data(data)
|
|
4
|
+
send_data @response
|
|
5
|
+
close_connection_after_writing
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def response=(response)
|
|
9
|
+
@response = response
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(response, port=8081)
|
|
14
|
+
@sig = EventMachine::start_server("127.0.0.1", port, Server) { |s|
|
|
15
|
+
s.response = response
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stop
|
|
20
|
+
EventMachine.stop_server @sig
|
|
21
|
+
end
|
|
22
|
+
end
|