goliath 1.0.4 → 1.0.6

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

Potentially problematic release.


This version of goliath might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -4
  3. data/goliath.gemspec +11 -7
  4. data/lib/goliath/api.rb +1 -1
  5. data/lib/goliath/connection.rb +15 -11
  6. data/lib/goliath/constants.rb +1 -0
  7. data/lib/goliath/rack/default_response_format.rb +3 -9
  8. data/lib/goliath/rack/formatters/json.rb +1 -1
  9. data/lib/goliath/rack/jsonp.rb +7 -2
  10. data/lib/goliath/rack/params.rb +4 -3
  11. data/lib/goliath/rack/validator.rb +1 -1
  12. data/lib/goliath/request.rb +37 -20
  13. data/lib/goliath/response.rb +2 -0
  14. data/lib/goliath/runner.rb +4 -3
  15. data/lib/goliath/server.rb +17 -2
  16. data/lib/goliath/test_helper_sse.rb +76 -0
  17. data/lib/goliath/validation/error.rb +4 -1
  18. data/lib/goliath/version.rb +1 -1
  19. data/spec/integration/async_request_processing.rb +2 -2
  20. data/spec/integration/chunked_streaming_spec.rb +1 -1
  21. data/spec/integration/early_abort_spec.rb +6 -6
  22. data/spec/integration/echo_spec.rb +7 -7
  23. data/spec/integration/empty_body_spec.rb +2 -2
  24. data/spec/integration/event_stream_spec.rb +50 -0
  25. data/spec/integration/exception_handling_spec.rb +202 -0
  26. data/spec/integration/http_log_spec.rb +16 -16
  27. data/spec/integration/jsonp_spec.rb +61 -10
  28. data/spec/integration/keepalive_spec.rb +2 -2
  29. data/spec/integration/pipelining_spec.rb +3 -3
  30. data/spec/integration/reloader_spec.rb +3 -3
  31. data/spec/integration/template_spec.rb +7 -7
  32. data/spec/integration/test_helper_spec.rb +3 -3
  33. data/spec/integration/trace_spec.rb +2 -2
  34. data/spec/integration/valid_spec.rb +25 -5
  35. data/spec/integration/websocket_spec.rb +2 -2
  36. data/spec/spec_helper.rb +1 -3
  37. data/spec/unit/api_spec.rb +1 -1
  38. data/spec/unit/connection_spec.rb +8 -8
  39. data/spec/unit/console_spec.rb +3 -3
  40. data/spec/unit/env_spec.rb +9 -9
  41. data/spec/unit/headers_spec.rb +8 -8
  42. data/spec/unit/rack/default_mime_type_spec.rb +3 -3
  43. data/spec/unit/rack/formatters/json_spec.rb +35 -13
  44. data/spec/unit/rack/formatters/plist_spec.rb +8 -8
  45. data/spec/unit/rack/formatters/xml_spec.rb +18 -18
  46. data/spec/unit/rack/formatters/yaml_spec.rb +13 -13
  47. data/spec/unit/rack/heartbeat_spec.rb +15 -15
  48. data/spec/unit/rack/params_spec.rb +99 -62
  49. data/spec/unit/rack/render_spec.rb +14 -14
  50. data/spec/unit/rack/validation/boolean_value_spec.rb +6 -6
  51. data/spec/unit/rack/validation/default_params_spec.rb +13 -13
  52. data/spec/unit/rack/validation/numeric_range_spec.rb +17 -17
  53. data/spec/unit/rack/validation/param_spec.rb +75 -75
  54. data/spec/unit/rack/validation/request_method_spec.rb +9 -9
  55. data/spec/unit/rack/validation/required_param_spec.rb +39 -39
  56. data/spec/unit/rack/validation/required_value_spec.rb +19 -19
  57. data/spec/unit/request_spec.rb +18 -18
  58. data/spec/unit/response_spec.rb +6 -6
  59. data/spec/unit/runner_spec.rb +31 -31
  60. data/spec/unit/server_spec.rb +21 -21
  61. data/spec/unit/validation/standard_http_errors_spec.rb +6 -6
  62. metadata +149 -94
  63. data/examples/around.rb +0 -38
  64. data/examples/clone.rb +0 -26
  65. data/examples/router.rb +0 -15
  66. data/examples/test.rb +0 -31
  67. data/examples/upload.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c49ef49ca7b81daf949084e54013fe33751d9461
4
- data.tar.gz: c9bd1ae49086f0fa8ff01dd8617994156ff1b448
3
+ metadata.gz: 2bfab8c9906a1737abc3e7cdf163a52e13e516c7
4
+ data.tar.gz: a1ac6184a35dbfe56a04d117872d31b7c3647c71
5
5
  SHA512:
6
- metadata.gz: 4d9e9698db573be7495f89394bae42ad613397489a5e979994b29ee1518238c73884444f8be2bdef7b7bea936a1bced59b07e7d85eb5cec5dc1e2fb75758de8a
7
- data.tar.gz: 4a800250911ccb181214f88c43442539b18cb96676b960ffa8e7a3acddc1aa3413d83c58bab1b389280d09f4822ac97f815be9db80cd8dde52483a006e991062
6
+ metadata.gz: 833e67c341fde76ca1e5d69430f0f0c2d70186de0474979421fbbeeac78d5755eb79bfde0495e606105fb71f0291a6e8fa6be4c3e55ea6da2d663b8aa6c20937
7
+ data.tar.gz: 15de365fe175166182ead10d6c397971d33391e190f994552216433ebf121be35732805cd56cc364c3f9f0a5361589cd4205954014288fd52ab995d6807ccc93
data/README.md CHANGED
@@ -64,13 +64,14 @@ Goliath has been used in production environments for 2+ years, across many diffe
64
64
  * [Middleware](https://github.com/postrank-labs/goliath/wiki/Middleware)
65
65
  * [Configuration](https://github.com/postrank-labs/goliath/wiki/Configuration)
66
66
  * [Plugins](https://github.com/postrank-labs/goliath/wiki/Plugins)
67
+ * [Zero Downtime Restart](https://github.com/postrank-labs/goliath/wiki/Zero-downtime-restart)
68
+
67
69
 
68
70
  ### Hands-on applications:
69
71
 
70
- If you are you new to EventMachine, or want a detailed walk-through of building a Goliath powered API? You're in luck, we have two super-awesome Peepcode screencasts which will teach you all you need to know:
72
+ If you are you new to EventMachine, or want a detailed walk-through of building a Goliath powered API? You're in luck, a super-awesome Pluralsight screencast which will teach you all you need to know:
71
73
 
72
- * [Meet EventMachine: Part 1](http://peepcode.com/products/eventmachine) - introduction to EM, Fibers, etc.
73
- * [Meet EventMachine: Part 2](http://peepcode.com/products/eventmachine-ii) - building an API with Goliath
74
+ * [Meet EventMachine](http://www.pluralsight.com/courses/meet-eventmachine) - introduction to EM, Fibers, building an API with Goliath
74
75
 
75
76
  Additionally, you can also watch this presentation from GoGaRuCo 2011, which describes the design and motivation behind Goliath:
76
77
 
@@ -90,4 +91,4 @@ Other resources:
90
91
 
91
92
  ## License & Acknowledgments
92
93
 
93
- Goliath is distributed under the MIT license, for full details please see the LICENSE file.
94
+ Goliath is distributed under the MIT license, for full details please see the LICENSE file.
data/goliath.gemspec CHANGED
@@ -12,13 +12,14 @@ Gem::Specification.new do |s|
12
12
  s.summary = 'Async framework for writing API servers'
13
13
  s.description = s.summary
14
14
 
15
- s.required_ruby_version = '>=1.9.2'
15
+ s.required_ruby_version = '>=2.1.0'
16
16
 
17
17
  s.add_dependency 'eventmachine', '>= 1.0.0.beta.4'
18
18
  s.add_dependency 'em-synchrony', '>= 1.0.0'
19
- s.add_dependency 'em-websocket', "0.3.8"
20
- s.add_dependency 'http_parser.rb', '0.6.0'
19
+ s.add_dependency 'em-websocket', '0.3.8'
20
+ s.add_dependency 'http_parser.rb', '>= 0.6.0'
21
21
  s.add_dependency 'log4r'
22
+ s.add_dependency 'einhorn'
22
23
 
23
24
  s.add_dependency 'rack', '>=1.2.2'
24
25
  s.add_dependency 'rack-contrib'
@@ -27,7 +28,8 @@ Gem::Specification.new do |s|
27
28
  s.add_dependency 'multi_json'
28
29
 
29
30
  s.add_development_dependency 'rake', '>=0.8.7'
30
- s.add_development_dependency 'rspec', '>2.0'
31
+ s.add_development_dependency 'rspec', '~> 3.0'
32
+ s.add_development_dependency 'test-unit'
31
33
  s.add_development_dependency 'nokogiri'
32
34
  s.add_development_dependency 'em-http-request', '>=1.0.0'
33
35
  s.add_development_dependency 'em-mongo', '~> 0.4.0'
@@ -35,14 +37,16 @@ Gem::Specification.new do |s|
35
37
  s.add_development_dependency 'multipart_body'
36
38
  s.add_development_dependency 'amqp', '>=0.7.1'
37
39
  s.add_development_dependency 'em-websocket-client'
40
+ s.add_development_dependency 'em-eventsource'
41
+ s.add_development_dependency 'rack', '< 2'
38
42
 
39
43
  s.add_development_dependency 'tilt', '>=1.2.2'
40
44
  s.add_development_dependency 'haml', '>=3.0.25'
41
45
  s.add_development_dependency 'yard'
42
46
 
43
- s.add_development_dependency 'guard', '~> 1.8.3'
44
- s.add_development_dependency 'guard-rspec', '~> 3.1.0'
45
- s.add_development_dependency 'listen', '~> 1.3.1'
47
+ s.add_development_dependency 'guard', '~> 2.0'
48
+ s.add_development_dependency 'guard-rspec', '~> 4.0'
49
+ s.add_development_dependency 'listen', '~> 2.0'
46
50
 
47
51
  if RUBY_PLATFORM != 'java'
48
52
  s.add_development_dependency 'yajl-ruby'
data/lib/goliath/api.rb CHANGED
@@ -172,7 +172,7 @@ module Goliath
172
172
 
173
173
  rescue Goliath::Validation::Error => e
174
174
  env[RACK_EXCEPTION] = e
175
- env[ASYNC_CALLBACK].call(validation_error(e.status_code, e.message))
175
+ env[ASYNC_CALLBACK].call(validation_error(e.status_code, e.message, e.headers))
176
176
 
177
177
  rescue Exception => e
178
178
  logthis = "#{e.backtrace[0]}: #{e.message} (#{e.class})\n"
@@ -23,12 +23,13 @@ module Goliath
23
23
  @parser = Http::Parser.new
24
24
  @parser.on_headers_complete = proc do |h|
25
25
  env = Goliath::Env.new
26
- env[SERVER_PORT] = port.to_s
27
- env[RACK_LOGGER] = logger
28
- env[OPTIONS] = options
29
- env[STATUS] = status
30
- env[CONFIG] = config
31
- env[REMOTE_ADDR] = remote_address
26
+ env[SERVER_PORT] = port.to_s
27
+ env[RACK_LOGGER] = logger
28
+ env[OPTIONS] = options
29
+ env[STATUS] = status
30
+ env[CONFIG] = config
31
+ env[REMOTE_ADDR] = remote_address
32
+ env[RACK_URL_SCHEME] = options[:ssl] ? "https" : "http"
32
33
 
33
34
  r = Goliath::Request.new(@app, self, env)
34
35
  r.parse_header(h, @parser) do
@@ -41,7 +42,8 @@ module Goliath
41
42
  end
42
43
 
43
44
  @parser.on_body = proc do |data|
44
- @requests.first.parse(data)
45
+ req = @requests.first
46
+ req.parse(data) unless req.env[:terminate_connection]
45
47
  end
46
48
 
47
49
  @parser.on_message_complete = proc do
@@ -83,15 +85,17 @@ module Goliath
83
85
  end
84
86
 
85
87
  def terminate_request(keep_alive)
86
- if req = @pending.shift
87
- @current = req
88
- @current.succeed
89
- elsif @current
88
+ if @current
90
89
  @current.close
91
90
  @current = nil
92
91
  end
93
92
 
94
93
  close_connection_after_writing rescue nil if !keep_alive
94
+
95
+ if req = @pending.shift
96
+ @current = req
97
+ @current.succeed
98
+ end
95
99
  end
96
100
 
97
101
  def remote_address
@@ -23,6 +23,7 @@ module Goliath
23
23
  RACK_VERSION_NUM = [1, 0]
24
24
  RACK_LOGGER = 'rack.logger'
25
25
  RACK_EXCEPTION = 'rack.exception'
26
+ RACK_URL_SCHEME = 'rack.url_scheme'
26
27
 
27
28
  ASYNC_CALLBACK = 'async.callback'
28
29
  ASYNC_HEADERS = 'async.headers'
@@ -4,17 +4,11 @@ module Goliath
4
4
  include Goliath::Rack::AsyncMiddleware
5
5
 
6
6
  def post_process(env, status, headers, body)
7
- return [status, headers, body] if body.respond_to?(:to_ary)
8
-
9
- new_body = []
10
- if body.respond_to?(:each)
11
- body.each { |chunk| new_body << chunk }
7
+ if body.is_a?(String)
8
+ [status, headers, [body]]
12
9
  else
13
- new_body << body
10
+ [status, headers, body]
14
11
  end
15
- new_body.collect! { |item| item.to_s }
16
-
17
- [status, headers, new_body.flatten]
18
12
  end
19
13
  end
20
14
  end
@@ -19,7 +19,7 @@ module Goliath
19
19
  end
20
20
 
21
21
  def json_response?(headers)
22
- headers['Content-Type'] =~ %r{^application/(json|javascript)}
22
+ headers['Content-Type'] =~ %r{^application/((vnd\.api\+)?json|javascript)}
23
23
  end
24
24
  end
25
25
  end
@@ -18,10 +18,15 @@ module Goliath
18
18
  response = body
19
19
  end
20
20
 
21
+ response = "#{env.params['callback']}(#{response})"
22
+
21
23
  headers[Goliath::Constants::CONTENT_TYPE] = 'application/javascript'
22
- [status, headers, ["#{env.params['callback']}(#{response})"]]
24
+ if headers['Content-Length']
25
+ headers['Content-Length'] = response.bytesize.to_s
26
+ end
27
+
28
+ [status, headers, [response]]
23
29
  end
24
30
  end
25
31
  end
26
32
  end
27
-
@@ -4,7 +4,7 @@ require 'rack/utils'
4
4
  module Goliath
5
5
  module Rack
6
6
  URL_ENCODED = %r{^application/x-www-form-urlencoded}
7
- JSON_ENCODED = %r{^application/json}
7
+ JSON_ENCODED = %r{^application/((vnd\.api\+)?json|javascript)}
8
8
 
9
9
  # A middle ware to parse params. This will parse both the
10
10
  # query string parameters and the body and place them into
@@ -60,10 +60,11 @@ module Goliath
60
60
  end
61
61
 
62
62
  def call(env)
63
- Goliath::Rack::Validator.safely(env) do
63
+ error_response = Goliath::Rack::Validator.safely(env) do
64
64
  env['params'] = retrieve_params(env)
65
- @app.call(env)
65
+ nil
66
66
  end
67
+ error_response || @app.call(env)
67
68
  end
68
69
  end
69
70
  end
@@ -39,7 +39,7 @@ module Goliath
39
39
  begin
40
40
  yield
41
41
  rescue Goliath::Validation::Error => e
42
- validation_error(e.status_code, e.message, headers)
42
+ validation_error(e.status_code, e.message, e.headers.merge(headers))
43
43
  rescue Exception => e
44
44
  env.logger.error(e.message)
45
45
  env.logger.error(e.backtrace.join("\n"))
@@ -92,7 +92,7 @@ module Goliath
92
92
 
93
93
  @env[REQUEST_METHOD] = parser.http_method
94
94
  @env[REQUEST_URI] = parser.request_url
95
- @env[QUERY_STRING] = uri.query
95
+ @env[QUERY_STRING] = uri.query || ''.freeze
96
96
  @env[HTTP_VERSION] = parser.http_version.join('.')
97
97
  @env[SCRIPT_NAME] = ""
98
98
  @env[REQUEST_PATH] = uri.path
@@ -197,22 +197,14 @@ module Goliath
197
197
  callback do
198
198
  begin
199
199
  @response.status, @response.headers, @response.body = status, headers, body
200
- @response.each { |chunk| @conn.send_data(chunk) }
201
-
202
- elapsed_time = (Time.now.to_f - @env[:start_time]) * 1000
203
- begin
204
- Goliath::Request.log_block.call(@env, @response, elapsed_time)
205
- rescue => err
206
- # prevent an infinite loop if the block raised an error
207
- @env[RACK_LOGGER].error("log block raised #{err}")
208
- end
209
200
 
210
- @conn.terminate_request(keep_alive)
201
+ stream_data(@response.each) do
202
+ terminate_request
203
+ end
211
204
  rescue Exception => e
212
205
  server_exception(e)
213
206
  end
214
207
  end
215
-
216
208
  rescue Exception => e
217
209
  server_exception(e)
218
210
  end
@@ -220,28 +212,53 @@ module Goliath
220
212
 
221
213
  private
222
214
 
215
+ # Writes each chunk of the response data in a new tick. This achieves
216
+ # streaming, because EventMachine flushes the sent data to the socket at
217
+ # the end of each tick.
218
+ def stream_data(chunks, &block)
219
+ @conn.send_data(chunks.next)
220
+ EM.next_tick { stream_data(chunks, &block) }
221
+ rescue StopIteration
222
+ block.call
223
+ rescue Exception => e
224
+ server_exception(e)
225
+ end
226
+
227
+ # Logs the response time and terminates the request.
228
+ def terminate_request
229
+ elapsed_time = (Time.now.to_f - @env[:start_time]) * 1000
230
+ begin
231
+ Goliath::Request.log_block.call(@env, @response, elapsed_time)
232
+ rescue => err
233
+ # prevent an infinite loop if the block raised an error
234
+ @env[RACK_LOGGER].error("log block raised #{err}")
235
+ end
236
+
237
+ @conn.terminate_request(keep_alive)
238
+ end
239
+
223
240
  # Handles logging server exceptions
224
241
  #
225
242
  # @param e [Exception] The exception to log
226
243
  # @return [Nil]
227
244
  def server_exception(e)
228
245
  if e.is_a?(Goliath::Validation::Error)
229
- status, headers, body = [e.status_code, {}, ('{"error":"%s"}' % e.message)]
246
+ status, headers, body = [e.status_code, e.headers, ('{"error":"%s"}' % e.message)]
230
247
  else
231
- @env[RACK_LOGGER].error("#{e.message}\n#{e.backtrace.join("\n")}")
232
- message = Goliath.env?(:production) ? 'An error happened' : e.message
248
+ logthis = "#{e.backtrace[0]}: #{e.message} (#{e.class})\n"
249
+ e.backtrace[1..-1].each do |bt|
250
+ logthis += " from #{bt}\n"
251
+ end
252
+ @env.logger.error(logthis)
253
+ @env[RACK_EXCEPTION] = e
233
254
 
255
+ message = Goliath.env?(:production) ? 'An error happened' : e.message
234
256
  status, headers, body = [500, {}, message]
235
257
  end
236
258
 
237
259
  headers['Content-Length'] = body.bytesize.to_s
238
260
  @env[:terminate_connection] = true
239
261
  post_process([status, headers, body])
240
-
241
- # Mark the request as complete to force a flush on the response.
242
- # Note: #on_body and #response hooks may still fire if the data
243
- # is already in the parser buffer.
244
- succeed
245
262
  end
246
263
 
247
264
  # Used to determine if the connection should be kept open
@@ -79,6 +79,8 @@ module Goliath
79
79
  # @yield [String] The header line, headers and body content
80
80
  # @return [Nil]
81
81
  def each
82
+ return enum_for(__method__) unless block_given?
83
+
82
84
  yield head
83
85
  yield headers_output
84
86
 
@@ -150,6 +150,7 @@ module Goliath
150
150
  opts.on('-a', '--address HOST', "Bind to HOST address (default: #{@options[:address]})") { |addr| @options[:address] = addr }
151
151
  opts.on('-p', '--port PORT', "Use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i }
152
152
  opts.on('-S', '--socket FILE', "Bind to unix domain socket") { |v| @options[:address] = v; @options[:port] = nil }
153
+ opts.on('-E', '--einhorn', "Use Einhorn socket manager") { |v| @options[:einhorn] = true }
153
154
 
154
155
  opts.separator ""
155
156
  opts.separator "Daemon options:"
@@ -211,7 +212,8 @@ module Goliath
211
212
 
212
213
  File.umask(0000)
213
214
 
214
- stdout_log_file = "#{File.dirname(@log_file)}/#{File.basename(@log_file)}_stdout.log"
215
+ log_extension = File.extname(@log_file)
216
+ stdout_log_file = "#{File.dirname(@log_file)}/#{File.basename(@log_file, log_extension)}_stdout#{log_extension}"
215
217
 
216
218
  STDIN.reopen("/dev/null")
217
219
  STDOUT.reopen(stdout_log_file, "a")
@@ -296,8 +298,7 @@ module Goliath
296
298
  # @return [Nil]
297
299
  def run_server
298
300
  log = setup_logger
299
-
300
- log.info("Starting server on #{@address}:#{@port} in #{Goliath.env} mode. Watch out for stones.")
301
+ log.info("Starting server on http#{ @server_options[:ssl] ? 's' : nil }://#{@address}:#{@port} in #{Goliath.env} mode. Watch out for stones.")
301
302
 
302
303
  server = setup_server(log)
303
304
  server.api.setup if server.api.respond_to?(:setup)
@@ -1,4 +1,5 @@
1
1
  require 'em-synchrony'
2
+ require 'einhorn'
2
3
  require 'goliath/connection'
3
4
  require 'goliath/goliath'
4
5
 
@@ -83,7 +84,7 @@ module Goliath
83
84
 
84
85
  EM.set_effective_user(options[:user]) if options[:user]
85
86
 
86
- config[Goliath::Constants::GOLIATH_SIGNATURE] = EM.start_server(address, port, Goliath::Connection) do |conn|
87
+ config[Goliath::Constants::GOLIATH_SIGNATURE] = start_server(options) do |conn|
87
88
  if options[:ssl]
88
89
  conn.start_tls(
89
90
  :private_key_file => options[:ssl_key],
@@ -105,9 +106,23 @@ module Goliath
105
106
  end
106
107
  end
107
108
 
109
+ def start_server(options, &blk)
110
+ if options[:einhorn]
111
+ fd_num = Einhorn::Worker.socket!
112
+ socket = Socket.for_fd(fd_num)
113
+
114
+ EM.attach_server(socket, Goliath::Connection, &blk)
115
+ else
116
+ EM.start_server(address, port, Goliath::Connection, &blk)
117
+ end
118
+ end
119
+
108
120
  # Stops the server running.
109
121
  def stop
110
- EM.stop
122
+ EM.add_timer(0) do
123
+ logger.info('Stopping server...')
124
+ EM.stop
125
+ end
111
126
  end
112
127
 
113
128
  # Loads a configuration file
@@ -0,0 +1,76 @@
1
+ require 'em-eventsource'
2
+
3
+ module Goliath
4
+ module TestHelper
5
+ class SSEHelper
6
+ attr_reader :connection
7
+ def initialize(url)
8
+ @message_queue = EM::Queue.new
9
+ @named_queues = {}
10
+ @connection = EM::EventSource.new(url)
11
+ end
12
+
13
+ def listen
14
+ @connection.message do |message|
15
+ @message_queue.push(message)
16
+ end
17
+ end
18
+
19
+ def listen_to(name)
20
+ queue = (@named_queues[name] ||= [])
21
+ @connection.on(name) do |message|
22
+ queue.push(message)
23
+ end
24
+ end
25
+
26
+ def receive
27
+ pop_queue(@message_queue)
28
+ end
29
+
30
+ def receive_on(name)
31
+ queue = @named_queues.fetch(name) do
32
+ raise ArgumentError, "You have to call listen_to('#{name}') first"
33
+ end
34
+
35
+ pop_queue(queue)
36
+ end
37
+
38
+ def with_async_http
39
+ klass = EM::HttpConnection
40
+ if klass.instance_methods.include?(:aget)
41
+ begin
42
+ klass.send(:class_eval) do
43
+ alias :sget :get
44
+ alias :get :aget
45
+ end
46
+ yield if block_given?
47
+ ensure
48
+ klass.send(:class_eval) do
49
+ alias :get :sget
50
+ remove_method :sget
51
+ end
52
+ end
53
+ else
54
+ yield if block_given?
55
+ end
56
+ end
57
+
58
+ protected
59
+
60
+ def pop_queue(queue)
61
+ fiber = Fiber.current
62
+ queue.pop { |m| fiber.resume(m) }
63
+ Fiber.yield
64
+ end
65
+ end
66
+
67
+ def sse_client_connect(path,&blk)
68
+ url = "http://localhost:#{@test_server_port}#{path}"
69
+ client = SSEHelper.new(url)
70
+ client.with_async_http { client.connection.start }
71
+ client.listen
72
+ Fiber.new { blk.call(client) }.resume if blk
73
+ stop
74
+ end
75
+ end
76
+ end
@@ -4,6 +4,8 @@ module Goliath
4
4
  class Error < StandardError
5
5
  # The status code to return from the error handler
6
6
  attr_accessor :status_code
7
+ # The headers to return from the error handler
8
+ attr_accessor :headers
7
9
 
8
10
  # Create a new Goliath::Validation::Error.
9
11
  #
@@ -13,9 +15,10 @@ module Goliath
13
15
  # @param status_code [Integer] The status code to return
14
16
  # @param message [String] The error message to return
15
17
  # @return [Goliath::Validation::Error] The Goliath::Validation::Error
16
- def initialize(status_code, message)
18
+ def initialize(status_code, message, headers = {})
17
19
  super(message)
18
20
  @status_code = status_code
21
+ @headers = headers
19
22
  end
20
23
  end
21
24
  end
@@ -1,4 +1,4 @@
1
1
  module Goliath
2
2
  # The current version of Goliath
3
- VERSION = '1.0.4'
3
+ VERSION = '1.0.6'
4
4
  end
@@ -13,8 +13,8 @@ describe 'Async Request processing' do
13
13
 
14
14
  post_request(request_data, err) do |c|
15
15
  resp = MultiJson.load(c.response)
16
- resp['body'].should match('some=data')
17
- resp['head'].should include('X-Upload')
16
+ expect(resp['body']).to match('some=data')
17
+ expect(resp['head']).to include('X-Upload')
18
18
  end
19
19
  end
20
20
  end
@@ -27,7 +27,7 @@ describe "ChunkedStreaming" do
27
27
  it "should stream content" do
28
28
  with_api(ChunkedStreaming, {:verbose => true, :log_stdout => true}) do |server|
29
29
  streaming_client_connect('/streaming') do |client|
30
- client.receive.should == "chunked"
30
+ expect(client.receive).to eq("chunked")
31
31
  end
32
32
  end
33
33
  end
@@ -11,7 +11,7 @@ describe EarlyAbort do
11
11
  it "should return OK" do
12
12
  with_api(EarlyAbort) do
13
13
  get_request({}, err) do |c|
14
- c.response.should == "OK"
14
+ expect(c.response).to eq("OK")
15
15
  end
16
16
  end
17
17
  end
@@ -24,8 +24,8 @@ describe EarlyAbort do
24
24
  }
25
25
 
26
26
  post_request(request_data, err) do |c|
27
- c.response.should == "{\"error\":\"Can't handle requests with X-Crash: true.\"}"
28
- File.exist?("/tmp/goliath-test-error.log").should be_false
27
+ expect(c.response).to eq("{\"error\":\"Can't handle requests with X-Crash: true.\"}")
28
+ expect(File.exist?("/tmp/goliath-test-error.log")).to be false
29
29
  end
30
30
  end
31
31
  end
@@ -35,9 +35,9 @@ describe EarlyAbort do
35
35
  request_data = { :body => "a" * 20 }
36
36
 
37
37
  post_request(request_data, err) do |c|
38
- c.response.should =~ /Payload size can't exceed 10 bytes/
39
- File.exist?("/tmp/goliath-test-error.log").should be_false
38
+ expect(c.response).to match(/Payload size can't exceed 10 bytes/)
39
+ expect(File.exist?("/tmp/goliath-test-error.log")).to be false
40
40
  end
41
41
  end
42
42
  end
43
- end
43
+ end
@@ -10,7 +10,7 @@ describe Echo do
10
10
  with_api(Echo) do
11
11
  get_request({:query => {:echo => 'test'}}, err) do |c|
12
12
  b = MultiJson.load(c.response)
13
- b['response'].should == 'test'
13
+ expect(b['response']).to eq('test')
14
14
  end
15
15
  end
16
16
  end
@@ -19,8 +19,8 @@ describe Echo do
19
19
  with_api(Echo) do
20
20
  get_request({}, err) do |c|
21
21
  b = MultiJson.load(c.response)
22
- b['error'].should_not be_nil
23
- b['error'].should == 'echo identifier missing'
22
+ expect(b['error']).not_to be_nil
23
+ expect(b['error']).to eq('echo identifier missing')
24
24
  end
25
25
  end
26
26
  end
@@ -29,7 +29,7 @@ describe Echo do
29
29
  with_api(Echo) do
30
30
  post_request({:body => {'echo' => 'test'}}, err) do |c|
31
31
  b = MultiJson.load(c.response)
32
- b['response'].should == 'test'
32
+ expect(b['response']).to eq('test')
33
33
  end
34
34
  end
35
35
  end
@@ -42,7 +42,7 @@ describe Echo do
42
42
  post_request({:body => body.to_s,
43
43
  :head => head}, err) do |c|
44
44
  b = MultiJson.load(c.response)
45
- b['response'].should == 'test'
45
+ expect(b['response']).to eq('test')
46
46
  end
47
47
  end
48
48
  end
@@ -55,7 +55,7 @@ describe Echo do
55
55
  post_request({:body => body.to_s,
56
56
  :head => head}, err) do |c|
57
57
  b = MultiJson.load(c.response)
58
- b['response'].should == 'My Echo'
58
+ expect(b['response']).to eq('My Echo')
59
59
  end
60
60
  end
61
61
  end
@@ -64,7 +64,7 @@ describe Echo do
64
64
  with_api(Echo) do
65
65
  patch_request({:body => {'echo' => 'test'}}, err) do |c|
66
66
  b = MultiJson.load(c.response)
67
- b['response'].should == 'test'
67
+ expect(b['response']).to eq('test')
68
68
  end
69
69
  end
70
70
  end
@@ -12,8 +12,8 @@ describe 'Empty body API' do
12
12
  it 'serves a 201 with no body' do
13
13
  with_api(Empty) do
14
14
  get_request({}, err) do |c|
15
- c.response_header.status.should == 201
16
- c.response_header['CONTENT_LENGTH'].should == '0'
15
+ expect(c.response_header.status).to eq(201)
16
+ expect(c.response_header['CONTENT_LENGTH']).to eq('0')
17
17
  end
18
18
  end
19
19
  end