goliath 0.9.1 → 0.9.2

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 (94) hide show
  1. data/.gitignore +1 -0
  2. data/HISTORY +50 -0
  3. data/README.md +2 -0
  4. data/examples/activerecord/srv.rb +1 -3
  5. data/examples/async_aroundware_demo.rb +81 -0
  6. data/examples/async_upload.rb +1 -2
  7. data/examples/auth_and_rate_limit.rb +143 -0
  8. data/examples/chunked_streaming.rb +37 -0
  9. data/examples/conf_test.rb +1 -3
  10. data/examples/config/auth_and_rate_limit.rb +30 -0
  11. data/examples/config/template.rb +8 -0
  12. data/examples/content_stream.rb +1 -3
  13. data/examples/echo.rb +8 -6
  14. data/examples/env_use_statements.rb +17 -0
  15. data/examples/gziped.rb +1 -3
  16. data/examples/public/stylesheets/style.css +296 -0
  17. data/examples/rack_routes.rb +65 -3
  18. data/examples/rasterize/rasterize.js +15 -0
  19. data/examples/rasterize/rasterize.rb +36 -0
  20. data/examples/rasterize/rasterize_and_shorten.rb +37 -0
  21. data/examples/stream.rb +2 -2
  22. data/examples/template.rb +48 -0
  23. data/examples/test_rig.rb +125 -0
  24. data/examples/valid.rb +4 -2
  25. data/examples/views/debug.haml +4 -0
  26. data/examples/views/joke.markdown +13 -0
  27. data/examples/views/layout.erb +12 -0
  28. data/examples/views/layout.haml +39 -0
  29. data/examples/views/root.haml +28 -0
  30. data/goliath.gemspec +10 -3
  31. data/lib/goliath.rb +0 -36
  32. data/lib/goliath/api.rb +137 -26
  33. data/lib/goliath/application.rb +71 -21
  34. data/lib/goliath/connection.rb +4 -2
  35. data/lib/goliath/constants.rb +1 -0
  36. data/lib/goliath/env.rb +40 -1
  37. data/lib/goliath/goliath.rb +30 -15
  38. data/lib/goliath/headers.rb +2 -2
  39. data/lib/goliath/plugins/latency.rb +8 -2
  40. data/lib/goliath/rack.rb +18 -0
  41. data/lib/goliath/rack/async_aroundware.rb +56 -0
  42. data/lib/goliath/rack/async_middleware.rb +93 -0
  43. data/lib/goliath/rack/builder.rb +42 -0
  44. data/lib/goliath/rack/default_response_format.rb +3 -15
  45. data/lib/goliath/rack/formatters.rb +11 -0
  46. data/lib/goliath/rack/formatters/html.rb +2 -18
  47. data/lib/goliath/rack/formatters/json.rb +2 -17
  48. data/lib/goliath/rack/formatters/plist.rb +32 -0
  49. data/lib/goliath/rack/formatters/xml.rb +23 -31
  50. data/lib/goliath/rack/formatters/yaml.rb +27 -0
  51. data/lib/goliath/rack/jsonp.rb +1 -13
  52. data/lib/goliath/rack/params.rb +55 -27
  53. data/lib/goliath/rack/render.rb +13 -22
  54. data/lib/goliath/rack/templates.rb +357 -0
  55. data/lib/goliath/rack/tracer.rb +11 -12
  56. data/lib/goliath/rack/validation.rb +12 -0
  57. data/lib/goliath/rack/validation/default_params.rb +0 -2
  58. data/lib/goliath/rack/validation/numeric_range.rb +11 -2
  59. data/lib/goliath/rack/validation/request_method.rb +3 -2
  60. data/lib/goliath/rack/validation/required_param.rb +13 -11
  61. data/lib/goliath/rack/validation/required_value.rb +11 -15
  62. data/lib/goliath/rack/validator.rb +51 -0
  63. data/lib/goliath/request.rb +34 -20
  64. data/lib/goliath/response.rb +3 -2
  65. data/lib/goliath/runner.rb +5 -11
  66. data/lib/goliath/server.rb +2 -1
  67. data/lib/goliath/synchrony/mongo_receiver.rb +64 -0
  68. data/lib/goliath/synchrony/response_receiver.rb +64 -0
  69. data/lib/goliath/test_helper.rb +39 -21
  70. data/lib/goliath/validation.rb +2 -0
  71. data/lib/goliath/{rack/validation_error.rb → validation/error.rb} +0 -16
  72. data/lib/goliath/validation/standard_http_errors.rb +31 -0
  73. data/lib/goliath/version.rb +1 -1
  74. data/spec/integration/http_log_spec.rb +16 -16
  75. data/spec/integration/rack_routes_spec.rb +144 -0
  76. data/spec/integration/reloader_spec.rb +4 -4
  77. data/spec/integration/template_spec.rb +54 -0
  78. data/spec/integration/trace_spec.rb +23 -0
  79. data/spec/integration/valid_spec.rb +21 -0
  80. data/spec/spec_helper.rb +3 -1
  81. data/spec/unit/api_spec.rb +30 -0
  82. data/spec/unit/rack/builder_spec.rb +40 -0
  83. data/spec/unit/rack/formatters/plist_spec.rb +51 -0
  84. data/spec/unit/rack/formatters/yaml_spec.rb +53 -0
  85. data/spec/unit/rack/params_spec.rb +22 -0
  86. data/spec/unit/rack/render_spec.rb +10 -5
  87. data/spec/unit/rack/validation/numeric_range_spec.rb +8 -1
  88. data/spec/unit/rack/validation/request_method_spec.rb +8 -8
  89. data/spec/unit/rack/validation/required_param_spec.rb +19 -15
  90. data/spec/unit/rack/validation/required_value_spec.rb +10 -13
  91. data/spec/unit/server_spec.rb +4 -4
  92. data/spec/unit/validation/standard_http_errors_spec.rb +21 -0
  93. metadata +177 -35
  94. data/spec/unit/rack/validation_error_spec.rb +0 -40
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ _site
16
16
  examples/log
17
17
  examples/goliath.log*
18
18
  examples/goliath.pid
19
+ examples/rasterize/thumb
data/HISTORY ADDED
@@ -0,0 +1,50 @@
1
+ HISTORY
2
+ =======
3
+
4
+ v0.9.1 (Apr 12, 2011)
5
+ ---------------------
6
+ - Added extra messaging around the class not matching the file name (Carlos Brando)
7
+
8
+ - Fix issue with POST parameters not being parsed by Goliath::Rack::Params
9
+ - Added support for multipart encoded POST bodies
10
+ - Added support for parsing nested query string parameters (Nolan Evans)
11
+ - Added support for parsing application/json POST bodies
12
+ - Content-Types outside of multipart, urlencoded and application/json will not be parsed automatically.
13
+
14
+ - added 'run as user' option
15
+ - SERVER_NAME and SERVER_PORT are set to values in HOST header
16
+
17
+ - Cleaned up spec examples (Justin Ko)
18
+
19
+ - moved logger into 'rack.logger' key to be more Rack compliant (Env#logger added to
20
+ keep original API consistent)
21
+ - add command line option for specifying config file
22
+ - HTTP_CONTENT_LENGTH and HTTP_CONTENT_TYPE were changed to CONTENT_TYPE and CONTENT_LENGTH
23
+ to be more Rack compliant
24
+ - fix issue with loading config file in development mode
25
+
26
+ - Rack::Reloader will be loaded automatically by the framework in development mode.
27
+
28
+
29
+ v0.9.0 (Mar 9, 2011)
30
+ --------------------
31
+ (Initial Public Release)
32
+
33
+ Goliath is an open source version of the non-blocking (asynchronous) Ruby web server framework
34
+ powering PostRank. It is a lightweight framework designed to meet the following goals: bare
35
+ metal performance, Rack API and middleware support, simple configuration, fully asynchronous
36
+ processing, and readable and maintainable code (read: no callbacks).
37
+
38
+ The framework is powered by an EventMachine reactor, a high-performance HTTP parser and Ruby 1.9
39
+ runtime. One major advantage Goliath has over other asynchronous frameworks is the fact that by
40
+ leveraging Ruby fibers, it can untangle the complicated callback-based code into a format we are
41
+ all familiar and comfortable with: linear execution, which leads to more maintainable and readable code.
42
+
43
+ While MRI is the recommend platform, Goliath has been tested to run on JRuby and Rubinius.
44
+
45
+ Goliath has been in production at PostRank for over a year, serving a sustained 500 requests/s for
46
+ internal and external applications. Many of the Goliath processes have been running for months at
47
+ a time (read: no memory leaks) and have served hundreds of gigabytes of data without restarts. To
48
+ scale up and provide failover and redundancy, our individual Goliath servers at PostRank are usually
49
+ deployed behind a reverse proxy (such as HAProxy).
50
+
data/README.md CHANGED
@@ -73,6 +73,8 @@ Goliath has been in production at PostRank for over a year, serving a sustained
73
73
  * [Stage left: Enter Goliath - HTTP Proxy + MongoDB](http://everburning.com/news/stage-left-enter-goliath/)
74
74
  * [InfoQ: Meet the Goliath of Ruby Application Servers](http://www.infoq.com/articles/meet-goliath)
75
75
  * [Node.jsはコールバック・スパゲティを招くか](http://el.jibun.atmarkit.co.jp/rails/2011/03/nodejs-d123.html)
76
+ * [Goliath on LinuxFr.org (french)](http://linuxfr.org/news/en-vrac-spécial-ruby-jruby-sinatra-et-goliath)
77
+ * [Goliath et ses amis (slides in french)](http://nono.github.com/Presentations/20110416_Goliath/)
76
78
 
77
79
  ## Discussion and Support
78
80
 
@@ -21,9 +21,7 @@ end
21
21
  class Srv < Goliath::API
22
22
  use Goliath::Rack::Params
23
23
  use Goliath::Rack::DefaultMimeType
24
- use Goliath::Rack::Formatters::JSON
25
- use Goliath::Rack::Render
26
- use Goliath::Rack::ValidationError
24
+ use Goliath::Rack::Render, 'json'
27
25
 
28
26
  use Goliath::Rack::Validation::RequiredParam, {:key => 'id', :type => 'ID'}
29
27
  use Goliath::Rack::Validation::NumericRange, {:key => 'id', :min => 1}
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.dirname(__FILE__)+'/../lib'
3
+
4
+ require 'goliath'
5
+ require 'em-synchrony/em-http'
6
+ require 'yajl/json_gem'
7
+
8
+ #
9
+ # Here's a way to make an asynchronous request in the middleware, and only
10
+ # proceed with the response when both the endpoint and our middleware's
11
+ # responses have completed.
12
+ #
13
+ # To run this, start the 'test_rig.rb' server on port 9002:
14
+ #
15
+ # ./examples/test_rig.rb -sv -p 9002
16
+ #
17
+ # And then start this server on port 9000:
18
+ #
19
+ # ./async_aroundware_demo.rb -sv -p 9000
20
+ #
21
+ # Now curl the async_aroundware_demo_multi:
22
+ #
23
+ # $ time curl 'http://127.0.0.1:9000/?delay_1=1.0&delay_2=1.5'
24
+ # { "results": {
25
+ # "sleep_2": { "delay": 1.5, "actual": 1.5085558891296387 },
26
+ # "sleep_1": { "delay": 1.0, "actual": 1.0098700523376465 }
27
+ # } }
28
+ #
29
+ # The requests are run concurrently:
30
+ #
31
+ # $ ./examples/async_aroundware_demo.rb -sv -p 9000 -e prod &
32
+ # [68463:INFO] 2011-05-03 23:13:03 :: Starting server on 0.0.0.0:9000 in production mode. Watch out for stones.
33
+ # $ ab -n10 -c10 'http://127.0.0.1:9000/?delay_1=1.5&delay_2=2.0'
34
+ # Connection Times (ms)
35
+ # min mean[+/-sd] median max
36
+ # Connect: 0 0 0.1 0 0
37
+ # Processing: 2027 2111 61.6 2112 2204
38
+ # Waiting: 2027 2111 61.5 2112 2204
39
+ # Total: 2027 2112 61.5 2113 2204
40
+ #
41
+ #
42
+
43
+ BASE_URL = 'http://localhost:9002/'
44
+
45
+ class MyResponseReceiver < Goliath::Synchrony::MultiReceiver
46
+ def pre_process
47
+ # Request with delay_1 and drop_1 -- note: 'aget', because we want execution to continue
48
+ req = EM::HttpRequest.new(BASE_URL).aget(:query => { :delay => env.params['delay_1'], :drop => env.params['drop_1'] })
49
+ add :sleep_1, req
50
+ end
51
+
52
+ def post_process
53
+ # unify the results with the results of the API call
54
+ responses[:callback].each{|name, resp| body[:results][name] = JSON.parse(resp.response) }
55
+ responses[:errback ].each{|name, err| body[:errors][name] = err.error }
56
+ [status, headers, JSON.generate(body)]
57
+ end
58
+ end
59
+
60
+ class AsyncAroundwareDemo < Goliath::API
61
+ use Goliath::Rack::Params
62
+ use Goliath::Rack::Validation::NumericRange, {:key => 'delay_1', :default => 1.0, :max => 5.0, :min => 0.0, :as => Float}
63
+ use Goliath::Rack::Validation::NumericRange, {:key => 'delay_2', :default => 0.5, :max => 5.0, :min => 0.0, :as => Float}
64
+ #
65
+ use Goliath::Rack::AsyncAroundware, MyResponseReceiver
66
+
67
+ def response(env)
68
+ # Request with delay_2 and drop_2 -- note: 'get', because we want execution to proceed linearly
69
+ resp = EM::HttpRequest.new(BASE_URL).get(:query => { :delay => env.params['delay_2'], :drop => env.params['drop_2'] })
70
+
71
+ body = { :results => {}, :errors => {} }
72
+
73
+ if resp.response_header.status.to_i != 0
74
+ body[:results][:sleep_2] = JSON.parse(resp.response)
75
+ else
76
+ body[:errors ][:sleep_2] = resp.error
77
+ end
78
+
79
+ [200, { }, body]
80
+ end
81
+ end
@@ -7,8 +7,7 @@ require 'yajl'
7
7
  class AsyncUpload < Goliath::API
8
8
  use Goliath::Rack::Params # parse & merge query and body parameters
9
9
  use Goliath::Rack::DefaultMimeType # cleanup accepted media types
10
- use Goliath::Rack::Formatters::JSON # JSON output formatter
11
- use Goliath::Rack::Render # auto-negotiate response format
10
+ use Goliath::Rack::Render, 'json' # auto-negotiate response format
12
11
 
13
12
  def on_headers(env, headers)
14
13
  env.logger.info 'received headers: ' + headers.inspect
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ $: << File.join(File.dirname(__FILE__), '../lib')
3
+ require 'goliath'
4
+ require 'em-mongo'
5
+ require 'em-http'
6
+ require 'em-synchrony/em-http'
7
+ require 'yajl/json_gem'
8
+
9
+ require 'goliath/synchrony/mongo_receiver' # has the aroundware logic for talking to mongodb
10
+ require File.join(File.dirname(__FILE__), 'http_log') # Use the HttpLog as our actual endpoint, but include this in the middleware
11
+
12
+ # Usage:
13
+ #
14
+ # First launch a dummy responder, like hello_world.rb or test_rig.rb:
15
+ # ruby ./examples/hello_world.rb -sv -p 8080 -e prod &
16
+ #
17
+ # Then launch this script
18
+ # ruby ./examples/auth_and_rate_limit.rb -sv -p 9000 --config $PWD/examples/config/auth_and_rate_limit.rb
19
+ #
20
+
21
+ # Tracks and enforces account and rate limit policies.
22
+ #
23
+ # Before the request:
24
+ #
25
+ # * validates the apikey exists
26
+ # * launches requests for the account and current usage (hourly rate limit, etc)
27
+ #
28
+ # It then passes the request down the middleware chain; execution resumes only
29
+ # when both the remote request and the auth info have returned.
30
+ #
31
+ # After remote request and auth info return:
32
+ #
33
+ # * Check the account exists and is valid
34
+ # * Check the rate limit is OK
35
+ #
36
+ # If it passes all those checks, the request goes through; otherwise we raise an
37
+ # error that Goliath::Rack::Validator turns into a 4xx response
38
+ #
39
+ # WARNING: Since this passes ALL requests through to the responder, it's only
40
+ # suitable for idempotent requests (GET, typically). You may need to handle
41
+ # POST/PUT/DELETE requests differently.
42
+ #
43
+ #
44
+ class AuthReceiver < Goliath::Synchrony::MongoReceiver
45
+ include Goliath::Validation
46
+ include Goliath::Rack::Validator
47
+ attr_accessor :account_info, :usage_info
48
+
49
+ # time period to aggregate stats over, in seconds
50
+ TIMEBIN_SIZE = 60 * 60
51
+
52
+ class MissingApikeyError < BadRequestError ; end
53
+ class RateLimitExceededError < ForbiddenError ; end
54
+ class InvalidApikeyError < UnauthorizedError ; end
55
+
56
+ def pre_process
57
+ validate_apikey!
58
+ first('AccountInfo', { :_id => apikey }){|res| self.account_info = res }
59
+ first('UsageInfo', { :_id => usage_id }){|res| self.usage_info = res }
60
+ env.trace('pre_process_end')
61
+ end
62
+
63
+ def post_process
64
+ env.trace('post_process_beg')
65
+ env.logger.info [account_info, usage_info].inspect
66
+ self.account_info ||= {}
67
+ self.usage_info ||= {}
68
+
69
+ inject_headers
70
+
71
+ EM.next_tick do
72
+ safely(env){ charge_usage }
73
+ end
74
+
75
+ safely(env, headers) do
76
+ check_apikey!
77
+ check_rate_limit!
78
+
79
+ env.trace('post_process_end')
80
+ [status, headers, body]
81
+ end
82
+ end
83
+
84
+ # ===========================================================================
85
+
86
+ def validate_apikey!
87
+ if apikey.to_s.empty?
88
+ raise MissingApikeyError
89
+ end
90
+ end
91
+
92
+ def check_apikey!
93
+ unless account_info['valid'] == true
94
+ raise InvalidApikeyError
95
+ end
96
+ end
97
+
98
+ def check_rate_limit!
99
+ return true if usage_info['calls'].to_f <= account_info['max_call_rate'].to_f
100
+ raise RateLimitExceededError
101
+ end
102
+
103
+ def charge_usage
104
+ update('UsageInfo', { :_id => usage_id },
105
+ { '$inc' => { :calls => 1 } }, :upsert => true)
106
+ end
107
+
108
+ def inject_headers
109
+ headers.merge!({
110
+ 'X-RateLimit-MaxRequests' => account_info['max_call_rate'].to_s,
111
+ 'X-RateLimit-Requests' => usage_info['calls'].to_s,
112
+ 'X-RateLimit-Reset' => timebin_end.to_s,
113
+ })
114
+ end
115
+
116
+ # ===========================================================================
117
+
118
+ def apikey
119
+ env.params['_apikey']
120
+ end
121
+
122
+ def usage_id
123
+ "#{apikey}-#{timebin}"
124
+ end
125
+
126
+ def timebin
127
+ @timebin ||= timebin_beg
128
+ end
129
+
130
+ def timebin_beg
131
+ ((Time.now.to_i / TIMEBIN_SIZE).floor * TIMEBIN_SIZE)
132
+ end
133
+
134
+ def timebin_end
135
+ timebin_beg + TIMEBIN_SIZE
136
+ end
137
+ end
138
+
139
+ class AuthAndRateLimit < HttpLog
140
+ use Goliath::Rack::Tracer, 'X-Tracer'
141
+ use Goliath::Rack::Params # parse & merge query and body parameters
142
+ use Goliath::Rack::AsyncAroundware, AuthReceiver, 'api_auth_db'
143
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ #
5
+ # A simple HTTP streaming API which returns a 200 response for any GET request
6
+ # and then emits numbers 1 through 10 in 1 second intervals using Chunked
7
+ # transfer encoding, and finally closes the connection.
8
+ #
9
+ # Chunked transfer streaming works transparently with both browsers and
10
+ # streaming consumers.
11
+ #
12
+
13
+ require 'goliath'
14
+
15
+ class ChunkedStreaming < Goliath::API
16
+ def on_close(env)
17
+ env.logger.info "Connection closed."
18
+ end
19
+
20
+ def response(env)
21
+ i = 0
22
+ pt = EM.add_periodic_timer(1) do
23
+ env.chunked_stream_send("#{i}\n")
24
+ i += 1
25
+ end
26
+
27
+ EM.add_timer(10) do
28
+ pt.cancel
29
+
30
+ env.chunked_stream_send("!! BOOM !!\n")
31
+ env.chunked_stream_close
32
+ end
33
+
34
+ headers = { 'Content-Type' => 'text/plain', 'X-Stream' => 'Goliath' }
35
+ chunked_streaming_response(200, headers)
36
+ end
37
+ end
@@ -11,9 +11,7 @@ require 'goliath'
11
11
 
12
12
  class ConfTest < Goliath::API
13
13
  use Goliath::Rack::Params
14
- use Goliath::Rack::DefaultMimeType
15
- use Goliath::Rack::Formatters::JSON
16
- use Goliath::Rack::Render
14
+ use Goliath::Rack::Render, 'json'
17
15
 
18
16
  def options_parser(opts, options)
19
17
  options[:test] = 0
@@ -0,0 +1,30 @@
1
+ import 'http_log'
2
+
3
+ environment(:development) do
4
+
5
+ config['api_auth_db'] = EventMachine::Synchrony::ConnectionPool.new(:size => 20) do
6
+ conn = EM::Mongo::Connection.new('localhost', 27017, 1, {:reconnect_in => 1})
7
+ conn.db('buzzkill_test')
8
+ end
9
+
10
+ # for demo purposes, some dummy accounts
11
+ timebin = ((Time.now.to_i / 3600).floor * 3600)
12
+
13
+ # This user's calls should all go through
14
+ config['api_auth_db'].collection('AccountInfo').save({
15
+ :_id => 'i_am_awesome', 'valid' => true, 'max_call_rate' => 1_000_000 })
16
+
17
+ # this user's account is disabled
18
+ config['api_auth_db'].collection('AccountInfo').save({
19
+ :_id => 'i_am_lame', 'valid' => false, 'max_call_rate' => 1_000 })
20
+
21
+ # this user has not been seen, but will very quickly hit their limit
22
+ config['api_auth_db'].collection('AccountInfo').save({
23
+ :_id => 'i_am_limited', 'valid' => true, 'max_call_rate' => 10 })
24
+
25
+ # fakes a user with a bunch of calls already made this hour -- two more = no yuo
26
+ config['api_auth_db'].collection('AccountInfo').save({
27
+ :_id => 'i_am_busy', 'valid' => true, 'max_call_rate' => 1_000 })
28
+ config['api_auth_db'].collection('UsageInfo').save({
29
+ :_id => "i_am_busy-#{timebin}", 'calls' => 999 })
30
+ end
@@ -0,0 +1,8 @@
1
+ config[:template] = {
2
+ :layout_engine => :haml,
3
+ }
4
+ config[:template_engines] = {
5
+ :haml => {
6
+ :escape_html => true
7
+ }
8
+ }
@@ -12,12 +12,10 @@ require 'goliath'
12
12
  # rabbitmqctl set_permissions -p /test test ".*" ".*" ".*"
13
13
 
14
14
  class ContentStream < Goliath::API
15
- use Goliath::Rack::Formatters::JSON
16
15
  use Goliath::Rack::Params
17
16
 
18
- use Goliath::Rack::Render
17
+ use Goliath::Rack::Render, 'json'
19
18
  use Goliath::Rack::Heartbeat
20
- use Goliath::Rack::ValidationError
21
19
  use Goliath::Rack::Validation::RequestMethod, %w(GET)
22
20
 
23
21
  def on_close(env)
@@ -4,18 +4,20 @@ $:<< '../lib' << 'lib'
4
4
  require 'goliath'
5
5
  require 'goliath/plugins/latency'
6
6
 
7
- # Goliath uses multi-jon, so pick your favorite JSON serializer
7
+ # Goliath uses multi-json, so pick your favorite JSON serializer
8
8
  # require 'json'
9
9
  require 'yajl'
10
10
 
11
11
  class Echo < Goliath::API
12
- use Goliath::Rack::Params # parse & merge query and body parameters
12
+ use Goliath::Rack::Tracer # log trace statistics
13
13
  use Goliath::Rack::DefaultMimeType # cleanup accepted media types
14
- use Goliath::Rack::Formatters::JSON # JSON output formatter
15
- use Goliath::Rack::Render # auto-negotiate response format
14
+ use Goliath::Rack::Render, 'json' # auto-negotiate response format
15
+ use Goliath::Rack::Params # parse & merge query and body parameters
16
16
  use Goliath::Rack::Heartbeat # respond to /status with 200, OK (monitoring, etc)
17
- use Goliath::Rack::ValidationError # catch and render validation errors
18
17
 
18
+ # If you are using Golaith version <=0.9.1 you need to Goliath::Rack::ValidationError
19
+ # to prevent the request from remaining open after an error occurs
20
+ #use Goliath::Rack::ValidationError
19
21
  use Goliath::Rack::Validation::RequestMethod, %w(GET POST) # allow GET and POST requests only
20
22
  use Goliath::Rack::Validation::RequiredParam, {:key => 'echo'} # must provide ?echo= query or body param
21
23
 
@@ -30,4 +32,4 @@ class Echo < Goliath::API
30
32
  def response(env)
31
33
  [200, {}, process_request]
32
34
  end
33
- end
35
+ end