em-http-request 1.0.0.beta.3 → 1.0.0.beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -0,0 +1,23 @@
1
+ class HttpConnectionOptions
2
+ attr_reader :host, :port, :tls, :proxy
3
+ attr_reader :connect_timeout, :inactivity_timeout
4
+
5
+ def initialize(uri, options)
6
+ @connect_timeout = options[:connect_timeout] || 5 # default connection setup timeout
7
+ @inactivity_timeout = options[:inactivity_timeout] ||= 10 # default connection inactivity (post-setup) timeout
8
+
9
+ @tls = options[:tls] || options[:ssl] || {}
10
+ @proxy = options[:proxy]
11
+
12
+ uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s)
13
+ uri.port = (uri.scheme == "https" ? (uri.port || 443) : (uri.port || 80))
14
+
15
+ if proxy = options[:proxy]
16
+ @host = proxy[:host]
17
+ @port = proxy[:port]
18
+ else
19
+ @host = uri.host
20
+ @port = uri.port
21
+ end
22
+ end
23
+ end
@@ -63,7 +63,7 @@ module EventMachine
63
63
  query.to_s
64
64
  end
65
65
 
66
- if !uri.query.to_s.empty?
66
+ if uri && !uri.query.to_s.empty?
67
67
  encoded_query = [encoded_query, uri.query].reject {|part| part.empty?}.join("&")
68
68
  end
69
69
  encoded_query.to_s.empty? ? uri.path : "#{uri.path}?#{encoded_query}"
@@ -0,0 +1,38 @@
1
+ require 'cookiejar'
2
+
3
+ module EventMachine
4
+ module Middleware
5
+ class CookieJar
6
+ class << self
7
+ attr_reader :cookiejar
8
+
9
+ def cookiejar=(val)
10
+ @cookiejar = val
11
+ end
12
+
13
+ def set_cookie(url, cookie)
14
+ @cookiejar.set_cookie url, cookie
15
+ end
16
+ end
17
+
18
+ def request(c, h, r)
19
+ raise ArgumentError, "You may not set cookies outside of the cookie jar" if h.delete('cookie')
20
+ cookies = CookieJar::cookiejar.get_cookie_header(c.last_effective_url)
21
+ h['cookie'] = cookies unless cookies.empty?
22
+ [h, r]
23
+ end
24
+
25
+ def response(r)
26
+ cookies = r.response_header.cookie
27
+ if cookies
28
+ [cookies].flatten.each { |c|
29
+ EventMachine::Middleware::CookieJar.cookiejar.set_cookie r.last_effective_url, c
30
+ }
31
+ end
32
+ r.response
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ EventMachine::Middleware::CookieJar.cookiejar = CookieJar::Jar.new
@@ -0,0 +1,15 @@
1
+ require 'yajl'
2
+
3
+ module EventMachine
4
+ module Middleware
5
+ class JSONResponse
6
+ def response(resp)
7
+ begin
8
+ body = Yajl::Parser.parse(resp.response)
9
+ resp.response = body
10
+ rescue Exception => e
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'oauth'
2
+ require 'oauth/client/em_http'
3
+
4
+ module EventMachine
5
+ module Middleware
6
+
7
+ class OAuth
8
+ def initialize(opts = {})
9
+ @consumer = ::OAuth::Consumer.new(opts[:consumer_key], opts[:consumer_secret])
10
+ @access_token = ::OAuth::AccessToken.new(@consumer, opts[:access_token], opts[:access_token_secret])
11
+ end
12
+
13
+ def request(client, head, body)
14
+ @consumer.sign!(client, @access_token)
15
+ head.merge!(client.options[:head])
16
+
17
+ [head,body]
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/em-http/multi.rb CHANGED
@@ -8,17 +8,17 @@ module EventMachine
8
8
  #
9
9
  # EventMachine.run {
10
10
  #
11
- # multi = EventMachine::MultiRequest.new
11
+ # multi = EventMachine::MultiRequest.new
12
12
  #
13
- # # add multiple requests to the multi-handler
14
- # multi.add(EventMachine::HttpRequest.new('http://www.google.com/').get)
15
- # multi.add(EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
13
+ # # add multiple requests to the multi-handler
14
+ # multi.add(:a, EventMachine::HttpRequest.new('http://www.google.com/').get)
15
+ # multi.add(:b, EventMachine::HttpRequest.new('http://www.yahoo.com/').get)
16
16
  #
17
- # multi.callback {
18
- # p multi.responses[:succeeded]
19
- # p multi.responses[:failed]
17
+ # multi.callback {
18
+ # p multi.responses[:callback]
19
+ # p multi.responses[:errback]
20
20
  #
21
- # EventMachine.stop
21
+ # EventMachine.stop
22
22
  # }
23
23
  # }
24
24
  #
@@ -28,27 +28,27 @@ module EventMachine
28
28
 
29
29
  attr_reader :requests, :responses
30
30
 
31
- def initialize(conns=[], &block)
31
+ def initialize
32
32
  @requests = []
33
- @responses = {:succeeded => [], :failed => []}
34
-
35
- conns.each {|conn| add(conn)}
36
- callback(&block) if block_given?
33
+ @responses = {:callback => {}, :errback => {}}
37
34
  end
38
35
 
39
- def add(conn)
36
+ def add(name, conn)
40
37
  @requests.push(conn)
41
38
 
42
- conn.callback { @responses[:succeeded].push(conn); check_progress }
43
- conn.errback { @responses[:failed].push(conn); check_progress }
39
+ conn.callback { @responses[:callback][name] = conn; check_progress }
40
+ conn.errback { @responses[:errback][name] = conn; check_progress }
41
+ end
42
+
43
+ def finished?
44
+ (@responses[:callback].size + @responses[:errback].size) == @requests.size
44
45
  end
45
46
 
46
47
  protected
47
48
 
48
49
  # invoke callback if all requests have completed
49
50
  def check_progress
50
- succeed(self) if (@responses[:succeeded].size +
51
- @responses[:failed].size) == @requests.size
51
+ succeed(self) if finished?
52
52
  end
53
53
 
54
54
  end
@@ -3,38 +3,16 @@ module EventMachine
3
3
  @middleware = []
4
4
 
5
5
  def self.new(uri, options={})
6
- begin
7
- req = HttpOptions.new(uri, options)
6
+ connopt = HttpConnectionOptions.new(uri, options)
8
7
 
9
- EventMachine.connect(req.host, req.port, HttpConnection) do |c|
10
- c.opts = req
11
-
12
- c.pending_connect_timeout = req.options[:connect_timeout]
13
- c.comm_inactivity_timeout = req.options[:inactivity_timeout]
14
- end
15
-
16
- rescue EventMachine::ConnectionError => e
17
- #
18
- # Currently, this can only fire on initial connection setup
19
- # since #connect is a synchronous method. Hence, rescue the
20
- # exception, and return a failed deferred which will immediately
21
- # fail any client request.
22
- #
23
- # Once there is async-DNS, then we'll iterate over the outstanding
24
- # client requests and fail them in order.
25
- #
26
- # Net outcome: failed connection will invoke the same ConnectionError
27
- # message on the connection deferred, and on the client deferred.
28
- #
29
- conn = EventMachine::FailedConnection.new(req)
30
- conn.error = e.message
31
- conn.fail
32
- conn
33
- end
8
+ c = HttpConnection.new
9
+ c.connopts = connopt
10
+ c.uri = uri
11
+ c
34
12
  end
35
13
 
36
- def self.use(klass)
37
- @middleware << klass
14
+ def self.use(klass, *args, &block)
15
+ @middleware << klass.new(*args, &block)
38
16
  end
39
17
 
40
18
  def self.middleware
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  class HttpRequest
3
- VERSION = "1.0.0.beta.3"
3
+ VERSION = "1.0.0.beta.4"
4
4
  end
5
5
  end
data/spec/client_spec.rb CHANGED
@@ -228,6 +228,25 @@ describe EventMachine::HttpRequest do
228
228
  }
229
229
  end
230
230
 
231
+ it "should return peer's IP address" do
232
+ EventMachine.run {
233
+
234
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
235
+ conn.peer.should be_nil
236
+
237
+ http = conn.get
238
+ http.peer.should be_nil
239
+
240
+ http.errback { failed(http) }
241
+ http.callback {
242
+ conn.peer.should == '127.0.0.1'
243
+ http.peer.should == '127.0.0.1'
244
+
245
+ EventMachine.stop
246
+ }
247
+ }
248
+ end
249
+
231
250
  it "should remove all newlines from long basic auth header" do
232
251
  EventMachine.run {
233
252
  auth = {'authorization' => ['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz']}
@@ -305,6 +324,26 @@ describe EventMachine::HttpRequest do
305
324
  }
306
325
  end
307
326
 
327
+ it "should not decode the response when configured so" do
328
+ EventMachine.run {
329
+
330
+ http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/gzip').get :head => {
331
+ "accept-encoding" => "gzip, compressed"
332
+ }, :decoding => false
333
+
334
+ http.errback { failed(http) }
335
+ http.callback {
336
+ http.response_header.status.should == 200
337
+ http.response_header["CONTENT_ENCODING"].should == "gzip"
338
+
339
+ raw = http.response
340
+ Zlib::GzipReader.new(StringIO.new(raw)).read.should == "compressed"
341
+
342
+ EventMachine.stop
343
+ }
344
+ }
345
+ end
346
+
308
347
  it "should timeout after 0.1 seconds of inactivity" do
309
348
  EventMachine.run {
310
349
  t = Time.now.to_i
@@ -79,6 +79,22 @@ requires_connection do
79
79
  }
80
80
  end
81
81
 
82
+ it "should detect deflate encoding" do
83
+ EventMachine.run {
84
+
85
+ options = {:head => {"accept-encoding" => "deflate"}, :redirects => 5}
86
+ http = EventMachine::HttpRequest.new('http://www.msn.com').get options
87
+
88
+ http.errback { failed(http) }
89
+ http.callback {
90
+ http.response_header.status.should == 200
91
+ http.response_header["CONTENT_ENCODING"].should == "deflate"
92
+
93
+ EventMachine.stop
94
+ }
95
+ }
96
+ end
97
+
82
98
  context "keepalive" do
83
99
  it "should default to non-keepalive" do
84
100
  EventMachine.run {
@@ -88,7 +104,7 @@ requires_connection do
88
104
  http.errback { fail }
89
105
  start = Time.now.to_i
90
106
  http.callback {
91
- (start - Time.now.to_i).should be_within(1).of(0)
107
+ (Time.now.to_i - start).should be_within(2).of(0)
92
108
  EventMachine.stop
93
109
  }
94
110
  }
data/spec/helper.rb CHANGED
@@ -14,4 +14,16 @@ end
14
14
 
15
15
  def requires_connection(&blk)
16
16
  blk.call if system('ping -t1 -c1 google.com &> /dev/null')
17
+ end
18
+
19
+ def requires_port(port, &blk)
20
+ port_open = true
21
+ begin
22
+ s = TCPSocket.new('localhost', port)
23
+ s.close()
24
+ rescue
25
+ port_open = false
26
+ end
27
+
28
+ blk.call if port_open
17
29
  end
@@ -1,11 +1,12 @@
1
1
  require 'helper'
2
+ require 'em-http/middleware/cookie_jar'
2
3
 
3
4
  describe EventMachine::HttpRequest do
4
5
 
5
- module EmptyMiddleware; end
6
+ class EmptyMiddleware; end
6
7
 
7
8
  class GlobalMiddleware
8
- def self.response(resp)
9
+ def response(resp)
9
10
  resp.response_header['X-Global'] = 'middleware'
10
11
  end
11
12
  end
@@ -22,15 +23,44 @@ describe EventMachine::HttpRequest do
22
23
  }
23
24
  end
24
25
 
26
+ context "configuration" do
27
+ class ConfigurableMiddleware
28
+ def initialize(conf, &block)
29
+ @conf = conf
30
+ @block = block
31
+ end
32
+
33
+ def response(resp)
34
+ resp.response_header['X-Conf'] = @conf
35
+ resp.response_header['X-Block'] = @block.call
36
+ end
37
+ end
38
+
39
+ it "should accept middleware initialization parameters" do
40
+ EventMachine.run {
41
+ conn = EM::HttpRequest.new('http://127.0.0.1:8090')
42
+ conn.use ConfigurableMiddleware, 'conf-value' do
43
+ 'block-value'
44
+ end
45
+
46
+ req = conn.get
47
+ req.callback {
48
+ req.response_header['X-Conf'].should match('conf-value')
49
+ req.response_header['X-Block'].should match('block-value')
50
+ EM.stop
51
+ }
52
+ }
53
+ end
54
+ end
55
+
25
56
  context "request" do
26
57
  class ResponseMiddleware
27
- def self.response(resp)
58
+ def response(resp)
28
59
  resp.response_header['X-Header'] = 'middleware'
29
60
  resp.response = 'Hello, Middleware!'
30
61
  end
31
62
  end
32
63
 
33
-
34
64
  it "should execute response middleware before user callbacks" do
35
65
  EventMachine.run {
36
66
  conn = EM::HttpRequest.new('http://127.0.0.1:8090')
@@ -62,7 +92,7 @@ describe EventMachine::HttpRequest do
62
92
 
63
93
  context "request" do
64
94
  class RequestMiddleware
65
- def self.request(head, body)
95
+ def request(client, head, body)
66
96
  head['X-Middleware'] = 'middleware' # insert new header
67
97
  body += ' modified' # modify post body
68
98
 
@@ -87,11 +117,11 @@ describe EventMachine::HttpRequest do
87
117
 
88
118
  context "jsonify" do
89
119
  class JSONify
90
- def self.request(head, body)
120
+ def request(client, head, body)
91
121
  [head, Yajl::Encoder.encode(body)]
92
122
  end
93
123
 
94
- def self.response(resp)
124
+ def response(resp)
95
125
  resp.response = Yajl::Parser.parse(resp.response)
96
126
  end
97
127
  end
@@ -111,4 +141,52 @@ describe EventMachine::HttpRequest do
111
141
  end
112
142
  end
113
143
 
144
+ context "CookieJar" do
145
+ it "should use the cookie jar as opposed to any other method when in use" do
146
+ lambda {
147
+ EventMachine.run {
148
+ conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
149
+ middleware = EventMachine::Middleware::CookieJar
150
+ conn.use middleware
151
+ middleware.set_cookie('http://127.0.0.1:8090/', 'id=1')
152
+ req = conn.get :head => {'cookie' => 'id=2;'}
153
+ req.callback { failed(req) }
154
+ req.errback { failed(req) }
155
+ }
156
+ }.should raise_error(ArgumentError)
157
+ end
158
+
159
+ it "should send cookies" do
160
+ EventMachine.run {
161
+ uri = 'http://127.0.0.1:8090/cookie_parrot'
162
+ conn = EventMachine::HttpRequest.new(uri)
163
+ middleware = EventMachine::Middleware::CookieJar
164
+ conn.use middleware
165
+ middleware.set_cookie(uri, 'id=1')
166
+ req = conn.get
167
+ req.callback {
168
+ req.response_header.cookie.should == 'id=1'
169
+ EventMachine.stop
170
+ }
171
+ }
172
+ end
173
+
174
+ it "should store cookies and send them" do
175
+ EventMachine.run {
176
+ uri = 'http://127.0.0.1:8090/set_cookie'
177
+ conn = EventMachine::HttpRequest.new(uri)
178
+ middleware = EventMachine::Middleware::CookieJar
179
+ conn.use middleware
180
+ req = conn.get
181
+ req.callback {
182
+ req.response_header.cookie[0..3].should == 'id=1'
183
+ cookies = middleware.cookiejar.get_cookies(uri)
184
+ cookies.length.should == 1
185
+ cookies[0].to_s.should == "id=1"
186
+ EventMachine.stop
187
+ }
188
+ }
189
+ end
190
+ end
191
+
114
192
  end