astro-em-http-request 0.2.3 → 0.2.6

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.
@@ -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