em-http-request 0.2.10 → 0.2.11
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.
- data/LICENSE +58 -58
- data/README.md +157 -0
- data/Rakefile +110 -106
- data/VERSION +1 -1
- data/em-http-request.gemspec +5 -4
- data/examples/fetch.rb +30 -30
- data/examples/fibered-http.rb +38 -38
- data/examples/oauth-tweet.rb +49 -49
- 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-request.rb +1 -1
- data/lib/em-http.rb +2 -1
- data/lib/em-http/client.rb +58 -26
- data/lib/em-http/core_ext/bytesize.rb +5 -5
- data/lib/em-http/core_ext/hash.rb +53 -53
- data/lib/em-http/decoders.rb +122 -122
- data/lib/em-http/http_options.rb +33 -31
- 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/spec/fixtures/google.ca +20 -21
- data/spec/hash_spec.rb +24 -24
- 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 +198 -65
- data/spec/spec.opts +7 -0
- data/spec/stallion.rb +59 -2
- data/spec/stub_server.rb +22 -22
- metadata +6 -5
- data/README.rdoc +0 -138
data/lib/em-http/multi.rb
CHANGED
@@ -1,51 +1,55 @@
|
|
1
1
|
module EventMachine
|
2
2
|
|
3
|
-
# EventMachine based Multi request client, based on a streaming HTTPRequest class,
|
3
|
+
# EventMachine based Multi request client, based on a streaming HTTPRequest class,
|
4
4
|
# which allows you to open multiple parallel connections and return only when all
|
5
5
|
# of them finish. (i.e. ideal for parallelizing workloads)
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# == Example
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# EventMachine.run {
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# multi = EventMachine::MultiRequest.new
|
12
|
-
#
|
12
|
+
#
|
13
13
|
# # add multiple requests to the multi-handler
|
14
14
|
# multi.add(EventMachine::HttpRequest.new('http://www.google.com/').get)
|
15
15
|
# multi.add(EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# multi.callback {
|
18
18
|
# p multi.responses[:succeeded]
|
19
19
|
# p multi.responses[:failed]
|
20
|
-
#
|
20
|
+
#
|
21
21
|
# EventMachine.stop
|
22
22
|
# }
|
23
23
|
# }
|
24
|
-
#
|
25
|
-
|
24
|
+
#
|
25
|
+
|
26
26
|
class MultiRequest
|
27
27
|
include EventMachine::Deferrable
|
28
28
|
|
29
29
|
attr_reader :requests, :responses
|
30
|
-
|
31
|
-
def initialize
|
32
|
-
@requests
|
30
|
+
|
31
|
+
def initialize(conns=[], &block)
|
32
|
+
@requests = []
|
33
33
|
@responses = {:succeeded => [], :failed => []}
|
34
|
+
|
35
|
+
conns.each {|conn| add(conn)}
|
36
|
+
callback(&block) if block_given?
|
34
37
|
end
|
35
|
-
|
38
|
+
|
36
39
|
def add(conn)
|
37
40
|
@requests.push(conn)
|
38
41
|
|
39
42
|
conn.callback { @responses[:succeeded].push(conn); check_progress }
|
40
43
|
conn.errback { @responses[:failed].push(conn); check_progress }
|
41
44
|
end
|
42
|
-
|
45
|
+
|
43
46
|
protected
|
44
|
-
|
47
|
+
|
45
48
|
# invoke callback if all requests have completed
|
46
49
|
def check_progress
|
47
|
-
succeed if (@responses[:succeeded].size +
|
50
|
+
succeed(self) if (@responses[:succeeded].size +
|
51
|
+
@responses[:failed].size) == @requests.size
|
48
52
|
end
|
49
|
-
|
53
|
+
|
50
54
|
end
|
51
55
|
end
|
data/lib/em-http/request.rb
CHANGED
data/spec/fixtures/google.ca
CHANGED
@@ -1,21 +1,20 @@
|
|
1
|
-
HTTP/1.
|
2
|
-
Date:
|
3
|
-
Expires: -1
|
4
|
-
Cache-Control: private, max-age=0
|
5
|
-
Content-Type: text/html; charset=ISO-8859-1
|
6
|
-
Set-Cookie: PREF=ID=
|
7
|
-
Set-Cookie: NID=
|
8
|
-
Server: gws
|
9
|
-
X-XSS-Protection:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
window.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
})();
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sat, 14 Aug 2010 21:58:26 GMT
|
3
|
+
Expires: -1
|
4
|
+
Cache-Control: private, max-age=0
|
5
|
+
Content-Type: text/html; charset=ISO-8859-1
|
6
|
+
Set-Cookie: PREF=ID=11955ae9690fd292:TM=1281823106:LM=1281823106:S=wHdloFqGQ_OLSE92; expires=Mon, 13-Aug-2012 21:58:26 GMT; path=/; domain=.google.ca
|
7
|
+
Set-Cookie: NID=37=USTdOsxOSMbLjphkJ3S5Ueua3Yc23COXuK_pbztcHx7JoyhomwQySrvebCf3_u8eyrBiLWssVzaZcEOiKGEJbNdy8lRhnq_mfrdz693LaMjNPh__ccW4sgn1ZO6nQltE; expires=Sun, 13-Feb-2011 21:58:26 GMT; path=/; domain=.google.ca; HttpOnly
|
8
|
+
Server: gws
|
9
|
+
X-XSS-Protection: 1; mode=block
|
10
|
+
|
11
|
+
<!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>window.google={kEI:"ghFnTJiGG9WhnQf4o9X8BA",kEXPI:"25913,25976,26060",kCSI:{e:"25913,25976,26060",ei:"ghFnTJiGG9WhnQf4o9X8BA",expi:"25913,25976,26060"},ml:function(){},kHL:"en",time:function(){return(new Date).getTime()},log:function(b,d,c){var a=new Image,e=google,g=e.lc,f=e.li;a.onerror=(a.onload=(a.onabort=function(){delete g[f]}));g[f]=a;c=c||"/gen_204?atyp=i&ct="+b+"&cad="+d+"&zx="+google.time();a.src=c;e.li=f+1},lc:[],li:0,Toolbelt:{}};
|
12
|
+
window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{}catch(u){}window.google.jsrt_kill=1;
|
13
|
+
var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
|
14
|
+
window._gjuc())&&setTimeout(_gjp,500)};
|
15
|
+
window._gjp && _gjp()</script><style id=gstyle>body{margin:0}#gog{padding:3px 8px 0}td{line-height:.8em;}.gac_m td{line-height:17px;}form{margin-bottom:20px;}body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{font-weight:bold;font-style:normal}.lst{width:496px}.tiah{width:458px}input{font-family:inherit}a.gb1,a.gb2,a.gb3,a.gb4{color:#11c !important}#gbar,#guser{font-size:13px;padding-top:1px !important}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{color:#00c !important}body{background:#fff;color:black}input{-moz-box-sizing:content-box}a{color:#11c;text-decoration:none}a:hover,a:active{text-decoration:underline}.fl a{color:#4272db}a:visited{color:#551a8b}a.gb1,a.gb4{text-decoration:underline}a.gb3:hover{text-decoration:none}#ghead a.gb2:hover{color:#fff!important}.ds{display:-moz-inline-box}.ds{border-bottom:solid 1px #e7e7e7;border-right:solid 1px #e7e7e7;display:inline-block;margin:3px 0 4px;margin-left:4px}.sblc{padding-top:5px}.sblc a{display:block;margin:2px 0;margin-left:13px;font-size:11px;}.lsbb{background:#eee;border:solid 1px;border-color:#ccc #999 #999 #ccc;height:30px;display:block}.lsb{background:url(/images/srpr/nav_logo14.png) bottom;font:15px arial,sans-serif;border:none;color:#000;cursor:pointer;height:30px;margin:0;outline:0;vertical-align:top}.lsb:active{background:#ccc}.lst:focus{outline:none}.ftl,#fll a{margin:0 12px}#addlang a{padding:0 3px}.gac_v div{display:none}.gac_v .gac_v2,.gac_bt{display:block!important}</style><script>google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f.q.focus();if(document.images)new Image().src='/images/srpr/nav_logo14.png'" ><textarea id=csi style=display:none></textarea><div id=ghead><div id=gbar><nobr><b class=gb1>Web</b> <a onclick=gbar.qs(this) href="http://www.google.ca/imghp?hl=en&tab=wi" class=gb1>Images</a> <a onclick=gbar.qs(this) href="http://video.google.ca/?hl=en&tab=wv" class=gb1>Videos</a> <a onclick=gbar.qs(this) href="http://maps.google.ca/maps?hl=en&tab=wl" class=gb1>Maps</a> <a onclick=gbar.qs(this) href="http://news.google.ca/nwshp?hl=en&tab=wn" class=gb1>News</a> <a onclick=gbar.qs(this) href="http://books.google.ca/bkshp?hl=en&tab=wp" class=gb1>Books</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.ca/intl/en/options/" class=gb1 style="text-decoration:none"><u>more</u> »</a></nobr></div><div id=guser width=100%><nobr><span id=gbn class=gbi></span><span id=gbf class=gbf></span><span id=gbe><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.ca/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNG2Kt7TgMZuV7Fl3FeeTOmTWMvggA" class=gb4>iGoogle</a> | </span><a href="/preferences?hl=en" class=gb4>Settings</a> | <a href="https://www.google.com/accounts/Login?hl=en&continue=http://www.google.ca/" class=gb4>Sign in</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div></div> <center><br clear=all id=lgpd><div id=lga><table cellpadding=0 cellspacing=0 border=0><tr><td align=right valign=bottom><img src=images/hp0.gif width=158 height=78 alt="Google"></td><td valign=bottom><img src=images/hp1.gif width=50 height=78 alt=""></td><td valign=bottom><img src=images/hp2.gif width=68 height=78 alt=""></td></tr><tr><td class=h align=right valign=top><b></b></td><td valign=top><img src=images/hp3.gif width=50 height=32 alt=""></td><td valign=top class=h><font color=#666666 style=font-size:16px><b>Canada</b></font></td></tr></table><br></div><form action="/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%> </td><td align=center nowrap><input name=hl type=hidden value=en><input name=source type=hidden value=hp><input type=hidden name=ie value="ISO-8859-1"><div class=ds style="height:32px;margin:4px 0"><input autocomplete="off" maxlength=2048 name=q class="lst" title="Google Search" value="" size=57 style="background:#fff;border:1px solid #ccc;border-bottom-color:#999;border-right-color:#999;color:#000;font:18px arial,sans-serif bold;height:25px;margin:0;padding:5px 8px 0 6px;vertical-align:top"></div><br style="line-height:0"><span class=ds><span class=lsbb><input name=btnG type=submit value="Google Search" class=lsb></span></span><span class=ds><span class=lsbb><input name=btnI type=submit value="I'm Feeling Lucky" class=lsb></span></span></td><td nowrap width=25% align=left class=sblc><a href="/advanced_search?hl=en">Advanced Search</a><a href="/language_tools?hl=en">Language Tools</a></td></tr></table></form><div style="font-size:83%;min-height:3.5em"><br><div id=als><font size=-1 id=addlang>Google.ca offered in: <a href="http://www.google.ca/setprefs?sig=0_h3wwdBzWMwIKco5_3UM-S8-NI00=&hl=fr">fran�ais</a></font><br><br></div></div><div id=res></div><span id=footer><center id=fctr><div style="font-size:10pt"><div id=fll style="margin:19px auto 19px auto;text-align:center"><a href="/intl/en/ads/">Advertising Programs</a><a href="/services/">Business Solutions</a><a href="/intl/en/about.html">About Google</a><a href="http://www.google.com/ncr">Go to Google.com</a></div></div><p style="color:#767676;font-size:8pt">© 2010 - <a href="/intl/en/privacy.html">Privacy</a></p></center></span> <div id=xjsd></div><div id=xjsi><script>if(google.y)google.y.first=[];if(google.y)google.y.first=[];google.dstr=[];google.rein=[];window.setTimeout(function(){var a=document.createElement("script");a.src="/extern_js/f/CgJlbhICY2EgACswRTgBLCswWjgDLCswDjgXLCswFzgHLCswJzgELCswPDgDLCswCjhzQB0sKzAlOMqIASwrMEA4EiwrMEE4BSwrMFQ4ACwrMBg4BSwrMCY4DSyAAheQAhY/shQ1OaVztmo.js";(document.getElementById("xjsd")||document.body).appendChild(a);if(google.timers&&google.timers.load.t)google.timers.load.t.xjsls=(new Date).getTime();},0);
|
16
|
+
;google.neegg=1;google.y.first.push(function(){var form=document.f||document.f||document.gs;google.ac.i(form,form.q,'','','',{o:1,sw:1});google.History&&google.History.initialize('/')});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);google.fade=null;}</script></div><script>(function(){
|
17
|
+
var b,d,e,f;function g(a,c){if(a.removeEventListener){a.removeEventListener("load",c,false);a.removeEventListener("error",c,false)}else{a.detachEvent("onload",c);a.detachEvent("onerror",c)}}function h(a){f=(new Date).getTime();++d;a=a||window.event;var c=a.target||a.srcElement;g(c,h)}var i=document.getElementsByTagName("img");b=i.length;d=0;for(var j=0,k;j<b;++j){k=i[j];if(k.complete||typeof k.src!="string"||!k.src)++d;else if(k.addEventListener){k.addEventListener("load",h,false);k.addEventListener("error",
|
18
|
+
h,false)}else{k.attachEvent("onload",h);k.attachEvent("onerror",h)}}e=b-d;function l(){if(!google.timers.load.t)return;google.timers.load.t.ol=(new Date).getTime();google.timers.load.t.iml=f;google.kCSI.imc=d;google.kCSI.imn=b;google.kCSI.imp=e;google.report&&google.report(google.timers.load,google.kCSI)}if(window.addEventListener)window.addEventListener("load",l,false);else if(window.attachEvent)window.attachEvent("onload",l);google.timers.load.t.prt=(f=(new Date).getTime());
|
19
|
+
})();
|
20
|
+
</script>
|
data/spec/hash_spec.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
require 'spec/helper'
|
2
|
-
|
3
|
-
describe Hash do
|
4
|
-
|
5
|
-
describe ".to_params" do
|
6
|
-
it "should transform a basic hash into HTTP POST Params" do
|
7
|
-
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "a=alpha"
|
8
|
-
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "b=beta"
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should transform a more complex hash into HTTP POST Params" do
|
12
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "a=a"
|
13
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[0]=c"
|
14
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[1]=d"
|
15
|
-
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[2]=e"
|
16
|
-
end
|
17
|
-
|
18
|
-
it "should transform a very complex hash into HTTP POST Params" do
|
19
|
-
params = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.split("&")
|
20
|
-
params.should include "a=a"
|
21
|
-
params.should include "b[0][d]=d"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
1
|
+
require 'spec/helper'
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
|
5
|
+
describe ".to_params" do
|
6
|
+
it "should transform a basic hash into HTTP POST Params" do
|
7
|
+
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "a=alpha"
|
8
|
+
{:a => "alpha", :b => "beta"}.to_params.split("&").should include "b=beta"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should transform a more complex hash into HTTP POST Params" do
|
12
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "a=a"
|
13
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[0]=c"
|
14
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[1]=d"
|
15
|
+
{:a => "a", :b => ["c", "d", "e"]}.to_params.split("&").should include "b[2]=e"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should transform a very complex hash into HTTP POST Params" do
|
19
|
+
params = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.split("&")
|
20
|
+
params.should include "a=a"
|
21
|
+
params.should include "b[0][d]=d"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/helper.rb
CHANGED
data/spec/mock_spec.rb
CHANGED
@@ -2,26 +2,70 @@ require 'spec/helper'
|
|
2
2
|
|
3
3
|
describe 'em-http mock' do
|
4
4
|
|
5
|
+
before(:all) do
|
6
|
+
EventMachine::MockHttpRequest.activate!
|
7
|
+
end
|
8
|
+
|
9
|
+
after(:all) do
|
10
|
+
EventMachine::MockHttpRequest.deactivate!
|
11
|
+
end
|
12
|
+
|
5
13
|
before(:each) do
|
6
14
|
EventMachine::MockHttpRequest.reset_registry!
|
7
15
|
EventMachine::MockHttpRequest.reset_counts!
|
8
16
|
end
|
17
|
+
|
18
|
+
it "should serve a fake http request from a proc" do
|
19
|
+
EventMachine::HttpRequest.register('http://www.google.ca:80/', :get) { |req|
|
20
|
+
req.response_header.http_status = 200
|
21
|
+
req.response_header['SOME_WACKY_HEADER'] = 'WACKY_HEADER_VALUE'
|
22
|
+
req.response = "Well, now this is fun."
|
23
|
+
}
|
24
|
+
EM.run {
|
25
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
26
|
+
http.errback { fail }
|
27
|
+
http.callback {
|
28
|
+
http.response_header.status.should == 200
|
29
|
+
http.response_header['SOME_WACKY_HEADER'].should == 'WACKY_HEADER_VALUE'
|
30
|
+
http.response.should == "Well, now this is fun."
|
31
|
+
EventMachine.stop
|
32
|
+
}
|
33
|
+
}
|
9
34
|
|
10
|
-
|
11
|
-
|
35
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should serve a fake http request from a proc with raw data" do
|
39
|
+
EventMachine::HttpRequest.register('http://www.google.ca:80/', :get) { |req|
|
40
|
+
req.receive_data(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')))
|
41
|
+
}
|
12
42
|
EM.run {
|
13
|
-
|
14
|
-
http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
|
43
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
15
44
|
http.errback { fail }
|
16
45
|
http.callback {
|
17
|
-
http.
|
46
|
+
http.response_header.status.should == 200
|
47
|
+
http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\r\n\r\n", 2).last
|
48
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
18
49
|
EventMachine.stop
|
19
50
|
}
|
20
51
|
}
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should serve a fake http request from a file" do
|
55
|
+
EventMachine::HttpRequest.register_file('http://www.google.ca:80/', :get, {}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
|
56
|
+
EM.run {
|
21
57
|
|
22
|
-
|
58
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
59
|
+
http.errback { fail }
|
60
|
+
http.callback {
|
61
|
+
http.response_header.status.should == 200
|
62
|
+
http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\r\n\r\n", 2).last
|
63
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
64
|
+
EventMachine.stop
|
65
|
+
}
|
66
|
+
}
|
23
67
|
end
|
24
|
-
|
68
|
+
|
25
69
|
it "should serve a fake http request from a string" do
|
26
70
|
data = <<-HEREDOC
|
27
71
|
HTTP/1.0 200 OK
|
@@ -46,70 +90,71 @@ window._gjp && _gjp()</script><style>td{line-height:.8em;}.gac_m td{line-height:
|
|
46
90
|
function a(){google.timers.load.t.ol=(new Date).getTime();google.report&&google.timers.load.t.xjs&&google.report(google.timers.load,google.kCSI)}if(window.addEventListener)window.addEventListener("load",a,false);else if(window.attachEvent)window.attachEvent("onload",a);google.timers.load.t.prt=(new Date).getTime();
|
47
91
|
})();
|
48
92
|
HEREDOC
|
49
|
-
EventMachine::
|
93
|
+
EventMachine::HttpRequest.register('http://www.google.ca:80/', :get, {}, data)
|
50
94
|
EventMachine.run {
|
51
|
-
|
52
|
-
http = EventMachine::
|
95
|
+
|
96
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
53
97
|
http.errback { fail }
|
54
98
|
http.callback {
|
55
99
|
http.response.should == data.split("\n\n", 2).last
|
100
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
56
101
|
EventMachine.stop
|
57
102
|
}
|
58
103
|
}
|
59
|
-
|
60
|
-
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
104
|
+
|
61
105
|
end
|
62
106
|
|
63
107
|
it "should serve a fake failing http request" do
|
64
|
-
EventMachine::
|
108
|
+
EventMachine::HttpRequest.register('http://www.google.ca:80/', :get, {}, :fail)
|
65
109
|
error = false
|
66
110
|
|
67
111
|
EventMachine.run {
|
68
|
-
http = EventMachine::
|
112
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
113
|
+
http.callback {
|
114
|
+
EventMachine.stop
|
115
|
+
fail
|
116
|
+
}
|
69
117
|
http.errback {
|
70
|
-
|
118
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
71
119
|
EventMachine.stop
|
72
120
|
}
|
73
121
|
}
|
74
122
|
|
75
|
-
error.should be_true
|
76
|
-
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
77
123
|
end
|
78
|
-
|
124
|
+
|
79
125
|
it "should distinguish the cache by the given headers" do
|
80
|
-
EventMachine::
|
81
|
-
EventMachine::
|
126
|
+
EventMachine::HttpRequest.register_file('http://www.google.ca:80/', :get, {:user_agent => 'BERT'}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
|
127
|
+
EventMachine::HttpRequest.register_file('http://www.google.ca:80/', :get, {}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
|
82
128
|
EM.run {
|
83
|
-
|
84
|
-
http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
|
129
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
85
130
|
http.errback { fail }
|
86
131
|
http.callback {
|
87
|
-
http.
|
132
|
+
http.response_header.status.should == 200
|
133
|
+
http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\r\n\r\n", 2).last
|
134
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
135
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {:user_agent => 'BERT'}).should == 0
|
88
136
|
EventMachine.stop
|
89
137
|
}
|
90
138
|
}
|
91
|
-
|
92
|
-
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
93
|
-
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {:user_agent => 'BERT'}).should == 0
|
94
|
-
|
139
|
+
|
95
140
|
EM.run {
|
96
|
-
|
97
|
-
http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get({:head => {:user_agent => 'BERT'}})
|
141
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get({:head => {:user_agent => 'BERT'}})
|
98
142
|
http.errback { fail }
|
99
143
|
http.callback {
|
100
|
-
http.
|
144
|
+
http.response_header.status.should == 200
|
145
|
+
http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\r\n\r\n", 2).last
|
146
|
+
EventMachine::HttpRequest.count('http://www.google.ca:80/', :get, {:user_agent => 'BERT'}).should == 1
|
101
147
|
EventMachine.stop
|
102
148
|
}
|
103
149
|
}
|
104
|
-
|
105
|
-
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {:user_agent => 'BERT'}).should == 1
|
150
|
+
|
106
151
|
end
|
107
152
|
|
108
153
|
it "should raise an exception if pass-thru is disabled" do
|
109
|
-
EventMachine::
|
154
|
+
EventMachine::HttpRequest.pass_through_requests = false
|
110
155
|
EventMachine.run {
|
111
156
|
proc {
|
112
|
-
http = EventMachine::
|
157
|
+
http = EventMachine::HttpRequest.new('http://www.google.ca/').get
|
113
158
|
}.should raise_error
|
114
159
|
EventMachine.stop
|
115
160
|
}
|
data/spec/multi_spec.rb
CHANGED
@@ -5,26 +5,43 @@ describe EventMachine::MultiRequest do
|
|
5
5
|
|
6
6
|
it "should submit multiple requests in parallel and return once all of them are complete" do
|
7
7
|
EventMachine.run {
|
8
|
-
|
8
|
+
|
9
9
|
# create an instance of multi-request handler, and the requests themselves
|
10
10
|
multi = EventMachine::MultiRequest.new
|
11
|
-
|
11
|
+
|
12
12
|
# add multiple requests to the multi-handler
|
13
13
|
multi.add(EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(:query => {:q => 'test'}))
|
14
14
|
multi.add(EventMachine::HttpRequest.new('http://0.0.0.0:8083/').get(:timeout => 1))
|
15
|
-
|
15
|
+
|
16
16
|
multi.callback {
|
17
17
|
# verify successful request
|
18
18
|
multi.responses[:succeeded].size.should == 1
|
19
19
|
multi.responses[:succeeded].first.response.should match(/test/)
|
20
|
-
|
20
|
+
|
21
21
|
# verify invalid requests
|
22
22
|
multi.responses[:failed].size.should == 1
|
23
23
|
multi.responses[:failed].first.response_header.status.should == 0
|
24
|
-
|
24
|
+
|
25
25
|
EventMachine.stop
|
26
26
|
}
|
27
|
-
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should accept multiple open connections and return once all of them are complete" do
|
31
|
+
EventMachine.run {
|
32
|
+
http1 = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(:query => {:q => 'test'})
|
33
|
+
http2 = EventMachine::HttpRequest.new('http://0.0.0.0:8083/').get(:timeout => 1)
|
34
|
+
|
35
|
+
multi = EventMachine::MultiRequest.new([http1, http2]) do
|
36
|
+
multi.responses[:succeeded].size.should == 1
|
37
|
+
multi.responses[:succeeded].first.response.should match(/test/)
|
38
|
+
|
39
|
+
multi.responses[:failed].size.should == 1
|
40
|
+
multi.responses[:failed].first.response_header.status.should == 0
|
41
|
+
|
42
|
+
EventMachine.stop
|
43
|
+
end
|
44
|
+
}
|
28
45
|
end
|
29
46
|
|
30
47
|
it "should handle multiple mock requests" do
|
@@ -32,20 +49,20 @@ describe EventMachine::MultiRequest do
|
|
32
49
|
EventMachine::MockHttpRequest.register_file('http://0.0.0.0:8083/', :get, {}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
|
33
50
|
|
34
51
|
EventMachine.run {
|
35
|
-
|
52
|
+
|
36
53
|
# create an instance of multi-request handler, and the requests themselves
|
37
54
|
multi = EventMachine::MultiRequest.new
|
38
|
-
|
55
|
+
|
39
56
|
# add multiple requests to the multi-handler
|
40
57
|
multi.add(EventMachine::MockHttpRequest.new('http://127.0.0.1:8080/').get)
|
41
58
|
multi.add(EventMachine::MockHttpRequest.new('http://0.0.0.0:8083/').get)
|
42
|
-
|
59
|
+
|
43
60
|
multi.callback {
|
44
61
|
# verify successful request
|
45
62
|
multi.responses[:succeeded].size.should == 2
|
46
63
|
|
47
64
|
EventMachine.stop
|
48
65
|
}
|
49
|
-
}
|
66
|
+
}
|
50
67
|
end
|
51
68
|
end
|
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,19 @@ describe EventMachine::HttpRequest do
|
|
57
68
|
}
|
58
69
|
end
|
59
70
|
|
71
|
+
it "should accept optional host override" do
|
72
|
+
EventMachine.run {
|
73
|
+
http = EventMachine::HttpRequest.new('http://google.com:8080/').get :host => '127.0.0.1'
|
74
|
+
|
75
|
+
http.errback { failed }
|
76
|
+
http.callback {
|
77
|
+
http.response_header.status.should == 200
|
78
|
+
http.response.should match(/Hello/)
|
79
|
+
EventMachine.stop
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
60
84
|
it "should perform successfull GET with a URI passed as argument" do
|
61
85
|
EventMachine.run {
|
62
86
|
uri = URI.parse('http://127.0.0.1:8080/')
|
@@ -272,6 +296,20 @@ describe EventMachine::HttpRequest do
|
|
272
296
|
}
|
273
297
|
end
|
274
298
|
|
299
|
+
it "should return ETag and Last-Modified headers" do
|
300
|
+
EventMachine.run {
|
301
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_query').get
|
302
|
+
|
303
|
+
http.errback { failed }
|
304
|
+
http.callback {
|
305
|
+
http.response_header.status.should == 200
|
306
|
+
http.response_header.etag.should match('abcdefg')
|
307
|
+
http.response_header.last_modified.should match('Fri, 13 Aug 2010 17:31:21 GMT')
|
308
|
+
EventMachine.stop
|
309
|
+
}
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
275
313
|
it "should detect deflate encoding" do
|
276
314
|
EventMachine.run {
|
277
315
|
|
@@ -318,58 +356,95 @@ describe EventMachine::HttpRequest do
|
|
318
356
|
}
|
319
357
|
end
|
320
358
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
http.
|
327
|
-
|
359
|
+
context "redirect" do
|
360
|
+
it "should report last_effective_url" do
|
361
|
+
EventMachine.run {
|
362
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
|
363
|
+
http.errback { failed }
|
364
|
+
http.callback {
|
365
|
+
http.response_header.status.should == 200
|
366
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/'
|
328
367
|
|
329
|
-
|
368
|
+
EM.stop
|
369
|
+
}
|
330
370
|
}
|
331
|
-
|
332
|
-
end
|
371
|
+
end
|
333
372
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
373
|
+
it "should follow location redirects" do
|
374
|
+
EventMachine.run {
|
375
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get :redirects => 1
|
376
|
+
http.errback { failed }
|
377
|
+
http.callback {
|
378
|
+
http.response_header.status.should == 200
|
379
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
380
|
+
http.response.should == "compressed"
|
381
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
382
|
+
http.redirects.should == 1
|
344
383
|
|
345
|
-
|
384
|
+
EM.stop
|
385
|
+
}
|
346
386
|
}
|
347
|
-
|
348
|
-
end
|
387
|
+
end
|
349
388
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
389
|
+
it "should default to 0 redirects" do
|
390
|
+
EventMachine.run {
|
391
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect').get
|
392
|
+
http.errback { failed }
|
393
|
+
http.callback {
|
394
|
+
http.response_header.status.should == 301
|
395
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8080/gzip'
|
396
|
+
http.redirects.should == 0
|
358
397
|
|
359
|
-
|
398
|
+
EM.stop
|
399
|
+
}
|
360
400
|
}
|
361
|
-
|
362
|
-
end
|
401
|
+
end
|
363
402
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
403
|
+
it "should not invoke redirect logic on failed connections" do
|
404
|
+
EventMachine.run {
|
405
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8081/').get :timeout => 0.1, :redirects => 5
|
406
|
+
http.callback { failed }
|
407
|
+
http.errback {
|
408
|
+
http.redirects.should == 0
|
409
|
+
EM.stop
|
410
|
+
}
|
371
411
|
}
|
372
|
-
|
412
|
+
end
|
413
|
+
|
414
|
+
it "should normalize redirect urls" do
|
415
|
+
EventMachine.run {
|
416
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/bad').get :redirects => 1
|
417
|
+
http.errback { failed }
|
418
|
+
http.callback {
|
419
|
+
http.last_effective_url.to_s.should match('http://127.0.0.1:8080/')
|
420
|
+
http.response.should match('Hello, World!')
|
421
|
+
EM.stop
|
422
|
+
}
|
423
|
+
}
|
424
|
+
end
|
425
|
+
|
426
|
+
it "should fail gracefully on a missing host in absolute Location header" do
|
427
|
+
EventMachine.run {
|
428
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/nohost').get :redirects => 1
|
429
|
+
http.callback { failed }
|
430
|
+
http.errback {
|
431
|
+
http.error.should == 'Location header format error'
|
432
|
+
EM.stop
|
433
|
+
}
|
434
|
+
}
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should fail gracefully on an invalid host in Location header" do
|
438
|
+
pending "validate tld's?"
|
439
|
+
EventMachine.run {
|
440
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/redirect/badhost').get :redirects => 1
|
441
|
+
http.callback { failed }
|
442
|
+
http.errback {
|
443
|
+
http.error.should == 'Location header format error'
|
444
|
+
EM.stop
|
445
|
+
}
|
446
|
+
}
|
447
|
+
end
|
373
448
|
end
|
374
449
|
|
375
450
|
it "should optionally pass the response body progressively" do
|
@@ -593,35 +668,93 @@ describe EventMachine::HttpRequest do
|
|
593
668
|
EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get, {}).should == 1
|
594
669
|
end
|
595
670
|
|
596
|
-
context "connections via
|
671
|
+
context "connections via" do
|
672
|
+
context "direct proxy" do
|
673
|
+
it "should default to skip CONNECT" do
|
674
|
+
EventMachine.run {
|
597
675
|
|
598
|
-
|
599
|
-
|
676
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
677
|
+
:host => '127.0.0.1', :port => 8083
|
678
|
+
}
|
679
|
+
|
680
|
+
http.errback { p http.inspect; failed }
|
681
|
+
http.callback {
|
682
|
+
http.response_header.status.should == 200
|
683
|
+
http.response.should match('test')
|
684
|
+
EventMachine.stop
|
685
|
+
}
|
686
|
+
}
|
687
|
+
end
|
600
688
|
|
601
|
-
|
689
|
+
it "should send absolute URIs to the proxy server" do
|
690
|
+
EventMachine.run {
|
602
691
|
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
692
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/?q=test').get :proxy => {
|
693
|
+
:host => '127.0.0.1', :port => 8083
|
694
|
+
}
|
695
|
+
|
696
|
+
http.errback { p http.inspect; failed }
|
697
|
+
http.callback {
|
698
|
+
http.response_header.status.should == 200
|
699
|
+
# The test proxy server gives the requested uri back in this header
|
700
|
+
http.response_header['X_THE_REQUESTED_URI'].should == 'http://127.0.0.1:8080/?q=test'
|
701
|
+
http.response_header['X_THE_REQUESTED_URI'].should_not == '/?q=test'
|
702
|
+
http.response.should match('test')
|
703
|
+
EventMachine.stop
|
704
|
+
}
|
608
705
|
}
|
609
|
-
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should include query parameters specified in the options" do
|
709
|
+
EventMachine.run {
|
710
|
+
|
711
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(
|
712
|
+
:proxy => { :host => '127.0.0.1', :port => 8083 },
|
713
|
+
:query => { 'q' => 'test' }
|
714
|
+
)
|
715
|
+
|
716
|
+
http.errback { p http.inspect; failed }
|
717
|
+
http.callback {
|
718
|
+
http.response_header.status.should == 200
|
719
|
+
http.response.should match('test')
|
720
|
+
EventMachine.stop
|
721
|
+
}
|
722
|
+
}
|
723
|
+
end
|
610
724
|
end
|
611
725
|
|
612
|
-
|
613
|
-
|
726
|
+
context "CONNECT proxy" do
|
727
|
+
it "should work with CONNECT proxy servers" do
|
728
|
+
EventMachine.run {
|
614
729
|
|
615
|
-
|
616
|
-
|
730
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get({
|
731
|
+
:proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
732
|
+
})
|
617
733
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
734
|
+
http.errback { p http.inspect; failed }
|
735
|
+
http.callback {
|
736
|
+
http.response_header.status.should == 200
|
737
|
+
http.response.should == 'Hello, World!'
|
738
|
+
EventMachine.stop
|
739
|
+
}
|
623
740
|
}
|
624
|
-
|
741
|
+
end
|
742
|
+
|
743
|
+
it "should proxy POST data" do
|
744
|
+
EventMachine.run {
|
745
|
+
|
746
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
|
747
|
+
:body => "data", :proxy => {:host => '127.0.0.1', :port => 8082, :use_connect => true}
|
748
|
+
})
|
749
|
+
|
750
|
+
http.errback { failed }
|
751
|
+
http.callback {
|
752
|
+
http.response_header.status.should == 200
|
753
|
+
http.response.should match(/data/)
|
754
|
+
EventMachine.stop
|
755
|
+
}
|
756
|
+
}
|
757
|
+
end
|
625
758
|
end
|
626
759
|
end
|
627
760
|
|