em-http-request 1.0.1 → 1.0.2

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.

@@ -12,7 +12,6 @@ class HttpClientOptions
12
12
 
13
13
  @method = method.to_s.upcase
14
14
  @headers = options[:head] || {}
15
- @proxy = options[:proxy] || {}
16
15
  @query = options[:query]
17
16
  @path = options[:path]
18
17
 
@@ -45,13 +44,8 @@ class HttpClientOptions
45
44
  @uri.port ||= 80
46
45
  end
47
46
 
48
- if !@proxy.empty?
49
- @host = @proxy[:host]
50
- @port = @proxy[:port]
51
- else
52
- @host = @uri.host
53
- @port = @uri.port
54
- end
47
+ @host = @uri.host
48
+ @port = @uri.port
55
49
 
56
50
  end
57
- end
51
+ end
@@ -1,199 +1,205 @@
1
- module EventMachine
2
-
3
- module HTTPMethods
4
- def get options = {}, &blk; setup_request(:get, options, &blk); end
5
- def head options = {}, &blk; setup_request(:head, options, &blk); end
6
- def delete options = {}, &blk; setup_request(:delete,options, &blk); end
7
- def put options = {}, &blk; setup_request(:put, options, &blk); end
8
- def post options = {}, &blk; setup_request(:post, options, &blk); end
9
- end
10
-
11
- class HttpStubConnection < Connection
12
- include Deferrable
13
- attr_reader :parent
14
-
15
- def parent=(p)
16
- @parent = p
17
- @parent.conn = self
18
- end
19
-
20
- def receive_data(data)
21
- @parent.receive_data data
22
- end
23
-
24
- def connection_completed
25
- @parent.connection_completed
26
- end
27
-
28
- def unbind(reason=nil)
29
- @parent.unbind(reason)
30
- end
31
- end
32
-
33
- class HttpConnection
34
- include HTTPMethods
35
- include Socksify
36
-
37
- attr_reader :deferred
38
- attr_accessor :error, :connopts, :uri, :conn
39
-
40
- def initialize
41
- @deferred = true
42
- @middleware = []
43
- end
44
-
45
- def conn=(c)
46
- @conn = c
47
- @deferred = false
48
- end
49
-
50
- def activate_connection(client)
51
- begin
52
- EventMachine.bind_connect(@connopts.bind, @connopts.bind_port, @connopts.host, @connopts.port, HttpStubConnection) do |conn|
53
- post_init
54
-
55
- @deferred = false
56
- @conn = conn
57
-
58
- conn.parent = self
59
- conn.pending_connect_timeout = @connopts.connect_timeout
60
- conn.comm_inactivity_timeout = @connopts.inactivity_timeout
61
- end
62
-
63
- finalize_request(client)
64
- rescue EventMachine::ConnectionError => e
65
- #
66
- # Currently, this can only fire on initial connection setup
67
- # since #connect is a synchronous method. Hence, rescue the exception,
68
- # and return a failed deferred which fail any client request at next
69
- # tick. We fail at next tick to keep a consistent API when the newly
70
- # created HttpClient is failed. This approach has the advantage to
71
- # remove a state check of @deferred_status after creating a new
72
- # HttpRequest. The drawback is that users may setup a callback which we
73
- # know won't be used.
74
- #
75
- # Once there is async-DNS, then we'll iterate over the outstanding
76
- # client requests and fail them in order.
77
- #
78
- # Net outcome: failed connection will invoke the same ConnectionError
79
- # message on the connection deferred, and on the client deferred.
80
- #
81
- EM.next_tick{client.close(e.message)}
82
- end
83
- end
84
-
85
- def setup_request(method, options = {}, c = nil)
86
- c ||= HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
87
- @deferred ? activate_connection(c) : finalize_request(c)
88
- c
89
- end
90
-
91
- def finalize_request(c)
92
- @conn.callback { c.connection_completed }
93
-
94
- middleware.each do |m|
95
- c.callback &m.method(:response) if m.respond_to?(:response)
96
- end
97
-
98
- @clients.push c
99
- end
100
-
101
- def middleware
102
- [HttpRequest.middleware, @middleware].flatten
103
- end
104
-
105
- def post_init
106
- @clients = []
107
- @pending = []
108
-
109
- @p = Http::Parser.new
110
- @p.header_value_type = :mixed
111
- @p.on_headers_complete = proc do |h|
112
- client.parse_response_header(h, @p.http_version, @p.status_code)
113
- :reset if client.req.no_body?
114
- end
115
-
116
- @p.on_body = proc do |b|
117
- client.on_body_data(b)
118
- end
119
-
120
- @p.on_message_complete = proc do
121
- if not client.continue?
122
- c = @clients.shift
123
- c.state = :finished
124
- c.on_request_complete
125
- end
126
- end
127
- end
128
-
129
- def use(klass, *args, &block)
130
- @middleware << klass.new(*args, &block)
131
- end
132
-
133
- def peer
134
- Socket.unpack_sockaddr_in(@peer)[1] rescue nil
135
- end
136
-
137
- def receive_data(data)
138
- begin
139
- @p << data
140
- rescue HTTP::Parser::Error => e
141
- c = @clients.shift
142
- c.nil? ? unbind(e.message) : c.on_error(e.message)
143
- end
144
- end
145
-
146
- def connection_completed
147
- @peer = @conn.get_peername
148
-
149
- if @connopts.proxy && @connopts.proxy[:type] == :socks5
150
- socksify(client.req.uri.host, client.req.uri.port, *@connopts.proxy[:authorization]) { start }
151
- else
152
- start
153
- end
154
- end
155
-
156
- def start
157
- @conn.start_tls(@connopts.tls) if client && client.req.ssl?
158
- @conn.succeed
159
- end
160
-
161
- def redirect(client)
162
- @pending.push client
163
- end
164
-
165
- def unbind(reason)
166
- @clients.map { |c| c.unbind(reason) }
167
-
168
- if r = @pending.shift
169
- @clients.push r
170
-
171
- r.reset!
172
- @p.reset!
173
-
174
- begin
175
- @conn.set_deferred_status :unknown
176
- @conn.reconnect(r.req.host, r.req.port)
177
- @conn.callback { r.connection_completed }
178
- rescue EventMachine::ConnectionError => e
179
- @clients.pop.close(e.message)
180
- end
181
- end
182
- end
183
- alias :close :unbind
184
-
185
- def send_data(data)
186
- @conn.send_data data
187
- end
188
-
189
- def stream_file_data(filename, args = {})
190
- @conn.stream_file_data filename, args
191
- end
192
-
193
- private
194
-
195
- def client
196
- @clients.first
197
- end
198
- end
199
- end
1
+ module EventMachine
2
+
3
+ module HTTPMethods
4
+ def get options = {}, &blk; setup_request(:get, options, &blk); end
5
+ def head options = {}, &blk; setup_request(:head, options, &blk); end
6
+ def delete options = {}, &blk; setup_request(:delete,options, &blk); end
7
+ def put options = {}, &blk; setup_request(:put, options, &blk); end
8
+ def post options = {}, &blk; setup_request(:post, options, &blk); end
9
+ end
10
+
11
+ class HttpStubConnection < Connection
12
+ include Deferrable
13
+ attr_reader :parent
14
+
15
+ def parent=(p)
16
+ @parent = p
17
+ @parent.conn = self
18
+ end
19
+
20
+ def receive_data(data)
21
+ @parent.receive_data data
22
+ end
23
+
24
+ def connection_completed
25
+ @parent.connection_completed
26
+ end
27
+
28
+ def unbind(reason=nil)
29
+ @parent.unbind(reason)
30
+ end
31
+ end
32
+
33
+ class HttpConnection
34
+ include HTTPMethods
35
+ include Socksify
36
+
37
+ attr_reader :deferred
38
+ attr_accessor :error, :connopts, :uri, :conn
39
+
40
+ def initialize
41
+ @deferred = true
42
+ @middleware = []
43
+ end
44
+
45
+ def conn=(c)
46
+ @conn = c
47
+ @deferred = false
48
+ end
49
+
50
+ def activate_connection(client)
51
+ begin
52
+ EventMachine.bind_connect(@connopts.bind, @connopts.bind_port, @connopts.host, @connopts.port, HttpStubConnection) do |conn|
53
+ post_init
54
+
55
+ @deferred = false
56
+ @conn = conn
57
+
58
+ conn.parent = self
59
+ conn.pending_connect_timeout = @connopts.connect_timeout
60
+ conn.comm_inactivity_timeout = @connopts.inactivity_timeout
61
+ end
62
+
63
+ finalize_request(client)
64
+ rescue EventMachine::ConnectionError => e
65
+ #
66
+ # Currently, this can only fire on initial connection setup
67
+ # since #connect is a synchronous method. Hence, rescue the exception,
68
+ # and return a failed deferred which fail any client request at next
69
+ # tick. We fail at next tick to keep a consistent API when the newly
70
+ # created HttpClient is failed. This approach has the advantage to
71
+ # remove a state check of @deferred_status after creating a new
72
+ # HttpRequest. The drawback is that users may setup a callback which we
73
+ # know won't be used.
74
+ #
75
+ # Once there is async-DNS, then we'll iterate over the outstanding
76
+ # client requests and fail them in order.
77
+ #
78
+ # Net outcome: failed connection will invoke the same ConnectionError
79
+ # message on the connection deferred, and on the client deferred.
80
+ #
81
+ EM.next_tick{client.close(e.message)}
82
+ end
83
+ end
84
+
85
+ def setup_request(method, options = {}, c = nil)
86
+ c ||= HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
87
+ @deferred ? activate_connection(c) : finalize_request(c)
88
+ c
89
+ end
90
+
91
+ def finalize_request(c)
92
+ @conn.callback { c.connection_completed }
93
+
94
+ middleware.each do |m|
95
+ c.callback &m.method(:response) if m.respond_to?(:response)
96
+ end
97
+
98
+ @clients.push c
99
+ end
100
+
101
+ def middleware
102
+ [HttpRequest.middleware, @middleware].flatten
103
+ end
104
+
105
+ def post_init
106
+ @clients = []
107
+ @pending = []
108
+
109
+ @p = Http::Parser.new
110
+ @p.header_value_type = :mixed
111
+ @p.on_headers_complete = proc do |h|
112
+ client.parse_response_header(h, @p.http_version, @p.status_code)
113
+ :reset if client.req.no_body?
114
+ end
115
+
116
+ @p.on_body = proc do |b|
117
+ client.on_body_data(b)
118
+ end
119
+
120
+ @p.on_message_complete = proc do
121
+ if not client.continue?
122
+ c = @clients.shift
123
+ c.state = :finished
124
+ c.on_request_complete
125
+ end
126
+ end
127
+ end
128
+
129
+ def use(klass, *args, &block)
130
+ @middleware << klass.new(*args, &block)
131
+ end
132
+
133
+ def peer
134
+ Socket.unpack_sockaddr_in(@peer)[1] rescue nil
135
+ end
136
+
137
+ def receive_data(data)
138
+ begin
139
+ @p << data
140
+ rescue HTTP::Parser::Error => e
141
+ c = @clients.shift
142
+ c.nil? ? unbind(e.message) : c.on_error(e.message)
143
+ end
144
+ end
145
+
146
+ def connection_completed
147
+ @peer = @conn.get_peername
148
+
149
+ if @connopts.proxy && @connopts.proxy[:type] == :socks5
150
+ socksify(client.req.uri.host, client.req.uri.port, *@connopts.proxy[:authorization]) { start }
151
+ else
152
+ start
153
+ end
154
+ end
155
+
156
+ def start
157
+ @conn.start_tls(@connopts.tls) if client && client.req.ssl?
158
+ @conn.succeed
159
+ end
160
+
161
+ def redirect(client)
162
+ @pending.push client
163
+ end
164
+
165
+ def unbind(reason)
166
+ @clients.map { |c| c.unbind(reason) }
167
+
168
+ if r = @pending.shift
169
+ @clients.push r
170
+
171
+ r.reset!
172
+ @p.reset!
173
+
174
+ begin
175
+ @conn.set_deferred_status :unknown
176
+
177
+ if @connopts.proxy
178
+ @conn.reconnect(@connopts.host, @connopts.port)
179
+ else
180
+ @conn.reconnect(r.req.host, r.req.port)
181
+ end
182
+
183
+ @conn.callback { r.connection_completed }
184
+ rescue EventMachine::ConnectionError => e
185
+ @clients.pop.close(e.message)
186
+ end
187
+ end
188
+ end
189
+ alias :close :unbind
190
+
191
+ def send_data(data)
192
+ @conn.send_data data
193
+ end
194
+
195
+ def stream_file_data(filename, args = {})
196
+ @conn.stream_file_data filename, args
197
+ end
198
+
199
+ private
200
+
201
+ def client
202
+ @clients.first
203
+ end
204
+ end
205
+ end
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  class HttpRequest
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
5
5
  end
@@ -3,19 +3,21 @@ require 'fiber'
3
3
 
4
4
  describe EventMachine::HttpRequest do
5
5
  context "with fibers" do
6
- it "should be transparent to connexion errors" do
6
+
7
+ it "should be transparent to connection errors" do
7
8
  EventMachine.run do
8
9
  Fiber.new do
9
10
  f = Fiber.current
10
- http = EventMachine::HttpRequest.new('http://non-existing.domain/').get
11
- http.callback {failed(http)}
12
- http.errback {f.resume}
13
- Fiber.yield
14
- EventMachine.stop
11
+ fired = false
12
+ http = EventMachine::HttpRequest.new('http://non-existing.domain/', :connection_timeout => 0.1).get
13
+ http.callback { failed(http) }
14
+ http.errback { f.resume :errback }
15
+
16
+ Fiber.yield.should == :errback
17
+ EM.stop
15
18
  end.resume
16
19
  end
17
20
  end
21
+
18
22
  end
19
23
  end
20
-
21
-