em-http-request 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of em-http-request might be problematic. Click here for more details.

data/README.rdoc CHANGED
@@ -3,8 +3,10 @@
3
3
  EventMachine based HTTP Request interface. Supports streaming response processing, uses Ragel HTTP parser.
4
4
  - Simple interface for single & parallel requests via deferred callbacks
5
5
  - Automatic gzip & deflate decoding
6
- - Basic-Auth support
7
- - Custom timeouts
6
+ - Basic-Auth & OAuth support
7
+ - Custom timeouts
8
+ - Proxy support (with SSL Tunneling)
9
+ - Bi-directional communication with web-socket services
8
10
 
9
11
  Screencast / Demo of using EM-HTTP-Request:
10
12
  - http://everburning.com/news/eventmachine-screencast-em-http-request/
@@ -35,6 +37,7 @@ Screencast / Demo of using EM-HTTP-Request:
35
37
  }
36
38
 
37
39
  == Multi request example
40
+ Fire and wait for multiple requess to complete via the MultiRequest interface.
38
41
 
39
42
  EventMachine.run {
40
43
  multi = EventMachine::MultiRequest.new
@@ -52,6 +55,7 @@ Screencast / Demo of using EM-HTTP-Request:
52
55
  }
53
56
 
54
57
  == Basic-Auth example
58
+ Full basic author support. For OAuth, check examples/oauth-tweet.rb file.
55
59
 
56
60
  EventMachine.run {
57
61
  http = EventMachine::HttpRequest.new('http://www.website.com/').get :head => {'authorization' => ['user', 'pass']}
@@ -63,17 +67,6 @@ Screencast / Demo of using EM-HTTP-Request:
63
67
  }
64
68
  }
65
69
 
66
- == OAuth example
67
-
68
- EventMachine.run {
69
- http = EventMachine::HttpRequest.new('http://www.website.com/').get :head => {'authorization' => 'OAuth oauth_nonce=...'}
70
- http.errback { failed }
71
- http.callback {
72
- p http.response_header
73
- EventMachine.stop
74
- }
75
- }
76
-
77
70
 
78
71
  == POST example
79
72
  EventMachine.run {
@@ -84,6 +77,9 @@ Screencast / Demo of using EM-HTTP-Request:
84
77
  }
85
78
 
86
79
  == Streaming body processing
80
+ Allows you to consume an HTTP stream of content in real-time. Each time a new piece of conent is pushed
81
+ to the client, it is passed to the stream callback for you to operate on.
82
+
87
83
  EventMachine.run {
88
84
  http = EventMachine::HttpRequest.new('http://www.website.com/').get
89
85
  http.stream { |chunk| print chunk }
@@ -92,10 +88,33 @@ Screencast / Demo of using EM-HTTP-Request:
92
88
  }
93
89
 
94
90
  == Proxy example
91
+ Full transparent proxy support with support for SSL tunneling.
92
+
95
93
  EventMachine.run {
96
- http1 = EventMachine::HttpRequest.new('http://www.website.com/').get :proxy => {
94
+ http = EventMachine::HttpRequest.new('http://www.website.com/').get :proxy => {
97
95
  :host => 'www.myproxy.com',
98
96
  :port => 8080,
99
97
  :authorization => ['username', 'password'] # authorization is optional
98
+ }
99
+
100
+ == WebSocket example
101
+ Bi-directional communication with WebSockets: simply pass in a ws:// resource and the client will
102
+ negotiate the connection upgrade for you. On successfull handshake the callback is invoked, and
103
+ any incoming messages will be passed to the stream callback. The client can also send data to the
104
+ server at will by calling the "send" method!
105
+ - http://www.igvita.com/2009/12/22/ruby-websockets-tcp-for-the-browser/
106
+
107
+ EventMachine.run {
108
+ http = EventMachine::HttpRequest.new("ws://yourservice.com/websocket").get :timeout => 0
109
+
110
+ http.errback { puts "oops" }
111
+ http.callback {
112
+ puts "WebSocket connected!"
113
+ http.send("Hello client")
100
114
  }
101
115
 
116
+ http.stream { |msg|
117
+ puts "Recieved: #{msg}"
118
+ http.send "Pong: #{msg}"
119
+ }
120
+ }
data/Rakefile CHANGED
@@ -36,7 +36,6 @@ end
36
36
 
37
37
  task :spec do
38
38
  sh 'spec test/test_*.rb'
39
- sh 'spec test/mock/test_*.rb'
40
39
  end
41
40
 
42
41
  def make(makedir)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.5
1
+ 0.2.6
@@ -0,0 +1,50 @@
1
+ # Courtesy of Darcy Laycock:
2
+ # http://gist.github.com/265261
3
+ #
4
+
5
+ require 'rubygems'
6
+
7
+ require 'em-http'
8
+ require 'oauth'
9
+
10
+ # At a minimum, require 'oauth/request_proxy/em_http_request'
11
+ # for this example, we'll use Net::HTTP like support.
12
+ require 'oauth/client/em_http'
13
+
14
+ # You need two things: an oauth consumer and an access token.
15
+ # You need to generate an access token, I suggest looking elsewhere how to do that or wait for a full tutorial.
16
+ # For a consumer key / consumer secret, signup for an app at:
17
+ # http://twitter.com/apps/new
18
+
19
+ # Edit in your details.
20
+ CONSUMER_KEY = ""
21
+ CONSUMER_SECRET = ""
22
+ ACCESS_TOKEN = ""
23
+ ACCESS_TOKEN_SECRET = ""
24
+
25
+ def twitter_oauth_consumer
26
+ @twitter_oauth_consumer ||= OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, :site => "http://twitter.com")
27
+ end
28
+
29
+ def twitter_oauth_access_token
30
+ @twitter_oauth_access_token ||= OAuth::AccessToken.new(twitter_oauth_consumer, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
31
+ end
32
+
33
+ EM.run do
34
+
35
+ request = EventMachine::HttpRequest.new('http://twitter.com/statuses/update.json')
36
+ http = request.post(:body => {'status' => 'Hello Twitter from em-http-request with OAuth'}, :head => {"Content-Type" => "application/x-www-form-urlencoded"}) do |client|
37
+ twitter_oauth_consumer.sign!(client, twitter_oauth_access_token)
38
+ end
39
+
40
+ http.callback do
41
+ puts "Response: #{http.response} (Code: #{http.response_header.status})"
42
+ EM.stop_event_loop
43
+ end
44
+
45
+ http.errback do
46
+ puts "Failed to post"
47
+ EM.stop_event_loop
48
+ end
49
+
50
+ end
@@ -27,6 +27,7 @@ void client_http_field(void *data, const char *field, size_t flen, const char *v
27
27
  VALUE req = (VALUE)data;
28
28
  VALUE v = Qnil;
29
29
  VALUE f = Qnil;
30
+ VALUE el = Qnil;
30
31
 
31
32
  v = rb_str_new(value, vlen);
32
33
  f = rb_str_new(field, flen);
@@ -41,7 +42,18 @@ void client_http_field(void *data, const char *field, size_t flen, const char *v
41
42
  }
42
43
  }
43
44
 
44
- rb_hash_aset(req, f, v);
45
+ el = rb_hash_lookup(req, f);
46
+ switch(TYPE(el)) {
47
+ case T_ARRAY:
48
+ rb_ary_push(el, v);
49
+ break;
50
+ case T_STRING:
51
+ rb_hash_aset(req, f, rb_ary_new3(2, el, v));
52
+ break;
53
+ default:
54
+ rb_hash_aset(req, f, v);
55
+ break;
56
+ }
45
57
  }
46
58
 
47
59
  void client_reason_phrase(void *data, const char *at, size_t length)
@@ -212,7 +212,7 @@ module EventMachine
212
212
  # if connecting via proxy, then state will be :proxy_connected,
213
213
  # indicating successful tunnel. from here, initiate normal http
214
214
  # exchange
215
- else
215
+ else
216
216
  @state = :response_header
217
217
 
218
218
  ssl = @options[:tls] || @options[:ssl] || {}
@@ -246,14 +246,43 @@ module EventMachine
246
246
  @stream = blk
247
247
  end
248
248
 
249
+ # raw data push from the client (WebSocket) should
250
+ # only be invoked after handshake, otherwise it will
251
+ # inject data into the header exchange
252
+ #
253
+ # frames need to start with 0x00-0x7f byte and end with
254
+ # an 0xFF byte. Per spec, we can also set the first
255
+ # byte to a value betweent 0x80 and 0xFF, followed by
256
+ # a leading length indicator
257
+ def send(data)
258
+ if @state == :websocket
259
+ send_data("\x00#{data}\xff")
260
+ end
261
+ end
262
+
249
263
  def normalize_body
250
- if @options[:body].is_a? Hash
251
- @options[:body].to_params
252
- else
253
- @options[:body]
264
+ @normalized_body ||= begin
265
+ if @options[:body].is_a? Hash
266
+ @options[:body].to_params
267
+ else
268
+ @options[:body]
269
+ end
270
+ end
271
+ end
272
+
273
+ def normalize_uri
274
+ @normalized_uri ||= begin
275
+ uri = @uri.dup
276
+ encoded_query = encode_query(@uri.path, @options[:query], @uri.query)
277
+ path, query = encoded_query.split("?", 2)
278
+ uri.query = query unless encoded_query.empty?
279
+ uri.path = path
280
+ uri
254
281
  end
255
282
  end
256
-
283
+
284
+ def websocket?; @uri.scheme == 'ws'; end
285
+
257
286
  def send_request_header
258
287
  query = @options[:query]
259
288
  head = @options[:head] ? munge_header_keys(@options[:head]) : {}
@@ -267,7 +296,12 @@ module EventMachine
267
296
  head = proxy[:head] ? munge_header_keys(proxy[:head]) : {}
268
297
  head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization]
269
298
  request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"]
270
-
299
+
300
+ elsif websocket?
301
+ head['upgrade'] = 'WebSocket'
302
+ head['connection'] = 'Upgrade'
303
+ head['origin'] = @options[:origin] || @uri.host
304
+
271
305
  else
272
306
  # Set the Content-Length if body is given
273
307
  head['content-length'] = body.length if body
@@ -283,7 +317,7 @@ module EventMachine
283
317
  end
284
318
  end
285
319
 
286
- # Set the Host header if it hasn't been specified already
320
+ # Set the Host header if it hasn't been specified already
287
321
  head['host'] ||= encode_host
288
322
 
289
323
  # Set the User-Agent if it hasn't been specified
@@ -357,6 +391,8 @@ module EventMachine
357
391
  process_response_footer
358
392
  when :body
359
393
  process_body
394
+ when :websocket
395
+ process_websocket
360
396
  when :finished, :invalid
361
397
  break
362
398
  else raise RuntimeError, "invalid state: #{@state}"
@@ -435,7 +471,15 @@ module EventMachine
435
471
  on_request_complete
436
472
  end
437
473
 
438
- if @response_header.chunked_encoding?
474
+ if websocket?
475
+ if @response_header.status == 101
476
+ @state = :websocket
477
+ succeed
478
+ else
479
+ fail "websocket handshake failed"
480
+ end
481
+
482
+ elsif @response_header.chunked_encoding?
439
483
  @state = :chunk_header
440
484
  elsif @response_header.content_length
441
485
  @state = :body
@@ -554,7 +598,23 @@ module EventMachine
554
598
  false
555
599
  end
556
600
 
557
-
601
+ def process_websocket
602
+ return false if @data.empty?
603
+
604
+ # slice the message out of the buffer and pass in
605
+ # for processing, and buffer data otherwise
606
+ buffer = @data.read
607
+ while msg = buffer.slice!(/\000([^\377]*)\377/)
608
+ msg.gsub!(/^\x00|\xff$/, '')
609
+ @stream.call(msg)
610
+ end
611
+
612
+ # store remainder if message boundary has not yet
613
+ # been recieved
614
+ @data << buffer if not buffer.empty?
615
+
616
+ false
617
+ end
558
618
  end
559
619
 
560
620
  end
data/lib/em-http/mock.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  module EventMachine
2
- class HttpRequest
2
+ class MockHttpRequest < EventMachine::HttpRequest
3
3
 
4
4
  include HttpEncoding
5
5
 
6
6
  class FakeHttpClient < EventMachine::HttpClient
7
7
 
8
- def setup(response)
8
+ def setup(response, uri)
9
+ @uri = uri
9
10
  receive_data(response)
10
11
  succeed(self)
11
12
  end
@@ -56,12 +57,12 @@ module EventMachine
56
57
  alias_method :real_send_request, :send_request
57
58
 
58
59
  protected
59
- def send_request
60
+ def send_request(&blk)
60
61
  query = "#{@uri.scheme}://#{@uri.host}:#{@uri.port}#{encode_query(@uri.path, @options[:query], @uri.query)}"
61
62
  if s = @@registry[query] and fake = s[@method]
62
63
  @@registry_count[query][@method] += 1
63
64
  client = FakeHttpClient.new(nil)
64
- client.setup(fake)
65
+ client.setup(fake, @uri)
65
66
  client
66
67
  elsif @@pass_through_requests
67
68
  real_send_request
@@ -47,15 +47,15 @@ module EventMachine
47
47
  # OK then)
48
48
  #
49
49
 
50
- def get options = {}; setup_request(:get, options); end
51
- def head options = {}; setup_request(:head, options); end
52
- def delete options = {}; setup_request(:delete, options); end
53
- def put options = {}; setup_request(:put, options); end
54
- def post options = {}; setup_request(:post, options); end
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
55
55
 
56
56
  protected
57
57
 
58
- def setup_request(method, options)
58
+ def setup_request(method, options, &blk)
59
59
  raise ArgumentError, "invalid request path" unless /^\// === @uri.path
60
60
  @options = options
61
61
 
@@ -76,10 +76,10 @@ module EventMachine
76
76
  @port_to_connect ||= 80
77
77
 
78
78
  @method = method.to_s.upcase
79
- send_request
79
+ send_request(&blk)
80
80
  end
81
81
 
82
- def send_request
82
+ def send_request(&blk)
83
83
  begin
84
84
  EventMachine.connect(@host_to_connect, @port_to_connect, EventMachine::HttpClient) { |c|
85
85
  c.uri = @uri
@@ -87,6 +87,7 @@ module EventMachine
87
87
  c.options = @options
88
88
  c.comm_inactivity_timeout = @options[:timeout]
89
89
  c.pending_connect_timeout = @options[:timeout]
90
+ blk.call(c) unless blk.nil?
90
91
  }
91
92
  rescue EventMachine::ConnectionError => e
92
93
  conn = EventMachine::HttpClient.new("")
File without changes
data/spec/hash_spec.rb ADDED
@@ -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
data/spec/helper.rb ADDED
@@ -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'
@@ -1,32 +1,25 @@
1
- require 'rubygems'
2
- require 'spec'
3
- require 'pp'
4
-
5
- require 'lib/em-http'
6
- require 'lib/em-http/mock'
1
+ require 'spec/helper'
7
2
 
8
3
  describe 'em-http mock' do
9
4
 
10
5
  before(:each) do
11
- EM::HttpRequest.reset_registry!
12
- EM::HttpRequest.reset_counts!
6
+ EventMachine::MockHttpRequest.reset_registry!
7
+ EventMachine::MockHttpRequest.reset_counts!
13
8
  end
14
9
 
15
10
  it "should serve a fake http request from a file" do
16
- EM::HttpRequest.register_file('http://www.google.ca:80/', :get, File.join(File.dirname(__FILE__), 'fixtures', 'google.ca'))
17
- EM.run do
18
- http = EM::HttpRequest.new('http://www.google.ca/').get
19
- http.callback 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 {
20
17
  http.response.should == File.read(File.join(File.dirname(__FILE__), 'fixtures', 'google.ca')).split("\n\n", 2).last
21
- EM.stop
22
- end
23
- http.errback do
24
- fail
25
- EM.stop
26
- end
27
- end
18
+ EventMachine.stop
19
+ }
20
+ }
28
21
 
29
- EM::HttpRequest.count('http://www.google.ca:80/', :get).should == 1
22
+ EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get).should == 1
30
23
  end
31
24
 
32
25
  it "should serve a fake http request from a string" do
@@ -52,32 +45,28 @@ window._gjp && _gjp()</script><style>td{line-height:.8em;}.gac_m td{line-height:
52
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(){
53
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();
54
47
  })();
55
- HEREDOC
56
- EM::HttpRequest.register('http://www.google.ca:80/', :get, data)
57
- EM.run do
58
- http = EM::HttpRequest.new('http://www.google.ca/').get
59
- http.callback do
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 {
60
55
  http.response.should == data.split("\n\n", 2).last
61
- EM.stop
62
- end
63
- http.errback do
64
- fail
65
- EM.stop
66
- end
67
- end
56
+ EventMachine.stop
57
+ }
58
+ }
68
59
 
69
- EM::HttpRequest.count('http://www.google.ca:80/', :get).should == 1
60
+ EventMachine::MockHttpRequest.count('http://www.google.ca:80/', :get).should == 1
70
61
  end
71
62
 
72
63
  it "should raise an exception if pass-thru is disabled" do
73
- EM::HttpRequest.pass_through_requests = false
74
- EM.run do
64
+ EventMachine::MockHttpRequest.pass_through_requests = false
65
+ EventMachine.run {
75
66
  proc {
76
- http = EM::HttpRequest.new('http://www.google.ca/').get
67
+ http = EventMachine::MockHttpRequest.new('http://www.google.ca/').get
77
68
  }.should raise_error
78
- EM.stop
79
- end
80
-
69
+ EventMachine.stop
70
+ }
81
71
  end
82
-
83
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
 
@@ -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
@@ -17,7 +17,7 @@ describe EventMachine::HttpRequest do
17
17
  http.response_header.status.should == 0
18
18
  EventMachine.stop
19
19
  }
20
- }
20
+ }
21
21
  end
22
22
 
23
23
  it "should fail GET on invalid host" do
@@ -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,9 +80,9 @@ describe EventMachine::HttpRequest do
80
80
  http.response.should == ""
81
81
  EventMachine.stop
82
82
  }
83
- }
83
+ }
84
84
  end
85
-
85
+
86
86
  # should be no different than a GET
87
87
  it "should perform successfull DELETE with a URI passed as argument" do
88
88
  EventMachine.run {
@@ -95,7 +95,7 @@ describe EventMachine::HttpRequest do
95
95
  http.response.should == ""
96
96
  EventMachine.stop
97
97
  }
98
- }
98
+ }
99
99
  end
100
100
 
101
101
  it "should return 404 on invalid path" do
@@ -103,7 +103,7 @@ describe EventMachine::HttpRequest do
103
103
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/fail').get
104
104
 
105
105
  http.errback { failed }
106
- http.callback {
106
+ http.callback {
107
107
  http.response_header.status.should == 404
108
108
  EventMachine.stop
109
109
  }
@@ -183,13 +183,13 @@ describe EventMachine::HttpRequest do
183
183
  http.errback { failed }
184
184
  http.callback {
185
185
  http.response_header.status.should == 200
186
-
186
+
187
187
  http.response.should match(/key1=1&key2\[0\]=2&key2\[1\]=3/)
188
188
  EventMachine.stop
189
189
  }
190
190
  }
191
191
  end
192
-
192
+
193
193
  it "should perform successfull POST with Ruby Hash/Array as params and with the correct content length" do
194
194
  EventMachine.run {
195
195
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_length').post :body => {"key1" => "data1"}
@@ -197,7 +197,7 @@ describe EventMachine::HttpRequest do
197
197
  http.errback { failed }
198
198
  http.callback {
199
199
  http.response_header.status.should == 200
200
-
200
+
201
201
  http.response.to_i.should == 10
202
202
  EventMachine.stop
203
203
  }
@@ -223,7 +223,7 @@ describe EventMachine::HttpRequest do
223
223
  http = EventMachine::HttpRequest.new('http://digg.com/').get
224
224
 
225
225
  http.errback { failed }
226
- http.callback {
226
+ http.callback {
227
227
  http.response_header.status.should == 200
228
228
  EventMachine.stop
229
229
  }
@@ -242,8 +242,8 @@ describe EventMachine::HttpRequest do
242
242
  }
243
243
  }
244
244
  end
245
-
246
- it "should send proper OAuth auth header" do
245
+
246
+ it "should send proper OAuth auth header" do
247
247
  EventMachine.run {
248
248
  oauth_header = 'OAuth oauth_nonce="oqwgSYFUD87MHmJJDv7bQqOF2EPnVus7Wkqj5duNByU", b=c, d=e'
249
249
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/oauth_auth').get :head => {'authorization' => oauth_header}
@@ -254,7 +254,7 @@ describe EventMachine::HttpRequest do
254
254
  http.response.should == oauth_header
255
255
  EventMachine.stop
256
256
  }
257
- }
257
+ }
258
258
  end
259
259
 
260
260
  it "should work with keep-alive servers" do
@@ -318,7 +318,7 @@ describe EventMachine::HttpRequest do
318
318
  it "should optionally pass the response body progressively" do
319
319
  EventMachine.run {
320
320
  body = ''
321
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
321
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get
322
322
 
323
323
  http.errback { failed }
324
324
  http.stream { |chunk| body += chunk }
@@ -461,7 +461,7 @@ describe EventMachine::HttpRequest do
461
461
  }
462
462
  end
463
463
 
464
- 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
465
465
  EventMachine.run {
466
466
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/echo_content_type').post({
467
467
  :body => {:a => :b}, :head => {'content-type' => 'text'}})
@@ -480,44 +480,153 @@ describe EventMachine::HttpRequest do
480
480
  EventMachine.run {
481
481
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/relative-location').get
482
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
- }
483
+ http.errback { failed }
484
+ http.callback {
485
+ http.response_header['LOCATION'].should == 'http://127.0.0.1:8080/forwarded'
486
+ EventMachine.stop
488
487
  }
488
+ }
489
489
  end
490
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
+
491
528
  context "connections via proxy" do
492
-
529
+
493
530
  it "should work with proxy servers" do
494
531
  EventMachine.run {
495
532
 
496
533
  http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').get :proxy => {:host => '127.0.0.1', :port => 8082}
497
534
 
498
535
  http.errback { p http.inspect; failed }
499
- http.callback {
536
+ http.callback {
500
537
  http.response_header.status.should == 200
501
538
  http.response.should == 'Hello, World!'
502
539
  EventMachine.stop
503
540
  }
504
541
  }
505
- end
542
+ end
506
543
 
507
544
  it "should proxy POST data" do
508
545
  EventMachine.run {
509
546
 
510
- http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
511
- :body => "data", :proxy => {:host => '127.0.0.1', :port => 8082}})
547
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8080/').post({
548
+ :body => "data", :proxy => {:host => '127.0.0.1', :port => 8082}})
512
549
 
513
- http.errback { failed }
514
- http.callback {
515
- http.response_header.status.should == 200
516
- http.response.should match(/data/)
517
- EventMachine.stop
518
- }
519
- }
520
- end
521
-
550
+ http.errback { failed }
551
+ http.callback {
552
+ http.response_header.status.should == 200
553
+ http.response.should match(/data/)
554
+ EventMachine.stop
555
+ }
556
+ }
557
+ end
558
+ end
559
+
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
575
+ EventMachine.stop
576
+ }
577
+ }
578
+ end
579
+
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
601
+ EventMachine.stop
602
+ }
603
+ }
604
+ end
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
522
631
  end
523
- end
632
+ end
File without changes
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-http-request
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Grigorik
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-12 00:00:00 -05:00
12
+ date: 2009-12-31 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -50,6 +50,7 @@ files:
50
50
  - VERSION
51
51
  - examples/fetch.rb
52
52
  - examples/fibered-http.rb
53
+ - examples/oauth-tweet.rb
53
54
  - ext/buffer/em_buffer.c
54
55
  - ext/buffer/extconf.rb
55
56
  - ext/http11_client/ext_help.h
@@ -65,14 +66,14 @@ files:
65
66
  - lib/em-http/mock.rb
66
67
  - lib/em-http/multi.rb
67
68
  - lib/em-http/request.rb
68
- - test/helper.rb
69
- - test/mock/fixtures/google.ca
70
- - test/mock/test_mock.rb
71
- - test/stallion.rb
72
- - test/stub_server.rb
73
- - test/test_hash.rb
74
- - test/test_multi.rb
75
- - test/test_request.rb
69
+ - spec/fixtures/google.ca
70
+ - spec/hash_spec.rb
71
+ - spec/helper.rb
72
+ - spec/mock_spec.rb
73
+ - spec/multi_spec.rb
74
+ - spec/request_spec.rb
75
+ - spec/stallion.rb
76
+ - spec/stub_server.rb
76
77
  has_rdoc: true
77
78
  homepage: http://github.com/igrigorik/em-http-request
78
79
  licenses: []
@@ -102,12 +103,13 @@ signing_key:
102
103
  specification_version: 3
103
104
  summary: EventMachine based, async HTTP Request interface
104
105
  test_files:
105
- - test/helper.rb
106
- - test/mock/test_mock.rb
107
- - test/stallion.rb
108
- - test/stub_server.rb
109
- - test/test_hash.rb
110
- - test/test_multi.rb
111
- - test/test_request.rb
106
+ - spec/hash_spec.rb
107
+ - spec/helper.rb
108
+ - spec/mock_spec.rb
109
+ - spec/multi_spec.rb
110
+ - spec/request_spec.rb
111
+ - spec/stallion.rb
112
+ - spec/stub_server.rb
112
113
  - examples/fetch.rb
113
114
  - examples/fibered-http.rb
115
+ - examples/oauth-tweet.rb
data/test/helper.rb DELETED
@@ -1,5 +0,0 @@
1
- require 'rubygems'
2
- require 'spec'
3
- require 'pp'
4
-
5
- require 'lib/em-http'
data/test/test_hash.rb DELETED
@@ -1,19 +0,0 @@
1
- require 'test/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.should == "a=alpha&b=beta"
8
- end
9
-
10
- it "should transform a more complex hash into HTTP POST Params" do
11
- {:a => "a", :b => ["c", "d", "e"]}.to_params.should == "a=a&b[0]=c&b[1]=d&b[2]=e"
12
- end
13
-
14
- # Ruby 1.8 Hash is not sorted, so this test breaks randomly. Maybe once we're all on 1.9. ;-)
15
- # it "should transform a very complex hash into HTTP POST Params" do
16
- # {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}.to_params.should == "a=a&b[0][d]=d&b[0][c]=c&b[1][f]=f&b[1][e]=e"
17
- # end
18
- end
19
- end