astro-em-http-request 0.2.3 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ module EventMachine
2
+ class MockHttpRequest < EventMachine::HttpRequest
3
+
4
+ include HttpEncoding
5
+
6
+ class FakeHttpClient < EventMachine::HttpClient
7
+
8
+ def setup(response, uri)
9
+ @uri = uri
10
+ receive_data(response)
11
+ succeed(self)
12
+ end
13
+
14
+ def unbind
15
+ end
16
+
17
+ end
18
+
19
+ @@registry = nil
20
+ @@registry_count = nil
21
+
22
+ def self.reset_counts!
23
+ @@registry_count = Hash.new{|h,k| h[k] = Hash.new(0)}
24
+ end
25
+
26
+ def self.reset_registry!
27
+ @@registry = Hash.new{|h,k| h[k] = {}}
28
+ end
29
+
30
+ reset_counts!
31
+ reset_registry!
32
+
33
+ @@pass_through_requests = true
34
+
35
+ def self.pass_through_requests=(pass_through_requests)
36
+ @@pass_through_requests = pass_through_requests
37
+ end
38
+
39
+ def self.pass_through_requests
40
+ @@pass_through_requests
41
+ end
42
+
43
+ def self.register(uri, method, data)
44
+ method = method.to_s.upcase
45
+ @@registry[uri][method] = data
46
+ end
47
+
48
+ def self.register_file(uri, method, file)
49
+ register(uri, method, File.read(file))
50
+ end
51
+
52
+ def self.count(uri, method)
53
+ method = method.to_s.upcase
54
+ @@registry_count[uri][method]
55
+ end
56
+
57
+ alias_method :real_send_request, :send_request
58
+
59
+ protected
60
+ def send_request(&blk)
61
+ query = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}#{encode_query(@uri.path, @options[:query], @uri.query)}"
62
+ if s = @@registry[query] and fake = s[@method]
63
+ @@registry_count[query][@method] += 1
64
+ client = FakeHttpClient.new(nil)
65
+ client.setup(fake, @uri)
66
+ client
67
+ elsif @@pass_through_requests
68
+ real_send_request
69
+ else
70
+ raise "this request #{query} for method #{@method} isn't registered, and pass_through_requests is current set to false"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -9,7 +9,6 @@ module EventMachine
9
9
  #
10
10
  # == Example
11
11
  #
12
- #
13
12
  # EventMachine.run {
14
13
  # http = EventMachine::HttpRequest.new('http://127.0.0.1/').get :query => {'keyname' => 'value'}
15
14
  #
@@ -24,6 +23,8 @@ module EventMachine
24
23
  #
25
24
 
26
25
  class HttpRequest
26
+
27
+ attr_reader :options, :method
27
28
 
28
29
  def initialize(host, headers = {})
29
30
  @headers = headers
@@ -45,44 +46,53 @@ module EventMachine
45
46
  # Called for each response body chunk (you may assume HTTP 200
46
47
  # OK then)
47
48
  #
48
- # host: String
49
- # Manually specify TCP connect host address, independent of
50
- # Host: header
51
- #
52
- # max_bytes: Integer
53
- # Explictly specify a maximum number of bytes that this connection may receive before shutting down.
54
- # Default : none.
55
- #
56
- # max_connection_duration: Integer
57
- # Explictly specify a maximum number of second for the connection. This is useful to avoid dealing with never ending connections (AtomStream-like)
58
- # Default : none.
59
- #
60
-
61
-
62
- def get options = {}; send_request(:get, options); end
63
- def head options = {}; send_request(:head, options); end
64
- def post options = {}; send_request(:post, options); end
49
+
50
+ def get options = {}, &blk; setup_request(:get, options, &blk); end
51
+ def head options = {}, &blk; setup_request(:head, options, &blk); end
52
+ def delete options = {}, &blk; setup_request(:delete, options, &blk); end
53
+ def put options = {}, &blk; setup_request(:put, options, &blk); end
54
+ def post options = {}, &blk; setup_request(:post, options, &blk); end
65
55
 
66
56
  protected
67
57
 
68
- def send_request(method, options)
58
+ def setup_request(method, options, &blk)
69
59
  raise ArgumentError, "invalid request path" unless /^\// === @uri.path
60
+ @options = options
61
+
62
+ if proxy = options[:proxy]
63
+ @host_to_connect = proxy[:host]
64
+ @port_to_connect = proxy[:port]
65
+ else
66
+ @host_to_connect = options[:host] || @uri.host
67
+ @port_to_connect = options[:port] || @uri.port
68
+ end
69
+
70
+ # default connect & inactivity timeouts
71
+ @options[:timeout] = 10 if not @options[:timeout]
70
72
 
71
- # default connect & inactivity timeouts
72
- options[:timeout] = 5 if not options[:timeout]
73
-
74
- # Make sure the port is set as Addressable::URI doesn't set the
75
- # port if it isn't there.
76
- @uri.port = @uri.port ? @uri.port : 80
77
- method = method.to_s.upcase
73
+ # Make sure the ports are set as Addressable::URI doesn't
74
+ # set the port if it isn't there
75
+ if @uri.scheme == "https"
76
+ @uri.port ||= 443
77
+ @port_to_connect ||= 443
78
+ else
79
+ @uri.port ||= 80
80
+ @port_to_connect ||= 80
81
+ end
82
+
83
+ @method = method.to_s.upcase
84
+ send_request(&blk)
85
+ end
86
+
87
+ def send_request(&blk)
78
88
  begin
79
- host = options[:host] || @uri.host
80
- raise ArgumentError, "invalid host" unless host
81
- raise ArgumentError, "invalid port" unless @uri.port
82
- EventMachine.connect(host, @uri.port, EventMachine::HttpClient) { |c|
89
+ raise ArgumentError, "invalid host" unless @host_to_connect
90
+ raise ArgumentError, "invalid port" unless @port_to_connect
91
+
92
+ EventMachine.connect(@host_to_connect, @port_to_connect, EventMachine::HttpClient) { |c|
83
93
  c.uri = @uri
84
- c.method = method
85
- c.options = options
94
+ c.method = @method
95
+ c.options = @options || {}
86
96
  if options.has_key?(:timeout) && options[:timeout]
87
97
  c.comm_inactivity_timeout = options[:timeout]
88
98
  c.pending_connect_timeout = options[:timeout] if c.respond_to?(:pending_connect_timeout)
@@ -90,11 +100,11 @@ module EventMachine
90
100
  c.comm_inactivity_timeout = 5
91
101
  c.pending_connect_timeout = 5 if c.respond_to?(:pending_connect_timeout)
92
102
  end
103
+ blk.call(c) unless blk.nil?
93
104
  }
94
- rescue RuntimeError => e
95
- raise e unless e.message == "no connection"
105
+ rescue EventMachine::ConnectionError => e
96
106
  conn = EventMachine::HttpClient.new("")
97
- conn.on_error("no connection", true)
107
+ conn.on_error(e.message, true)
98
108
  conn
99
109
  end
100
110
  end
@@ -0,0 +1,21 @@
1
+ HTTP/1.0 200 OK
2
+ Date: Mon, 16 Nov 2009 20:39:15 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=9454187d21c4a6a6:TM=1258403955:LM=1258403955:S=2-mf1n5oV5yAeT9-; expires=Wed, 16-Nov-2011 20:39:15 GMT; path=/; domain=.google.ca
7
+ Set-Cookie: NID=28=lvxxVdiBQkCetu_WFaUxLyB7qPlHXS5OdAGYTqge_laVlCKVN8VYYeVBh4bNZiK_Oan2gm8oP9GA-FrZfMPC3ZMHeNq37MG2JH8AIW9LYucU8brOeuggMEbLNNXuiWg4; expires=Tue, 18-May-2010 20:39:15 GMT; path=/; domain=.google.ca; HttpOnly
8
+ Server: gws
9
+ X-XSS-Protection: 0
10
+ X-Cache: MISS from .
11
+ Via: 1.0 .:80 (squid)
12
+ Connection: close
13
+
14
+ <!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>window.google={kEI:"eLgBS4LROqCQedKVvfUL",kEXPI:"17259,21329,21516,22107",kCSI:{e:"17259,21329,21516,22107",ei:"eLgBS4LROqCQedKVvfUL"},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};
15
+ window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{}catch(b){}window.google.jsrt_kill=1;
16
+ 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&&
17
+ window._gjuc())&&setTimeout(_gjp,500)};
18
+ window._gjp && _gjp()</script><style>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{font:17px arial,sans-serif;margin-bottom:.2em;vertical-align:bottom;}input{font-family:inherit}.lsb,.gac_sb{font-size:15px;height:1.85em!important;margin:.2em;}#gbar{height:22px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#guser{padding-bottom:7px !important;text-align:right}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb3,a.gb4{color:#00c !important}.gb3{text-decoration:none}</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/nav_logo7.png'" topmargin=3 marginheight=3><textarea id=csi style=display:none></textarea><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.ca/imghp?hl=en&tab=wi" class=gb1>Images</a> <a href="http://video.google.ca/?hl=en&tab=wv" class=gb1>Videos</a> <a href="http://maps.google.ca/maps?hl=en&tab=wl" class=gb1>Maps</a> <a href="http://news.google.ca/nwshp?hl=en&tab=wn" class=gb1>News</a> <a href="http://groups.google.ca/grphp?hl=en&tab=wg" class=gb1>Groups</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=gb3><u>more</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><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> | <a href="/preferences?hl=en" class=gb4>Search 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><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ca/images/logo.gif" width=276 id=logo onload="window.lol&&lol()"><br><br><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"><input autocomplete="off" maxlength=2048 name=q size=55 class=lst title="Google Search" value=""><br><input name=btnG type=submit value="Google Search" class=lsb><input name=btnI type=submit value="I'm Feeling Lucky" class=lsb></td><td nowrap width=25% align=left><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr><tr><td align=center colspan=3><font size=-1><span style="text-align:left">Search: <input id=all type=radio name=meta value="" checked><label for=all> the web </label> <input id=cty type=radio name=meta value="cr=countryCA"><label for=cty> pages from Canada </label> </span></font></td></tr></table></form><br><font size=-1>Google.ca offered in: <a href="http://www.google.ca/setprefs?sig=0_XtMil90_yvnNEW8dIglASaHCVhU=&hl=fr">fran?ais</a> </font><br><br><br><font size=-1><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></font><p><font size=-2>&copy;2009 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center><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/CgJlbhICY2EgACswCjhBQB0sKzAOOAksKzAYOAQsKzAlOMmIASwrMCY4BywrMCc4Aiw/n_sssePDGvc.js";(document.getElementById("xjsd")||document.body).appendChild(a)},0);
19
+ ;google.y.first.push(function(){google.ac.b=true;google.ac.i(document.f,document.f.q,'','')});google.xjs&&google.j&&google.j.xi&&google.j.xi()</script></div><script>(function(){
20
+ 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();
21
+ })();
@@ -0,0 +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
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'pp'
4
+
5
+ require 'lib/em-http'
6
+ require 'lib/em-http/mock'
7
+ require 'em-websocket'
@@ -0,0 +1,72 @@
1
+ require 'spec/helper'
2
+
3
+ describe 'em-http mock' do
4
+
5
+ before(:each) do
6
+ EventMachine::MockHttpRequest.reset_registry!
7
+ EventMachine::MockHttpRequest.reset_counts!
8
+ end
9
+
10
+ it "should serve a fake http request from a file" do
11
+ EventMachine::MockHttpRequest.register_file('http://www.google.ca:80/', :get, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
12
+ EM.run {
13
+
14
+ http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
15
+ http.errback { fail }
16
+ http.callback {
17
+ http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\n\n", 2).last
18
+ EventMachine.stop
19
+ }
20
+ }
21
+
22
+ EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get).should == 1
23
+ end
24
+
25
+ it "should serve a fake http request from a string" do
26
+ data = <<-HEREDOC
27
+ HTTP/1.0 200 OK
28
+ Date: Mon, 16 Nov 2009 20:39:15 GMT
29
+ Expires: -1
30
+ Cache-Control: private, max-age=0
31
+ Content-Type: text/html; charset=ISO-8859-1
32
+ Set-Cookie: PREF=ID=9454187d21c4a6a6:TM=1258403955:LM=1258403955:S=2-mf1n5oV5yAeT9-; expires=Wed, 16-Nov-2011 20:39:15 GMT; path=/; domain=.google.ca
33
+ Set-Cookie: NID=28=lvxxVdiBQkCetu_WFaUxLyB7qPlHXS5OdAGYTqge_laVlCKVN8VYYeVBh4bNZiK_Oan2gm8oP9GA-FrZfMPC3ZMHeNq37MG2JH8AIW9LYucU8brOeuggMEbLNNXuiWg4; expires=Tue, 18-May-2010 20:39:15 GMT; path=/; domain=.google.ca; HttpOnly
34
+ Server: gws
35
+ X-XSS-Protection: 0
36
+ X-Cache: MISS from .
37
+ Via: 1.0 .:80 (squid)
38
+ Connection: close
39
+
40
+ <!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>window.google={kEI:"eLgBS4LROqCQedKVvfUL",kEXPI:"17259,21329,21516,22107",kCSI:{e:"17259,21329,21516,22107",ei:"eLgBS4LROqCQedKVvfUL"},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};
41
+ window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{}catch(b){}window.google.jsrt_kill=1;
42
+ 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&&
43
+ window._gjuc())&&setTimeout(_gjp,500)};
44
+ window._gjp && _gjp()</script><style>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{font:17px arial,sans-serif;margin-bottom:.2em;vertical-align:bottom;}input{font-family:inherit}.lsb,.gac_sb{font-size:15px;height:1.85em!important;margin:.2em;}#gbar{height:22px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#guser{padding-bottom:7px !important;text-align:right}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb3,a.gb4{color:#00c !important}.gb3{text-decoration:none}</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/nav_logo7.png'" topmargin=3 marginheight=3><textarea id=csi style=display:none></textarea><div id=gbar><nobr><b class=gb1>Web</b> <a href="http://images.google.ca/imghp?hl=en&tab=wi" class=gb1>Images</a> <a href="http://video.google.ca/?hl=en&tab=wv" class=gb1>Videos</a> <a href="http://maps.google.ca/maps?hl=en&tab=wl" class=gb1>Maps</a> <a href="http://news.google.ca/nwshp?hl=en&tab=wn" class=gb1>News</a> <a href="http://groups.google.ca/grphp?hl=en&tab=wg" class=gb1>Groups</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=gb3><u>more</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><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> | <a href="/preferences?hl=en" class=gb4>Search 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><center><br clear=all id=lgpd><img alt="Google" height=110 src="/intl/en_ca/images/logo.gif" width=276 id=logo onload="window.lol&&lol()"><br><br><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"><input autocomplete="off" maxlength=2048 name=q size=55 class=lst title="Google Search" value=""><br><input name=btnG type=submit value="Google Search" class=lsb><input name=btnI type=submit value="I'm Feeling Lucky" class=lsb></td><td nowrap width=25% align=left><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?hl=en>Advanced Search</a><br>&nbsp;&nbsp;<a href=/language_tools?hl=en>Language Tools</a></font></td></tr><tr><td align=center colspan=3><font size=-1><span style="text-align:left">Search: <input id=all type=radio name=meta value="" checked><label for=all> the web </label> <input id=cty type=radio name=meta value="cr=countryCA"><label for=cty> pages from Canada </label> </span></font></td></tr></table></form><br><font size=-1>Google.ca offered in: <a href="http://www.google.ca/setprefs?sig=0_XtMil90_yvnNEW8dIglASaHCVhU=&hl=fr">fran?ais</a> </font><br><br><br><font size=-1><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></font><p><font size=-2>&copy;2009 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center><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/CgJlbhICY2EgACswCjhBQB0sKzAOOAksKzAYOAQsKzAlOMmIASwrMCY4BywrMCc4Aiw/n_sssePDGvc.js";(document.getElementById("xjsd")||document.body).appendChild(a)},0);
45
+ ;google.y.first.push(function(){google.ac.b=true;google.ac.i(document.f,document.f.q,'','')});google.xjs&&google.j&&google.j.xi&&google.j.xi()</script></div><script>(function(){
46
+ 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
+ })();
48
+ HEREDOC
49
+ EventMachine::MockHttpRequest.register('http://www.google.ca:80/', :get, data)
50
+ EventMachine.run {
51
+
52
+ http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
53
+ http.errback { fail }
54
+ http.callback {
55
+ http.response.should == data.split("\n\n", 2).last
56
+ EventMachine.stop
57
+ }
58
+ }
59
+
60
+ EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get).should == 1
61
+ end
62
+
63
+ it "should raise an exception if pass-thru is disabled" do
64
+ EventMachine::MockHttpRequest.pass_through_requests = false
65
+ EventMachine.run {
66
+ proc {
67
+ http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
68
+ }.should raise_error
69
+ EventMachine.stop
70
+ }
71
+ end
72
+ end
@@ -1,5 +1,5 @@
1
- require 'test/helper'
2
- require 'test/stallion'
1
+ require 'spec/helper'
2
+ require 'spec/stallion'
3
3
 
4
4
  describe EventMachine::MultiRequest do
5
5
 
@@ -11,7 +11,7 @@ describe EventMachine::MultiRequest do
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
- multi.add(EventMachine::HttpRequest.new('http://0.0.0.0/').get(:timeout => 1))
14
+ multi.add(EventMachine::HttpRequest.new('http://0.0.0.0:8083/').get(:timeout => 1))
15
15
 
16
16
  multi.callback {
17
17
  # verify successfull request
@@ -1,14 +1,14 @@
1
- require 'test/helper'
2
- require 'test/stallion'
3
- require 'test/stub_server'
4
-
1
+ require 'spec/helper'
2
+ require 'spec/stallion'
3
+ require 'spec/stub_server'
4
+
5
5
  describe EventMachine::HttpRequest do
6
6
 
7
7
  def failed
8
8
  EventMachine.stop
9
9
  fail
10
10
  end
11
-
11
+
12
12
  it "should fail GET on DNS timeout" do
13
13
  EventMachine.run {
14
14
  http = EventMachine::HttpRequest.new('http://127.1.1.1/').get :timeout => 1
@@ -26,7 +26,7 @@ describe EventMachine::HttpRequest do
26
26
  http.callback { failed }
27
27
  http.errback {
28
28
  http.response_header.status.should == 0
29
- http.errors.should match(/no connection/)
29
+ http.errors.should match(/unable to resolve server address/)
30
30
  EventMachine.stop
31
31
  }
32
32
  }
@@ -54,7 +54,7 @@ describe EventMachine::HttpRequest do
54
54
  }
55
55
  }
56
56
  end
57
-
57
+
58
58
  it "should perform successfull GET with a URI passed as argument" do
59
59
  EventMachine.run {
60
60
  uri = URI.parse('http://127.0.0.1:8080/')
@@ -66,7 +66,7 @@ describe EventMachine::HttpRequest do
66
66
  http.response.should match(/Hello/)
67
67
  EventMachine.stop
68
68
  }
69
- }
69
+ }
70
70
  end
71
71
 
72
72
  it "should perform successfull HEAD with a URI passed as argument" do
@@ -80,7 +80,22 @@ describe EventMachine::HttpRequest do
80
80
  http.response.should == ""
81
81
  EventMachine.stop
82
82
  }
83
- }
83
+ }
84
+ end
85
+
86
+ # should be no different than a GET
87
+ it "should perform successfull DELETE with a URI passed as argument" do
88
+ EventMachine.run {
89
+ uri = URI.parse('http://127.0.0.1:8080/')
90
+ http = EventMachine::HttpRequest.new(uri).delete
91
+
92
+ http.errback { failed }
93
+ http.callback {
94
+ http.response_header.status.should == 200
95
+ http.response.should == ""
96
+ EventMachine.stop
97
+ }
98
+ }
84
99
  end
85
100
 
86
101
  it "should return 404 on invalid path" do
@@ -88,7 +103,7 @@ describe EventMachine::HttpRequest do
88
103
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/fail').get
89
104
 
90
105
  http.errback { failed }
91
- http.callback {
106
+ http.callback {
92
107
  http.response_header.status.should == 404
93
108
  EventMachine.stop
94
109
  }
@@ -134,6 +149,20 @@ describe EventMachine::HttpRequest do
134
149
  }
135
150
  end
136
151
 
152
+ # should be no different than a POST
153
+ it "should perform successfull PUT" do
154
+ EventMachine.run {
155
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').put :body => "data"
156
+
157
+ http.errback { failed }
158
+ http.callback {
159
+ http.response_header.status.should == 200
160
+ http.response.should match(/data/)
161
+ EventMachine.stop
162
+ }
163
+ }
164
+ end
165
+
137
166
  it "should perform successfull POST" do
138
167
  EventMachine.run {
139
168
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post :body => "data"
@@ -154,13 +183,13 @@ describe EventMachine::HttpRequest do
154
183
  http.errback { failed }
155
184
  http.callback {
156
185
  http.response_header.status.should == 200
157
-
186
+
158
187
  http.response.should match(/key1=1&key2\[0\]=2&key2\[1\]=3/)
159
188
  EventMachine.stop
160
189
  }
161
190
  }
162
191
  end
163
-
192
+
164
193
  it "should perform successfull POST with Ruby Hash/Array as params and with the correct content length" do
165
194
  EventMachine.run {
166
195
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_length').post :body => {"key1" => "data1"}
@@ -168,7 +197,7 @@ describe EventMachine::HttpRequest do
168
197
  http.errback { failed }
169
198
  http.callback {
170
199
  http.response_header.status.should == 200
171
-
200
+
172
201
  http.response.to_i.should == 10
173
202
  EventMachine.stop
174
203
  }
@@ -214,6 +243,20 @@ describe EventMachine::HttpRequest do
214
243
  }
215
244
  end
216
245
 
246
+ it "should send proper OAuth auth header" do
247
+ EventMachine.run {
248
+ oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
249
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/oauth_auth').get :head => {'authorization' => oauth_header}
250
+
251
+ http.errback { failed }
252
+ http.callback {
253
+ http.response_header.status.should == 200
254
+ http.response.should == oauth_header
255
+ EventMachine.stop
256
+ }
257
+ }
258
+ end
259
+
217
260
  it "should work with keep-alive servers" do
218
261
  EventMachine.run {
219
262
 
@@ -275,7 +318,7 @@ describe EventMachine::HttpRequest do
275
318
  it "should optionally pass the response body progressively" do
276
319
  EventMachine.run {
277
320
  body = ''
278
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
321
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
279
322
 
280
323
  http.errback { failed }
281
324
  http.stream { |chunk| body += chunk }
@@ -418,7 +461,7 @@ describe EventMachine::HttpRequest do
418
461
  }
419
462
  end
420
463
 
421
- it "should not override content-type when passing in ruby hash/array for body" do
464
+ it "should not override content-type when passing in ruby hash/array for body" do
422
465
  EventMachine.run {
423
466
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post({
424
467
  :body => {:a => :b}, :head => {'content-type' => 'text'}})
@@ -437,34 +480,153 @@ describe EventMachine::HttpRequest do
437
480
  EventMachine.run {
438
481
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/relative-location').get
439
482
 
483
+ http.errback { failed }
484
+ http.callback {
485
+ http.response_header['LOCATION'].should == 'http://127.0.0.1:8080/forwarded'
486
+ EventMachine.stop
487
+ }
488
+ }
489
+ end
490
+
491
+ it 'should let you pass a block to be called once the client is created' do
492
+ client = nil
493
+ EventMachine.run {
494
+ request = EventMachine::HttpRequest.new('http://127.0.0.1:8080/')
495
+ http = request.post { |c|
496
+ c.options[:body] = {:callback_run => 'yes'}
497
+ client = c
498
+ }
499
+ http.errback { failed }
500
+ http.callback {
501
+ client.should be_kind_of(EventMachine::HttpClient)
502
+ http.response_header.status.should == 200
503
+ http.response.should match(/callback_run=yes/)
504
+ client.normalize_uri.should == Addressable::URI.parse('http://127.0.0.1:8080/')
505
+ EventMachine.stop
506
+ }
507
+ }
508
+ end
509
+
510
+ it "should retrieve multiple cookies" do
511
+ EventMachine::MockHttpRequest.register_file('http://www.google.ca:80/', :get, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
512
+ EventMachine.run {
513
+
514
+ http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
515
+ http.errback { fail }
516
+ http.callback {
517
+ c1 = "PREF=ID=9454187d21c4a6a6:TM=1258403955:LM=1258403955:S=2-mf1n5oV5yAeT9-; expires=Wed, 16-Nov-2011 20:39:15 GMT; path=/; domain=.google.ca"
518
+ c2 = "NID=28=lvxxVdiBQkCetu_WFaUxLyB7qPlHXS5OdAGYTqge_laVlCKVN8VYYeVBh4bNZiK_Oan2gm8oP9GA-FrZfMPC3ZMHeNq37MG2JH8AIW9LYucU8brOeuggMEbLNNXuiWg4; expires=Tue, 18-May-2010 20:39:15 GMT; path=/; domain=.google.ca; HttpOnly"
519
+ http.response_header.cookie.should == [c1, c2]
520
+
521
+ EventMachine.stop
522
+ }
523
+ }
524
+
525
+ EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get).should == 1
526
+ end
527
+
528
+ context "connections via proxy" do
529
+
530
+ it "should work with proxy servers" do
531
+ EventMachine.run {
532
+
533
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :proxy => {:host => '127.0.0.1', :port => 8082}
534
+
535
+ http.errback { p http.inspect; failed }
536
+ http.callback {
537
+ http.response_header.status.should == 200
538
+ http.response.should == 'Hello, World!'
539
+ EventMachine.stop
540
+ }
541
+ }
542
+ end
543
+
544
+ it "should proxy POST data" do
545
+ EventMachine.run {
546
+
547
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
548
+ :body => "data", :proxy => {:host => '127.0.0.1', :port => 8082}})
549
+
440
550
  http.errback { failed }
441
551
  http.callback {
442
- http.response_header['LOCATION'].should == 'http://127.0.0.1:8080/forwarded'
552
+ http.response_header.status.should == 200
553
+ http.response.should match(/data/)
443
554
  EventMachine.stop
444
555
  }
445
556
  }
557
+ end
446
558
  end
447
559
 
448
- it "should return an error if the response size exceeds the :max_bytes option" do
449
- EventMachine.run {
450
- http = EventMachine::HttpRequest.new("http://127.0.0.1:8080/").get(:max_bytes => 3)
451
- http.errback { |http|
452
- http.errors.should match /Bytes received exceeds limit/
560
+ context "websocket connection" do
561
+ # Spec: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-55
562
+ #
563
+ # ws.onopen = http.callback
564
+ # ws.onmessage = http.stream { |msg| }
565
+ # ws.errback = no connection
566
+ #
567
+
568
+ it "should invoke errback on failed upgrade" do
569
+ EventMachine.run {
570
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:8080/').get :timeout => 0
571
+
572
+ http.callback { failed }
573
+ http.errback {
574
+ http.response_header.status.should == 200
453
575
  EventMachine.stop
454
576
  }
455
- http.callback { failed }
456
577
  }
457
- end
578
+ end
458
579
 
459
- it "should return an error if the connection duration exceeds the :max_connection_duration option" do
460
- EventMachine.run {
461
- http = EventMachine::HttpRequest.new("http://updates.sixapart.com/atom-stream.xml").get(:max_connection_duration => 3)
462
- http.errback { |http|
463
- http.errors.should match /Max Connection Duration Exceeded/
580
+ it "should complete websocket handshake and transfer data from client to server and back" do
581
+ EventMachine.run {
582
+ MSG = "hello bi-directional data exchange"
583
+
584
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8085) do |ws|
585
+ ws.onmessage {|msg| ws.send msg}
586
+ end
587
+
588
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:8085/').get :timeout => 1
589
+ http.errback { failed }
590
+ http.callback {
591
+ http.response_header.status.should == 101
592
+ http.response_header['CONNECTION'].should match(/Upgrade/)
593
+ http.response_header['UPGRADE'].should match(/WebSocket/)
594
+
595
+ # push should only be invoked after handshake is complete
596
+ http.send(MSG)
597
+ }
598
+
599
+ http.stream { |chunk|
600
+ chunk.should == MSG
464
601
  EventMachine.stop
465
602
  }
466
- http.callback { failed }
467
603
  }
468
- end
604
+ end
469
605
 
606
+ it "should split multiple messages from websocket server into separate stream callbacks" do
607
+ EM.run do
608
+ messages = %w[1 2]
609
+ recieved = []
610
+
611
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8085) do |ws|
612
+ ws.onopen {
613
+ ws.send messages[0]
614
+ ws.send messages[1]
615
+ }
616
+ end
617
+
618
+ EventMachine.add_timer(0.1) do
619
+ http = EventMachine::HttpRequest.new('ws://127.0.0.1:8085/').get :timeout => 0
620
+ http.errback { failed }
621
+ http.callback { http.response_header.status.should == 101 }
622
+ http.stream {|msg|
623
+ msg.should == messages[recieved.size]
624
+ recieved.push msg
625
+
626
+ EventMachine.stop if recieved.size == messages.size
627
+ }
628
+ end
629
+ end
630
+ end
631
+ end
470
632
  end