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/dns_spec.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'helper'
2
+
3
+ describe EventMachine::HttpRequest do
4
+
5
+ it "should fail gracefully on an invalid host in Location header" do
6
+ EventMachine.run {
7
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/badhost', :connect_timeout => 0.1).get :redirects => 1
8
+ http.callback { failed(http) }
9
+ http.errback {
10
+ http.error.should match('unable to resolve server address')
11
+ EventMachine.stop
12
+ }
13
+ }
14
+ end
15
+
16
+ it "should fail GET on DNS timeout" do
17
+ EventMachine.run {
18
+ EventMachine.heartbeat_interval = 0.1
19
+ http = EventMachine::HttpRequest.new('http://127.1.1.1/', :connect_timeout => 0.1).get
20
+ http.callback { failed(http) }
21
+ http.errback {
22
+ http.response_header.status.should == 0
23
+ EventMachine.stop
24
+ }
25
+ }
26
+ end
27
+
28
+ it "should fail GET on invalid host" do
29
+ EventMachine.run {
30
+ EventMachine.heartbeat_interval = 0.1
31
+ http = EventMachine::HttpRequest.new('http://somethinglocal/', :connect_timeout => 0.1).get
32
+ http.callback { failed(http) }
33
+ http.errback {
34
+ http.error.should match(/unable to resolve server address/)
35
+ http.response_header.status.should == 0
36
+ EventMachine.stop
37
+ }
38
+ }
39
+ end
40
+
41
+ end
@@ -31,10 +31,10 @@ describe EventMachine::HttpEncoding do
31
31
  params.should == "bad%26str[key%26key][0]=bad%2B%26stuff&bad%26str[key%26key][1]=%5Btest%5D"
32
32
  end
33
33
 
34
- it "should be fast on long string escapes" do
35
- s = Time.now
36
- 5000.times { |n| form_encode_body({:a => "{a:'b', d:'f', g:['a','b']}"*50}) }
37
- (Time.now - s).should satisfy { |t| t < 1.5 }
38
- end
34
+ # xit "should be fast on long string escapes" do
35
+ # s = Time.now
36
+ # 5000.times { |n| form_encode_body({:a => "{a:'b', d:'f', g:['a','b']}"*50}) }
37
+ # (Time.now - s).should satisfy { |t| t < 1.5 }
38
+ # end
39
39
 
40
- end
40
+ end
@@ -0,0 +1,99 @@
1
+ require 'helper'
2
+
3
+ requires_connection do
4
+
5
+ describe EventMachine::HttpRequest do
6
+
7
+ it "should follow redirects on HEAD method (external)" do
8
+ EventMachine.run {
9
+ http = EventMachine::HttpRequest.new('http://www.google.com/').head :redirects => 1
10
+ http.errback { failed(http) }
11
+ http.callback {
12
+ http.response_header.status.should == 200
13
+ EM.stop
14
+ }
15
+ }
16
+ end
17
+
18
+ it "should perform a streaming GET" do
19
+ EventMachine.run {
20
+
21
+ # digg.com uses chunked encoding
22
+ http = EventMachine::HttpRequest.new('http://digg.com/news').get
23
+
24
+ http.errback { failed(http) }
25
+ http.callback {
26
+ http.response_header.status.should == 200
27
+ EventMachine.stop
28
+ }
29
+ }
30
+ end
31
+
32
+ it "should handle a 100 continue" do
33
+ EventMachine.run {
34
+ # 8.2.3 Use of the 100 (Continue) Status - http://www.ietf.org/rfc/rfc2616.txt
35
+ #
36
+ # An origin server SHOULD NOT send a 100 (Continue) response if
37
+ # the request message does not include an Expect request-header
38
+ # field with the "100-continue" expectation, and MUST NOT send a
39
+ # 100 (Continue) response if such a request comes from an HTTP/1.0
40
+ # (or earlier) client. There is an exception to this rule: for
41
+ # compatibility with RFC 2068, a server MAY send a 100 (Continue)
42
+ # status in response to an HTTP/1.1 PUT or POST request that does
43
+ # not include an Expect request-header field with the "100-
44
+ # continue" expectation. This exception, the purpose of which is
45
+ # to minimize any client processing delays associated with an
46
+ # undeclared wait for 100 (Continue) status, applies only to
47
+ # HTTP/1.1 requests, and not to requests with any other HTTP-
48
+ # version value.
49
+ #
50
+ # 10.1.1: 100 Continue - http://www.ietf.org/rfc/rfc2068.txt
51
+ # The client may continue with its request. This interim response is
52
+ # used to inform the client that the initial part of the request has
53
+ # been received and has not yet been rejected by the server. The client
54
+ # SHOULD continue by sending the remainder of the request or, if the
55
+ # request has already been completed, ignore this response. The server
56
+ # MUST send a final response after the request has been completed.
57
+
58
+ url = 'http://ws.serviceobjects.com/lv/LeadValidation.asmx/ValidateLead_V2'
59
+ http = EventMachine::HttpRequest.new(url).post :body => {:name => :test}
60
+
61
+ http.errback { failed(http) }
62
+ http.callback {
63
+ http.response_header.status.should == 500
64
+ http.response.should match('Missing')
65
+ EventMachine.stop
66
+ }
67
+ }
68
+ end
69
+
70
+ context "keepalive" do
71
+ it "should default to non-keepalive" do
72
+ EventMachine.run {
73
+ headers = {'If-Modified-Since' => 'Thu, 05 Aug 2010 22:54:44 GMT'}
74
+ http = EventMachine::HttpRequest.new('http://www.google.com/images/logos/ps_logo2.png').get :head => headers
75
+
76
+ http.errback { fail }
77
+ start = Time.now.to_i
78
+ http.callback {
79
+ (start - Time.now.to_i).should be_within(1).of(0)
80
+ EventMachine.stop
81
+ }
82
+ }
83
+ end
84
+
85
+ it "should work with keep-alive servers" do
86
+ EventMachine.run {
87
+ http = EventMachine::HttpRequest.new('http://mexicodiario.com/touch.public.json.php').get :keepalive => true
88
+
89
+ http.errback { failed(http) }
90
+ http.callback {
91
+ http.response_header.status.should == 200
92
+ EventMachine.stop
93
+ }
94
+ }
95
+ end
96
+ end
97
+
98
+ end
99
+ end
@@ -1,20 +1,16 @@
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
1
+ HTTP/1.1 301 Moved Permanently
2
+ Location: http://www.google.ca/
3
+ Content-Type: text/html; charset=UTF-8
4
+ Date: Sun, 09 Jan 2011 02:51:43 GMT
5
+ Expires: Tue, 08 Feb 2011 02:51:43 GMT
6
+ Cache-Control: public, max-age=2592000
8
7
  Server: gws
8
+ Content-Length: 218
9
9
  X-XSS-Protection: 1; mode=block
10
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> &raquo;</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%>&nbsp;</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&#39;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=&amp;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&nbsp;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">&copy; 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>
11
+ <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
12
+ <TITLE>301 Moved</TITLE></HEAD><BODY>
13
+ <H1>301 Moved</H1>
14
+ The document has moved
15
+ <A HREF="http://www.google.ca/">here</A>.
16
+ </BODY></HTML>
data/spec/helper.rb CHANGED
@@ -1,8 +1,17 @@
1
- require 'rubygems'
2
- require 'rspec'
3
- require 'pp'
4
-
5
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
6
-
7
- require 'em-http'
8
- require 'em-websocket'
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'em-http'
5
+ require 'yajl'
6
+
7
+ require 'stallion'
8
+ require 'stub_server'
9
+
10
+ def failed(http = nil)
11
+ EventMachine.stop
12
+ http ? fail(http.error) : fail
13
+ end
14
+
15
+ def requires_connection(&blk)
16
+ blk.call if system('ping -c1 google.com &> /dev/null')
17
+ end
@@ -0,0 +1,53 @@
1
+ require 'helper'
2
+
3
+ describe EventMachine::HttpRequest do
4
+
5
+ context "connections via" do
6
+ let(:proxy) { {:proxy => { :host => '127.0.0.1', :port => 8083 }} }
7
+
8
+ it "should use HTTP proxy" do
9
+ EventMachine.run {
10
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get
11
+
12
+ http.errback { failed(http) }
13
+ http.callback {
14
+ http.response_header.status.should == 200
15
+ http.response.should match('test')
16
+ EventMachine.stop
17
+ }
18
+ }
19
+ end
20
+
21
+ it "should send absolute URIs to the proxy server" do
22
+ EventMachine.run {
23
+
24
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/?q=test', proxy).get
25
+
26
+ http.errback { failed(http) }
27
+ http.callback {
28
+ http.response_header.status.should == 200
29
+
30
+ # The test proxy server gives the requested uri back in this header
31
+ http.response_header['X_THE_REQUESTED_URI'].should == 'http://127.0.0.1:8090/?q=test'
32
+ http.response_header['X_THE_REQUESTED_URI'].should_not == '/?q=test'
33
+ http.response.should match('test')
34
+ EventMachine.stop
35
+ }
36
+ }
37
+ end
38
+
39
+ it "should include query parameters specified in the options" do
40
+ EventMachine.run {
41
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/', proxy).get :query => { 'q' => 'test' }
42
+
43
+ http.errback { failed(http) }
44
+ http.callback {
45
+ http.response_header.status.should == 200
46
+ http.response.should match('test')
47
+ EventMachine.stop
48
+ }
49
+ }
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,114 @@
1
+ require 'helper'
2
+
3
+ describe EventMachine::HttpRequest do
4
+
5
+ module EmptyMiddleware; end
6
+
7
+ class GlobalMiddleware
8
+ def self.response(resp)
9
+ resp.response_header['X-Global'] = 'middleware'
10
+ end
11
+ end
12
+
13
+ it "should accept middleware" do
14
+ EventMachine.run {
15
+ lambda {
16
+ conn = EM::HttpRequest.new('http://127.0.0.1:8090')
17
+ conn.use ResponseMiddleware
18
+ conn.use EmptyMiddleware
19
+
20
+ EM.stop
21
+ }.should_not raise_error
22
+ }
23
+ end
24
+
25
+ context "request" do
26
+ class ResponseMiddleware
27
+ def self.response(resp)
28
+ resp.response_header['X-Header'] = 'middleware'
29
+ resp.response = 'Hello, Middleware!'
30
+ end
31
+ end
32
+
33
+
34
+ it "should execute response middleware before user callbacks" do
35
+ EventMachine.run {
36
+ conn = EM::HttpRequest.new('http://127.0.0.1:8090')
37
+ conn.use ResponseMiddleware
38
+
39
+ req = conn.get
40
+ req.callback {
41
+ req.response_header['X-Header'].should match('middleware')
42
+ req.response.should match('Hello, Middleware!')
43
+ EM.stop
44
+ }
45
+ }
46
+ end
47
+
48
+ it "should execute global response middleware before user callbacks" do
49
+ EventMachine.run {
50
+ EM::HttpRequest.use GlobalMiddleware
51
+
52
+ conn = EM::HttpRequest.new('http://127.0.0.1:8090')
53
+
54
+ req = conn.get
55
+ req.callback {
56
+ req.response_header['X-Global'].should match('middleware')
57
+ EM.stop
58
+ }
59
+ }
60
+ end
61
+ end
62
+
63
+ context "request" do
64
+ class RequestMiddleware
65
+ def self.request(head, body)
66
+ head['X-Middleware'] = 'middleware' # insert new header
67
+ body += ' modified' # modify post body
68
+
69
+ [head, body]
70
+ end
71
+ end
72
+
73
+ it "should execute request middleware before dispatching request" do
74
+ EventMachine.run {
75
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
76
+ conn.use RequestMiddleware
77
+
78
+ req = conn.post :body => "data"
79
+ req.callback {
80
+ req.response_header.status.should == 200
81
+ req.response.should match(/data modified/)
82
+ EventMachine.stop
83
+ }
84
+ }
85
+ end
86
+ end
87
+
88
+ context "jsonify" do
89
+ class JSONify
90
+ def self.request(head, body)
91
+ [head, Yajl::Encoder.encode(body)]
92
+ end
93
+
94
+ def self.response(resp)
95
+ resp.response = Yajl::Parser.parse(resp.response)
96
+ end
97
+ end
98
+
99
+ it "should use middleware to JSON encode and JSON decode the body" do
100
+ EventMachine.run {
101
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
102
+ conn.use JSONify
103
+
104
+ req = conn.post :body => {:ruby => :hash}
105
+ req.callback {
106
+ req.response_header.status.should == 200
107
+ req.response.should == {"ruby" => "hash"}
108
+ EventMachine.stop
109
+ }
110
+ }
111
+ end
112
+ end
113
+
114
+ end
data/spec/multi_spec.rb CHANGED
@@ -10,17 +10,13 @@ describe EventMachine::MultiRequest do
10
10
  multi = EventMachine::MultiRequest.new
11
11
 
12
12
  # add multiple requests to the multi-handler
13
- multi.add(EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get(:query => {:q => 'test'}))
14
- multi.add(EventMachine::HttpRequest.new('http://0.0.0.0:8083/').get(:timeout => 1))
13
+ multi.add(EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get(:query => {:q => 'test'}))
14
+ multi.add(EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get)
15
15
 
16
16
  multi.callback {
17
- # verify successful request
18
- multi.responses[:succeeded].size.should == 1
19
- multi.responses[:succeeded].first.response.should match(/test/)
20
-
21
- # verify invalid requests
22
- multi.responses[:failed].size.should == 1
23
- multi.responses[:failed].first.response_header.status.should == 0
17
+ multi.responses[:succeeded].size.should == 2
18
+ multi.responses[:succeeded][0].response.should match(/test|Hello/)
19
+ multi.responses[:succeeded][1].response.should match(/test|Hello/)
24
20
 
25
21
  EventMachine.stop
26
22
  }
@@ -29,40 +25,17 @@ describe EventMachine::MultiRequest do
29
25
 
30
26
  it "should accept multiple open connections and return once all of them are complete" do
31
27
  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)
28
+ http1 = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get(:query => {:q => 'test'})
29
+ http2 = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
34
30
 
35
31
  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
32
+ multi.responses[:succeeded].size.should == 2
33
+ multi.responses[:succeeded][0].response.should match(/test|Hello/)
34
+ multi.responses[:succeeded][1].response.should match(/test|Hello/)
41
35
 
42
36
  EventMachine.stop
43
37
  end
44
38
  }
45
39
  end
46
40
 
47
- it "should handle multiple mock requests" do
48
- EventMachine::MockHttpRequest.register_file('http://127.0.0.1:8080/', :get, {}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
49
- EventMachine::MockHttpRequest.register_file('http://0.0.0.0:8083/', :get, {}, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
50
-
51
- EventMachine.run {
52
-
53
- # create an instance of multi-request handler, and the requests themselves
54
- multi = EventMachine::MultiRequest.new
55
-
56
- # add multiple requests to the multi-handler
57
- multi.add(EventMachine::MockHttpRequest.new('http://127.0.0.1:8080/').get)
58
- multi.add(EventMachine::MockHttpRequest.new('http://0.0.0.0:8083/').get)
59
-
60
- multi.callback {
61
- # verify successful request
62
- multi.responses[:succeeded].size.should == 2
63
-
64
- EventMachine.stop
65
- }
66
- }
67
- end
68
- end
41
+ end