carnivore-http 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2aeabfa6f6dc0811a7cdeb3249e701a58e9eb899
4
- data.tar.gz: 7d01fd0e327f4d9172dd064e556482456a6b1483
3
+ metadata.gz: 9595b652c27534813c2b972ca1d51be428cb3ac7
4
+ data.tar.gz: c279093c9392ec09668b29c57603e4b0aca6c91c
5
5
  SHA512:
6
- metadata.gz: 056b6335c5c9c622f2f159423076e4e974250a504fe30b4e55b563f125954a01235b6e60d12b5d99bb96dfc1e272456396c052076c953861d8acd3c40d6d2fd5
7
- data.tar.gz: a7db515e4fe78aca2aa5581edf95b0f9a5895b763b48370b4c9eed700ef3741ebeedb67d26253d8862e3bf41b99a808e46720770ed4b10edbf4864f5862dadd9
6
+ metadata.gz: 76dab7d9fbd67731c3cb8130c335045222574f480bbb21688efcded5caa1697d99b61ab3506d2545267a1e7d42ff047856ca76124d57dfbaf058c5864743076d
7
+ data.tar.gz: d628ecc733fd036c324f85e9418fcaebafdd0f70b2d6a849ac32192e41e1b2db1c71dd2cdcae08707b4458d7194eebce95c20832fe71aa8b926bed28c40f281b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.3.0
2
+ * Updates for carnivore 1.0
3
+ * Remove reel and replace with puma
4
+
1
5
  # v0.2.8
2
6
  * Set confirmed state directly into message
3
7
  * Support body on non-200 type responses
@@ -10,9 +10,13 @@ Gem::Specification.new do |s|
10
10
  s.description = 'Carnivore HTTP source'
11
11
  s.license = 'Apache 2.0'
12
12
  s.require_path = 'lib'
13
- s.add_dependency 'carnivore', '>= 0.1.8'
14
- s.add_dependency 'reel', '~> 0.5.0'
15
- s.add_dependency 'blockenspiel'
16
- s.add_dependency 'htauth'
17
- s.files = Dir['**/*']
13
+ s.add_runtime_dependency 'carnivore', '>= 1.0.0', '< 2.0'
14
+ s.add_runtime_dependency 'puma', '~> 2.13.4'
15
+ s.add_runtime_dependency 'rack', '~> 1.6.4'
16
+ s.add_runtime_dependency 'blockenspiel', '~> 0.4.5'
17
+ s.add_runtime_dependency 'htauth', '~> 2.0.0'
18
+ s.add_development_dependency 'http'
19
+ s.add_development_dependency 'minitest'
20
+ s.add_development_dependency 'pry'
21
+ s.files = Dir['lib/**/*'] + %w(carnivore-http.gemspec README.md CHANGELOG.md)
18
22
  end
@@ -0,0 +1,218 @@
1
+ require 'rack'
2
+ require 'timeout'
3
+ require 'carnivore-http'
4
+
5
+ module Carnivore
6
+ module Http
7
+ # Rack app for processing messages
8
+ class App
9
+
10
+ # Customized response
11
+ class Response < Rack::Response
12
+
13
+ # Lazy status mapping
14
+ STATUS_CODES = Smash.new(
15
+ "continue" => 100,
16
+ "switching_protocols" => 101,
17
+ "processing" => 102,
18
+ "ok" => 200,
19
+ "created" => 201,
20
+ "accepted" => 202,
21
+ "non_authoritative_information" => 203,
22
+ "no_content" => 204,
23
+ "reset_content" => 205,
24
+ "partial_content" => 206,
25
+ "multi_status" => 207,
26
+ "already_reported" => 208,
27
+ "im_used" => 226,
28
+ "multiple_choices" => 300,
29
+ "moved_permanently" => 301,
30
+ "found" => 302,
31
+ "see_other" => 303,
32
+ "not_modified" => 304,
33
+ "use_proxy" => 305,
34
+ "temporary_redirect" => 307,
35
+ "permanent_redirect" => 308,
36
+ "bad_request" => 400,
37
+ "unauthorized" => 401,
38
+ "payment_required" => 402,
39
+ "forbidden" => 403,
40
+ "not_found" => 404,
41
+ "method_not_allowed" => 405,
42
+ "not_acceptable" => 406,
43
+ "proxy_authentication_required" => 407,
44
+ "request_timeout" => 408,
45
+ "conflict" => 409,
46
+ "gone" => 410,
47
+ "length_required" => 411,
48
+ "precondition_failed" => 412,
49
+ "payload_too_large" => 413,
50
+ "uri_too_long" => 414,
51
+ "unsupported_media_type" => 415,
52
+ "range_not_satisfiable" => 416,
53
+ "expectation_failed" => 417,
54
+ "misdirected_request" => 421,
55
+ "unprocessable_entity" => 422,
56
+ "locked" => 423,
57
+ "failed_dependency" => 424,
58
+ "upgrade_required" => 426,
59
+ "precondition_required" => 428,
60
+ "too_many_requests" => 429,
61
+ "request_header_fields_too_large" => 431,
62
+ "internal_server_error" => 500,
63
+ "not_implemented" => 501,
64
+ "bad_gateway" => 502,
65
+ "service_unavailable" => 503,
66
+ "gateway_timeout" => 504,
67
+ "http_version_not_supported" => 505,
68
+ "variant_also_negotiates" => 506,
69
+ "insufficient_storage" => 507,
70
+ "loop_detected" => 508,
71
+ "not_extended" => 510,
72
+ "network_authentication_required" => 511
73
+ )
74
+
75
+ # Create a new response
76
+ #
77
+ # @param code [String, Symbol, Integer] status code of response
78
+ # @param string_or_args [String, Hash] response content
79
+ # @option :body [String] response body
80
+ # @option :json [Hash, Array] response body to serialize
81
+ # @option :form [Hash] response body to encode
82
+ # @option :headers [Hash] response headers
83
+ # @return [self]
84
+ def initialize(code, string_or_args, &block)
85
+ status = STATUS_CODES.fetch(code, code).to_i
86
+ case string_or_args
87
+ when String
88
+ body = string_or_args
89
+ headers = {}
90
+ when Hash
91
+ headers = string_or_args.fetch(:headers, {})
92
+ if(string_or_args[:body])
93
+ body = string_or_args[:body]
94
+ unless(headers['Content-Type'])
95
+ headers['Content-Type'] = 'text/plain'
96
+ end
97
+ elsif(string_or_args[:json])
98
+ body = MultiJson.dump(string_or_args[:json])
99
+ unless(headers['Content-Type'])
100
+ headers['Content-Type'] = 'application/json'
101
+ end
102
+ elsif(string_or_args[:form])
103
+ body = dump_query_string(string_or_args[:form])
104
+ unless(headers['Content-Type'])
105
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
106
+ end
107
+ end
108
+ else
109
+ raise TypeError.new "Invalid type provided. Expected `String` or `Hash` but got `#{string_or_args.class}`"
110
+ end
111
+ super(body, status, headers, &block)
112
+ end
113
+
114
+ end
115
+
116
+
117
+ # Customized request
118
+ class Request < Rack::Request
119
+
120
+ include Zoidberg::SoftShell
121
+
122
+ option :cache_signals
123
+
124
+ # @return [Response]
125
+ attr_reader :response_value
126
+
127
+ # Respond to the request
128
+ #
129
+ # @param code [String, Symbol, Integer] response status code
130
+ # @param string_or_args [String, Hash]
131
+ # @return [TrueClass, FalseClass]
132
+ def respond(code, string_or_args='')
133
+ unless(@response_value)
134
+ signal(:response, Response.new(code, string_or_args))
135
+ else
136
+ raise 'Response was already set!'
137
+ end
138
+ end
139
+
140
+ # Response to this request
141
+ #
142
+ # @param timeout [Integer] maximum number of seconds to wait
143
+ # @return [Response]
144
+ def response(timeout=5)
145
+ unless(@response_value)
146
+ begin
147
+ Timeout.timeout(timeout) do
148
+ @response_value = wait_for(:response)
149
+ end
150
+ rescue Timeout::Error
151
+ @response_value = Response.new(:internal_server_error, 'Timeout waiting for response')
152
+ end
153
+ end
154
+ @response_value
155
+ end
156
+
157
+ # @return [Smash]
158
+ def headers
159
+ Smash[
160
+ env.map do |k,v|
161
+ k.start_with?('rack.') ? nil : [k.downcase.sub(/^http_/, '').to_sym,v]
162
+ end.compact
163
+ ]
164
+ end
165
+
166
+ # @return [String]
167
+ def remote_addr
168
+ headers['REMOTE_ADDR']
169
+ end
170
+
171
+ # @return [Symbol]
172
+ def method
173
+ request_method.to_s.downcase.to_sym
174
+ end
175
+
176
+ end
177
+
178
+ # @return [Proc] action to process request
179
+ attr_reader :action
180
+
181
+ # Create a new instance
182
+ #
183
+ # @param args [Hash]
184
+ # @yield processes request
185
+ # @return [self]
186
+ def initialize(args={}, &block)
187
+ @action = block
188
+ end
189
+
190
+ # Process the request
191
+ #
192
+ # @param env [Hash]
193
+ # @return [Array]
194
+ def call(env)
195
+ request = Request.new(env)
196
+ action.call(request)
197
+ request.response.finish
198
+ end
199
+
200
+ class << self
201
+
202
+ # Build a new app
203
+ #
204
+ # @param args [Hash] options
205
+ # @param block [Proc]
206
+ # @return [App]
207
+ def build_app(args={}, &block)
208
+ Rack::Builder.new do
209
+ use Rack::Chunked
210
+ run self.new(args, &block)
211
+ end
212
+ end
213
+
214
+ end
215
+
216
+ end
217
+ end
218
+ end
@@ -8,24 +8,22 @@ module Carnivore
8
8
  def process(*process_args)
9
9
  unless(@processing)
10
10
  @processing = true
11
- srv = build_listener do |con|
12
- con.each_request do |req|
13
- begin
14
- msg = build_message(con, req)
15
- msg = format(msg)
16
- if(authorized?(msg))
17
- callbacks.each do |name|
18
- c_name = callback_name(name)
19
- debug "Dispatching #{msg} to callback<#{name} (#{c_name})>"
20
- callback_supervisor[c_name].call(msg)
21
- end
22
- req.respond(:ok, 'So long, and thanks for all the fish!') if args[:auto_respond]
23
- else
24
- req.respond(:unauthorized, 'You are not authorized to perform requested action!')
11
+ srv = build_listener do |req|
12
+ begin
13
+ msg = build_message(req)
14
+ msg = format(msg)
15
+ if(authorized?(msg))
16
+ callbacks.each do |name|
17
+ c_name = callback_name(name)
18
+ debug "Dispatching #{msg} to callback<#{name} (#{c_name})>"
19
+ callback_supervisor[c_name].call(msg)
25
20
  end
26
- rescue => e
27
- req.respond(:bad_request, "Failed to process request -> #{e}")
21
+ req.respond(:ok, 'So long, and thanks for all the fish!') if args[:auto_respond]
22
+ else
23
+ req.respond(:unauthorized, 'You are not authorized to perform requested action!')
28
24
  end
25
+ rescue => e
26
+ req.respond(:bad_request, "Failed to process request -> #{e}")
29
27
  end
30
28
  end
31
29
  true
@@ -85,25 +85,23 @@ module Carnivore
85
85
  def process(*process_args)
86
86
  unless(processing)
87
87
  @processing = true
88
- srv = build_listener do |con|
89
- con.each_request do |req|
90
- begin
91
- msg = build_message(con, req)
92
- msg = format(msg)
93
- if(authorized?(msg))
94
- unless(@points.deliver(msg))
95
- warn "No match found for request: #{msg} (path: #{msg[:message][:request].url})"
96
- debug "Unmatched message (#{msg}): #{msg.inspect}"
97
- req.respond(:not_found, 'So long, and thanks for all the fish!')
98
- end
99
- else
100
- req.respond(:unauthorized, 'You are not authorized to perform requested action!')
88
+ srv = build_listener do |req|
89
+ begin
90
+ msg = build_message(req)
91
+ msg = format(msg)
92
+ if(authorized?(msg))
93
+ unless(@points.deliver(msg))
94
+ warn "No match found for request: #{msg} (path: #{msg[:message][:request].url})"
95
+ debug "Unmatched message (#{msg}): #{msg.inspect}"
96
+ req.respond(:not_found, 'So long, and thanks for all the fish!')
101
97
  end
102
- rescue => e
103
- error "Failed to process message: #{e.class} - #{e}"
104
- debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
105
- req.respond(:bad_request, 'Failed to process request')
98
+ else
99
+ req.respond(:unauthorized, 'You are not authorized to perform requested action!')
106
100
  end
101
+ rescue => e
102
+ error "Failed to process message: #{e.class} - #{e}"
103
+ debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
104
+ req.respond(:bad_request, 'Failed to process request')
107
105
  end
108
106
  end
109
107
  true
@@ -12,7 +12,6 @@ module Carnivore
12
12
  # Default response wait time stepping
13
13
  DEFAULT_RESPONSE_WAIT_STEP = 0.1
14
14
 
15
- finalizer :halt_listener
16
15
  include Bogo::Memoization
17
16
 
18
17
  # @return [String] end point path
@@ -21,10 +20,10 @@ module Carnivore
21
20
  attr_reader :http_method
22
21
 
23
22
  # Kill listener on shutdown
24
- def halt_listener
23
+ def terminate
25
24
  listener = memoize("#{args[:bind]}-#{args[:port]}", :global){ nil }
26
- if(listener && listener.alive?)
27
- listener.terminate
25
+ if(listener && listener.running)
26
+ listener.stop(:sync)
28
27
  end
29
28
  unmemoize("#{args[:bind]}-#{args[:port]}", :global)
30
29
  unmemoize("#{args[:bind]}-#{args[:port]}-queues", :global)
@@ -72,48 +71,37 @@ module Carnivore
72
71
  # Start the HTTP(S) listener
73
72
  def start_listener!
74
73
  memoize("#{args[:bind]}-#{args[:port]}", :global) do
75
- build_listener do |con|
76
- con.each_request do |req|
77
- begin
78
- msg = build_message(con, req)
79
- # Start with static path lookup since it's the
80
- # cheapest, then fallback to iterative globbing
81
- msg_queue = nil
82
- unless(msg_queue = message_queues["#{req.path}-#{req.method.to_s.downcase}"])
83
- message_queues.each do |k,v|
84
- path_glob, http_method = k.split('-')
85
- if(req.method.to_s.downcase == http_method && File.fnmatch(path_glob, req.path))
86
- msg_queue = v
87
- end
74
+ build_listener do |req|
75
+ begin
76
+ msg = build_message(req)
77
+ # Start with static path lookup since it's the
78
+ # cheapest, then fallback to iterative globbing
79
+ msg_queue = nil
80
+ unless(msg_queue = message_queues["#{req.path}-#{req.method.to_s.downcase}"])
81
+ message_queues.each do |k,v|
82
+ path_glob, http_method = k.split('-')
83
+ if(req.method.to_s.downcase == http_method && File.fnmatch(path_glob, req.path))
84
+ msg_queue = v
88
85
  end
89
86
  end
90
- if(msg_queue)
91
- if(authorized?(msg))
92
- msg_queue[:queue] << msg
93
- if(msg_queue[:config][:auto_respond])
94
- code = msg_queue[:config].fetch(:response, :code, 'ok').to_sym
95
- response = msg_queue[:config].fetch(:response, :message, 'So long and thanks for all the fish!')
96
- req.respond(code, response)
97
- else
98
- wait_time = msg_queue[:config].fetch(:response_timeout, DEFAULT_RESPONSE_TIMEOUT).to_f
99
- wait_step = msg_queue[:config].fetch(:response_wait_step, DEFAULT_RESPONSE_WAIT_STEP).to_f
100
- while(!con.socket.closed? && wait_time > 0)
101
- sleep(wait_step)
102
- wait_time -= wait_step
103
- end
104
- if(con.response_state == :headers)
105
- raise "Timeout has been exceeded waiting for response! (#{msg})"
106
- end
107
- end
108
- else
109
- req.respond(:unauthorized, 'You are not authorized to perform requested action!')
87
+ end
88
+ if(msg_queue)
89
+ if(authorized?(msg))
90
+ msg_queue[:queue] << msg
91
+ if(msg_queue[:config][:auto_respond])
92
+ code = msg_queue[:config].fetch(:response, :code, 'ok').to_sym
93
+ response = msg_queue[:config].fetch(:response, :message, 'So long and thanks for all the fish!')
94
+ req.respond(code, response)
110
95
  end
111
96
  else
112
- req.respond(:not_found, 'Requested path not found!')
97
+ req.respond(:unauthorized, 'You are not authorized to perform requested action!')
113
98
  end
114
- rescue => e
115
- req.respond(:bad_request, "Failed to process request -> #{e}")
99
+ else
100
+ req.respond(:not_found, 'Requested path not found!')
116
101
  end
102
+ rescue => e
103
+ req.respond(:bad_request, "Failed to process request -> #{e}")
104
+ puts "#{e}\n#{e.backtrace.join("\n")}"
117
105
  end
118
106
  end
119
107
  end
@@ -123,8 +111,9 @@ module Carnivore
123
111
  def receive(*_)
124
112
  val = nil
125
113
  until(val)
126
- val = Celluloid::Future.new{ message_queue[:queue].pop }.value
114
+ val = defer{ message_queue[:queue].pop }
127
115
  end
116
+ info "PROCESSING MSG: #{val}"
128
117
  val
129
118
  end
130
119
 
@@ -1,4 +1,4 @@
1
- require 'reel'
1
+ require 'puma'
2
2
  require 'tempfile'
3
3
  require 'carnivore/source'
4
4
  require 'carnivore-http/utils'
@@ -9,14 +9,10 @@ module Carnivore
9
9
  # Carnivore HTTP source
10
10
  class HttpSource < Source
11
11
 
12
- trap_exit :retry_delivery_failure
13
-
14
12
  include Carnivore::Http::Utils::Params
15
13
 
16
14
  # @return [Hash] source arguments
17
15
  attr_reader :args
18
- # @return [Carnivore::Http::RetryDelivery]
19
- attr_reader :retry_delivery
20
16
  # @return [Array<IPAddr>] allowed request origin addresses
21
17
  attr_reader :auth_allowed_origins
22
18
  # @return [HTAuth::PasswdFile]
@@ -28,8 +24,13 @@ module Carnivore
28
24
  def setup(args={})
29
25
  require 'fileutils'
30
26
  @args = default_args(args)
31
- @retry_delivery = Carnivore::Http::RetryDelivery.new(retry_directory)
32
- self.link retry_delivery
27
+ unless(retry_delivery)
28
+ Carnivore::Supervisor.supervisor.supervise_as(
29
+ :http_retry_delivery,
30
+ Carnivore::Http::RetryDelivery,
31
+ retry_directory
32
+ )
33
+ end
33
34
  if(args.get(:authorization, :allowed_origins))
34
35
  require 'ipaddr'
35
36
  @allowed_origins = [args.get(:authorization, :allowed_origins)].flatten.compact.map do |origin_check|
@@ -42,25 +43,12 @@ module Carnivore
42
43
  args.get(:authorization, :htpasswd)
43
44
  )
44
45
  end
46
+ @listeners = []
45
47
  end
46
48
 
47
- # Handle failed retry deliveries
48
- #
49
- # @param actor [Object] terminated actor
50
- # @param reason [Exception] reason for termination
51
- # @return [NilClass]
52
- def retry_delivery_failure(actor, reason)
53
- if(actor == retry_delivery)
54
- if(reason)
55
- error "Failed RetryDelivery encountered: #{reason}. Rebuilding."
56
- @retry_delivery = Carnivore::Http::RetryDelivery.new(retry_directory)
57
- else
58
- info 'Encountered RetryDelivery failure. No reason so assuming teardown.'
59
- end
60
- else
61
- error "Unknown actor failure encountered: #{reason}"
62
- end
63
- nil
49
+ # @return [RetryDelivery]
50
+ def retry_delivery
51
+ Carnivore::Supervisor.supervisor[:http_retry_delivery]
64
52
  end
65
53
 
66
54
  # @return [String, NilClass] directory storing failed messages
@@ -218,7 +206,7 @@ module Carnivore
218
206
  method = options.fetch(:method,
219
207
  args.fetch(:method, :post)
220
208
  ).to_s.downcase.to_sym
221
- message_id = message.is_a?(Hash) ? message.fetch(:id, Celluloid.uuid) : Celluloid.uuid
209
+ message_id = message.is_a?(Hash) ? message.fetch(:id, Carnivore.uuid) : Carnivore.uuid
222
210
  payload = message.is_a?(String) ? message : MultiJson.dump(message)
223
211
  info "Transmit request type for Message ID: #{message_id}"
224
212
  async.perform_transmission(message_id.to_s, payload, method, url, options.fetch(:headers, {}))
@@ -280,8 +268,7 @@ module Carnivore
280
268
  args[:response_body] = 'Thanks' if code == :ok && args.empty?
281
269
  body = args.delete(:response_body)
282
270
  debug "Confirming #{message} with: Code: #{code.inspect} Args: #{args.inspect} Body: #{body}"
283
- message[:message][:request].respond(code, *(args.empty? ? [body] : [args, body]))
284
- message[:message][:connection].close
271
+ message[:message][:request].respond(code, *(args.empty? ? [body] : [args.merge(:body => body)]))
285
272
  message[:message][:confirmed] = true
286
273
  else
287
274
  warn "Message was already confimed. Confirmation not sent! (#{message})"
@@ -293,16 +280,27 @@ module Carnivore
293
280
  # @param block [Proc] processing block
294
281
  # @return [Reel::Server::HTTP, Reel::Server::HTTPS]
295
282
  def build_listener(&block)
283
+ app = Carnivore::Http::App.new(&block)
284
+ options = {:bind => []}
296
285
  if(args[:ssl])
297
- ssl_config = Smash.new(args[:ssl][key].dup)
298
- [:key, :cert].each do |key|
299
- if(ssl_config[key])
300
- ssl_config[key] = File.open(ssl_config.delete(key))
301
- end
302
- end
303
- Reel::Server::HTTPS.supervise(args[:bind], args[:port], ssl_config, &block)
286
+ ssl_config = Smash.new(args[:ssl])
287
+ options[:bind] << "ssl://#{args[:bind]}:#{args[:port]}?cert=#{ssl_config[:cert]}&key=#{ssl_config[:key]}"
304
288
  else
305
- Reel::Server::HTTP.supervise(args[:bind], args[:port], &block)
289
+ options[:bind] << "tcp://#{args[:bind]}:#{args[:port]}"
290
+ end
291
+ srv = Puma::Server.new(app, Puma::Events.stdio, options)
292
+ @listeners.push(srv)
293
+ srv.binder.parse(options[:bind], Puma::Events.stdio)
294
+ srv.run
295
+ srv
296
+ end
297
+
298
+ def terminate
299
+ if(@listeners)
300
+ @listeners.each do |l|
301
+ l.stop(:sync)
302
+ end
303
+ @listeners.clear
306
304
  end
307
305
  end
308
306
 
@@ -311,30 +309,28 @@ module Carnivore
311
309
 
312
310
  # Build message hash from request
313
311
  #
314
- # @param con [Reel::Connection]
315
- # @param req [Reel::Request]
312
+ # @param req [Carnivore::Http::App::Request]
316
313
  # @return [Hash]
317
314
  # @note
318
315
  # if body size is greater than BODY_TO_FILE_SIZE
319
316
  # the body will be a temp file instead of a string
320
- def build_message(con, req)
317
+ def build_message(req)
321
318
  msg = Smash.new(
322
319
  :request => req,
323
320
  :headers => Smash[
324
321
  req.headers.map{ |k,v| [k.downcase.tr('-', '_'), v]}
325
322
  ],
326
- :connection => con,
327
323
  :query => parse_query_string(req.query_string),
328
324
  :origin => req.remote_addr,
329
325
  :authentication => {}
330
326
  )
331
327
  if(msg[:headers][:content_type] == 'application/json')
332
328
  msg[:body] = MultiJson.load(
333
- req.body.to_s
329
+ req.body.read
334
330
  )
335
331
  elsif(msg[:headers][:content_type] == 'application/x-www-form-urlencoded')
336
332
  msg[:body] = parse_query_string(
337
- req.body.to_s
333
+ req.body.read
338
334
  )
339
335
  if(msg[:body].size == 1 && msg[:body].values.first.is_a?(Array) && msg[:body].values.first.empty?)
340
336
  msg[:body] = msg[:body].keys.first
@@ -346,7 +342,7 @@ module Carnivore
346
342
  end
347
343
  msg[:body].rewind
348
344
  else
349
- msg[:body] = req.body.to_s
345
+ msg[:body] = req.body.read
350
346
  end
351
347
  if(msg[:headers][:authorization])
352
348
  user, pass = Base64.urlsafe_decode64(
@@ -1,7 +1,6 @@
1
1
  require 'blockenspiel'
2
2
  require 'singleton'
3
3
  require 'carnivore/utils'
4
- require 'celluloid'
5
4
 
6
5
  module Carnivore
7
6
  module Http
@@ -12,7 +11,8 @@ module Carnivore
12
11
  # End point
13
12
  class Endpoint
14
13
 
15
- include Celluloid
14
+ include Zoidberg::SoftShell
15
+ include Zoidberg::Supervise
16
16
  include Carnivore::Utils::Params
17
17
  include Carnivore::Utils::Logging
18
18
 
@@ -58,7 +58,7 @@ module Carnivore
58
58
  end
59
59
 
60
60
  include Carnivore::Utils::Params
61
- include Celluloid::Logger
61
+ include Carnivore::Utils::Logging
62
62
  include Blockenspiel::DSL
63
63
 
64
64
  # @return [Hash] static path endpoints
@@ -232,7 +232,7 @@ module Carnivore
232
232
  #
233
233
  # @yield new API block
234
234
  def define(&block)
235
- store(Celluloid.uuid, block)
235
+ store(Zoidberg.uuid, block)
236
236
  end
237
237
 
238
238
  # Store block
@@ -5,7 +5,8 @@ module Carnivore
5
5
 
6
6
  class RetryDelivery
7
7
 
8
- include Celluloid
8
+ include Zoidberg::SoftShell
9
+ include Zoidberg::Supervise
9
10
  include Carnivore::Utils::Logging
10
11
 
11
12
  # @return [String] message directory
@@ -24,13 +24,21 @@ module Carnivore
24
24
  # @return [Hash]
25
25
  def parse_query_string(string)
26
26
  unless(string.to_s.empty?)
27
- args = CGI.parse(string)
27
+ args = Rack::Utils.parse_nested_query(string)
28
28
  format_query_args(args)
29
29
  else
30
- {}
30
+ Smash.new
31
31
  end
32
32
  end
33
33
 
34
+ # Generate query string
35
+ #
36
+ # @param hash [Hash] request parameters
37
+ # @return [String]
38
+ def dump_query_string(hash)
39
+ Rack::Utils.build_nested_query(hash)
40
+ end
41
+
34
42
  # Cast hash values when possible
35
43
  #
36
44
  # @param args [Hash]
@@ -1,6 +1,6 @@
1
1
  module Carnivore
2
2
  module Http
3
3
  # current library version
4
- VERSION = Gem::Version.new('0.2.8')
4
+ VERSION = Gem::Version.new('0.3.0')
5
5
  end
6
6
  end
@@ -5,6 +5,7 @@ require 'multi_json'
5
5
  module Carnivore
6
6
  # HTTP namespace
7
7
  module Http
8
+ autoload :App, 'carnivore-http/app'
8
9
  autoload :PointBuilder, 'carnivore-http/point_builder'
9
10
  autoload :RetryDelivery, 'carnivore-http/retry_delivery'
10
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carnivore-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-13 00:00:00.000000000 Z
11
+ date: 2015-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: carnivore
@@ -16,36 +16,84 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.8
19
+ version: 1.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: 0.1.8
29
+ version: 1.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: puma
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 2.13.4
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 2.13.4
27
47
  - !ruby/object:Gem::Dependency
28
- name: reel
48
+ name: rack
29
49
  requirement: !ruby/object:Gem::Requirement
30
50
  requirements:
31
51
  - - "~>"
32
52
  - !ruby/object:Gem::Version
33
- version: 0.5.0
53
+ version: 1.6.4
34
54
  type: :runtime
35
55
  prerelease: false
36
56
  version_requirements: !ruby/object:Gem::Requirement
37
57
  requirements:
38
58
  - - "~>"
39
59
  - !ruby/object:Gem::Version
40
- version: 0.5.0
60
+ version: 1.6.4
41
61
  - !ruby/object:Gem::Dependency
42
62
  name: blockenspiel
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 0.4.5
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.4.5
75
+ - !ruby/object:Gem::Dependency
76
+ name: htauth
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 2.0.0
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 2.0.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: http
43
91
  requirement: !ruby/object:Gem::Requirement
44
92
  requirements:
45
93
  - - ">="
46
94
  - !ruby/object:Gem::Version
47
95
  version: '0'
48
- type: :runtime
96
+ type: :development
49
97
  prerelease: false
50
98
  version_requirements: !ruby/object:Gem::Requirement
51
99
  requirements:
@@ -53,13 +101,27 @@ dependencies:
53
101
  - !ruby/object:Gem::Version
54
102
  version: '0'
55
103
  - !ruby/object:Gem::Dependency
56
- name: htauth
104
+ name: minitest
57
105
  requirement: !ruby/object:Gem::Requirement
58
106
  requirements:
59
107
  - - ">="
60
108
  - !ruby/object:Gem::Version
61
109
  version: '0'
62
- type: :runtime
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: pry
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
63
125
  prerelease: false
64
126
  version_requirements: !ruby/object:Gem::Requirement
65
127
  requirements:
@@ -73,13 +135,10 @@ extensions: []
73
135
  extra_rdoc_files: []
74
136
  files:
75
137
  - CHANGELOG.md
76
- - CONTRIBUTING.md
77
- - Gemfile
78
- - LICENSE
79
138
  - README.md
80
- - carnivore-http-0.2.6.gem
81
139
  - carnivore-http.gemspec
82
140
  - lib/carnivore-http.rb
141
+ - lib/carnivore-http/app.rb
83
142
  - lib/carnivore-http/http.rb
84
143
  - lib/carnivore-http/http_endpoints.rb
85
144
  - lib/carnivore-http/http_paths.rb
@@ -88,9 +147,6 @@ files:
88
147
  - lib/carnivore-http/retry_delivery.rb
89
148
  - lib/carnivore-http/utils.rb
90
149
  - lib/carnivore-http/version.rb
91
- - test/spec.rb
92
- - test/specs/http.rb
93
- - test/specs/paths.rb
94
150
  homepage: https://github.com/carnivore-rb/carnivore-http
95
151
  licenses:
96
152
  - Apache 2.0
@@ -111,9 +167,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
167
  version: '0'
112
168
  requirements: []
113
169
  rubyforge_project:
114
- rubygems_version: 2.2.2
170
+ rubygems_version: 2.4.8
115
171
  signing_key:
116
172
  specification_version: 4
117
173
  summary: Message processing helper
118
174
  test_files: []
119
- has_rdoc:
data/CONTRIBUTING.md DELETED
@@ -1,25 +0,0 @@
1
- # Contributing
2
-
3
- ## Branches
4
-
5
- ### `master` branch
6
-
7
- The master branch is the current stable released version.
8
-
9
- ### `develop` branch
10
-
11
- The develop branch is the current edge of development.
12
-
13
- ## Pull requests
14
-
15
- * https://github.com/carnivore-rb/carnivore-http/pulls
16
-
17
- Please base all pull requests of the `develop` branch. Merges to
18
- `master` only occur through the `develop` branch. Pull requests
19
- based on `master` will likely be cherry picked.
20
-
21
- ## Issues
22
-
23
- Need to report an issue? Use the github issues:
24
-
25
- * https://github.com/carnivore-rb/carnivore-http/issues
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
data/LICENSE DELETED
@@ -1,13 +0,0 @@
1
- Copyright 2014 Chris Roberts
2
-
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
6
-
7
- http://www.apache.org/licenses/LICENSE-2.0
8
-
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
Binary file
data/test/spec.rb DELETED
@@ -1 +0,0 @@
1
- require 'carnivore/spec_helper'
data/test/specs/http.rb DELETED
@@ -1,69 +0,0 @@
1
- require 'http'
2
- require 'minitest/autorun'
3
- require 'carnivore-http'
4
-
5
- describe 'Carnivore::Source::Http' do
6
-
7
- before do
8
- MessageStore.init
9
- Carnivore::Source.build(
10
- :type => :http,
11
- :args => {
12
- :name => :http_source,
13
- :bind => '127.0.0.1',
14
- :port => '8705'
15
- }
16
- ).add_callback(:store) do |message|
17
- MessageStore.messages.push(message[:message][:body])
18
- message.confirm!
19
- end
20
- @runner = Thread.new{ Carnivore.start! }
21
- source_wait
22
- end
23
-
24
- after do
25
- @runner.terminate
26
- end
27
-
28
- describe 'HTTP source based communication' do
29
-
30
- before do
31
- MessageStore.messages.clear
32
- end
33
-
34
- describe 'Building an HTTP based source' do
35
-
36
- it 'returns the source' do
37
- Carnivore::Supervisor.supervisor[:http_source].wont_be_nil
38
- end
39
-
40
- end
41
-
42
- describe 'message transmissions' do
43
-
44
- it 'should accept message transmits' do
45
- Carnivore::Supervisor.supervisor[:http_source].transmit('test message')
46
- end
47
-
48
- it 'should receive messages' do
49
- Carnivore::Supervisor.supervisor[:http_source].transmit('test message 2')
50
- source_wait(2) do
51
- !MessageStore.messages.empty?
52
- end
53
- MessageStore.messages.wont_be_empty
54
- MessageStore.messages.pop.must_equal 'test message 2'
55
- end
56
-
57
- it 'should accept http requests' do
58
- HTTP.get('http://127.0.0.1:8705/')
59
- source_wait(2) do
60
- !MessageStore.messages.empty?
61
- end
62
- MessageStore.messages.wont_be_empty
63
- MessageStore.messages.pop.wont_be_nil
64
- end
65
-
66
- end
67
- end
68
-
69
- end
data/test/specs/paths.rb DELETED
@@ -1,124 +0,0 @@
1
- require 'http'
2
- require 'minitest/autorun'
3
- require 'carnivore-http'
4
-
5
-
6
- describe 'Carnivore::Source::Http' do
7
-
8
- before do
9
- MessageStore.init
10
-
11
- unless(@runner)
12
- Carnivore::Source.build(
13
- :type => :http_paths,
14
- :args => {
15
- :name => :fubar_source,
16
- :path => '/fubar',
17
- :method => :post,
18
- :bind => '127.0.0.1',
19
- :port => '8706',
20
- :auto_respond => false
21
- }
22
- ).add_callback(:store) do |message|
23
- MessageStore.messages.push(message[:message][:body])
24
- message.confirm!(:response_body => 'custom response')
25
- end
26
- Carnivore::Source.build(
27
- :type => :http_paths,
28
- :args => {
29
- :name => :ohai_source,
30
- :path => '/ohai',
31
- :method => :get,
32
- :bind => '127.0.0.1',
33
- :port => '8706'
34
- }
35
- ).add_callback(:store) do |message|
36
- MessageStore.messages.push(message[:message][:body])
37
- end
38
- Carnivore::Source.build(
39
- :type => :http_paths,
40
- :args => {
41
- :name => :glob_source,
42
- :path => '/glob/v*/*',
43
- :method => :get,
44
- :bind => '127.0.0.1',
45
- :port => '8706'
46
- }
47
- ).add_callback(:store) do |message|
48
- MessageStore.messages.push(message[:message][:body])
49
- end
50
- @runner = Thread.new{ Carnivore.start! }
51
- source_wait
52
- end
53
- end
54
-
55
- after do
56
- @runner.terminate
57
- end
58
-
59
- describe 'HTTP source based communication' do
60
-
61
- before do
62
- MessageStore.messages.clear
63
- end
64
-
65
- describe 'Building an HTTP based source' do
66
-
67
- it 'returns the sources' do
68
- Carnivore::Supervisor.supervisor[:fubar_source].wont_be_nil
69
- Carnivore::Supervisor.supervisor[:ohai_source].wont_be_nil
70
- end
71
-
72
- end
73
-
74
- describe 'source message transmissions' do
75
-
76
- it 'should accept message transmits' do
77
- Carnivore::Supervisor.supervisor[:fubar_source].transmit('test message')
78
- Carnivore::Supervisor.supervisor[:ohai_source].transmit('test message')
79
- end
80
-
81
- it 'should receive messages' do
82
- Carnivore::Supervisor.supervisor[:fubar_source].transmit('test message to fubar')
83
- source_wait(4) do
84
- !MessageStore.messages.empty?
85
- end
86
- MessageStore.messages.wont_be_empty
87
- MessageStore.messages.pop.must_equal 'test message to fubar'
88
- Carnivore::Supervisor.supervisor[:ohai_source].transmit('test message to ohai')
89
- source_wait(4) do
90
- !MessageStore.messages.empty?
91
- end
92
- MessageStore.messages.wont_be_empty
93
- MessageStore.messages.pop.must_equal 'test message to ohai'
94
- end
95
-
96
- end
97
-
98
- describe 'HTTP message transmissions' do
99
-
100
- it 'should receive messages and provide custom response' do
101
- response = HTTP.post('http://127.0.0.1:8706/fubar', :body => 'test')
102
- response.body.to_s.must_equal 'custom response'
103
- source_wait{ !MessageStore.messages.empty? }
104
- MessageStore.messages.pop.must_equal 'test'
105
- end
106
-
107
- it 'should receive messages and provide default response' do
108
- response = HTTP.get('http://127.0.0.1:8706/ohai')
109
- source_wait{ !MessageStore.messages.empty? }
110
- response.body.to_s.must_equal 'So long and thanks for all the fish!'
111
- MessageStore.messages.pop.must_be :empty?
112
- end
113
-
114
- it 'should receive messages via glob matching' do
115
- response = HTTP.get('http://127.0.0.1:8706/glob/v2/things')
116
- source_wait{ !MessageStore.messages.empty? }
117
- response.body.to_s.must_equal 'So long and thanks for all the fish!'
118
- MessageStore.messages.pop.must_be :empty?
119
- end
120
-
121
- end
122
- end
123
-
124
- end