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

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.

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