goliath 0.9.4 → 1.0.0.beta.1

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 (102) hide show
  1. data/.gitignore +3 -0
  2. data/Guardfile +8 -0
  3. data/HISTORY.md +10 -0
  4. data/LICENSE +1 -1
  5. data/README.md +28 -29
  6. data/Rakefile +10 -2
  7. data/examples/activerecord/config/srv.rb +2 -1
  8. data/examples/activerecord/srv.rb +14 -5
  9. data/examples/api_proxy.rb +3 -7
  10. data/examples/around.rb +38 -0
  11. data/examples/async_aroundware_demo.rb +2 -2
  12. data/examples/async_upload.rb +1 -1
  13. data/examples/auth_and_rate_limit.rb +1 -1
  14. data/examples/clone.rb +26 -0
  15. data/examples/config/websocket.rb +1 -0
  16. data/examples/custom_logs.rb +21 -0
  17. data/examples/custom_server.rb +6 -44
  18. data/examples/early_abort.rb +6 -3
  19. data/examples/echo.rb +1 -1
  20. data/examples/fiber_pool.rb +35 -0
  21. data/examples/grape/config/apiserver.rb +8 -0
  22. data/examples/grape/server.rb +67 -0
  23. data/examples/gziped.rb +1 -1
  24. data/examples/params.rb +36 -0
  25. data/examples/rasterize/rasterize.rb +1 -2
  26. data/examples/router.rb +15 -0
  27. data/examples/template.rb +14 -7
  28. data/examples/test.rb +31 -0
  29. data/examples/upload.rb +17 -0
  30. data/examples/views/joke.markdown +4 -4
  31. data/examples/websocket.rb +39 -0
  32. data/examples/ws/favicon.ico +0 -0
  33. data/examples/ws/index.erb +50 -0
  34. data/goliath.gemspec +24 -6
  35. data/lib/goliath/api.rb +15 -82
  36. data/lib/goliath/application.rb +3 -17
  37. data/lib/goliath/connection.rb +16 -9
  38. data/lib/goliath/constants.rb +2 -0
  39. data/lib/goliath/env.rb +2 -3
  40. data/lib/goliath/goliath.rb +24 -34
  41. data/lib/goliath/plugins/latency.rb +7 -3
  42. data/lib/goliath/rack/builder.rb +1 -37
  43. data/lib/goliath/rack/favicon.rb +31 -0
  44. data/lib/goliath/rack/formatters/json.rb +1 -1
  45. data/lib/goliath/rack/heartbeat.rb +1 -0
  46. data/lib/goliath/rack/jsonp.rb +1 -0
  47. data/lib/goliath/rack/params.rb +2 -17
  48. data/lib/goliath/rack/render.rb +0 -1
  49. data/lib/goliath/rack/templates.rb +18 -7
  50. data/lib/goliath/rack/types/base.rb +24 -0
  51. data/lib/goliath/rack/types/boolean.rb +18 -0
  52. data/lib/goliath/rack/types/core.rb +19 -0
  53. data/lib/goliath/rack/types/symbol.rb +16 -0
  54. data/lib/goliath/rack/types.rb +10 -0
  55. data/lib/goliath/rack/validation/coerce.rb +48 -0
  56. data/lib/goliath/rack/validation/param.rb +113 -0
  57. data/lib/goliath/rack/validation/request_method.rb +1 -1
  58. data/lib/goliath/rack/validation/required.rb +47 -0
  59. data/lib/goliath/rack/validation/required_param.rb +42 -17
  60. data/lib/goliath/rack/validation.rb +3 -0
  61. data/lib/goliath/rack.rb +2 -2
  62. data/lib/goliath/request.rb +41 -9
  63. data/lib/goliath/response.rb +0 -2
  64. data/lib/goliath/runner.rb +55 -4
  65. data/lib/goliath/server.rb +7 -3
  66. data/lib/goliath/test_helper.rb +70 -16
  67. data/lib/goliath/test_helper_ws.rb +42 -0
  68. data/lib/goliath/version.rb +1 -1
  69. data/lib/goliath/websocket.rb +80 -0
  70. data/pkg/goliath-0.9.4.gem +0 -0
  71. data/pkg/goliath-1.0.0.beta.1.gem +0 -0
  72. data/spec/integration/async_request_processing.rb +1 -1
  73. data/spec/integration/early_abort_spec.rb +3 -10
  74. data/spec/integration/echo_spec.rb +8 -8
  75. data/spec/integration/jsonp_spec.rb +31 -0
  76. data/spec/integration/keepalive_spec.rb +2 -2
  77. data/spec/integration/template_spec.rb +10 -5
  78. data/spec/integration/test_helper_spec.rb +33 -0
  79. data/spec/integration/valid_spec.rb +35 -5
  80. data/spec/integration/websocket_spec.rb +44 -0
  81. data/spec/unit/api_spec.rb +2 -19
  82. data/spec/unit/connection_spec.rb +3 -0
  83. data/spec/unit/rack/formatters/json_spec.rb +3 -3
  84. data/spec/unit/rack/heartbeat_spec.rb +13 -0
  85. data/spec/unit/rack/params_spec.rb +2 -8
  86. data/spec/unit/rack/validation/param_spec.rb +443 -0
  87. data/spec/unit/rack/validation/request_method_spec.rb +5 -0
  88. data/spec/unit/rack/validation/required_param_spec.rb +71 -1
  89. data/spec/unit/runner_spec.rb +21 -7
  90. data/test/echo_test.rb +25 -0
  91. data/test/test_helper.rb +5 -0
  92. metadata +316 -78
  93. data/examples/env_use_statements.rb +0 -20
  94. data/examples/favicon.rb +0 -40
  95. data/examples/rack_routes.rb +0 -125
  96. data/examples/rasterize/thumb/f7ad4cb03e5bfd0e2c43db8e598fb3cd.png +0 -0
  97. data/examples/valid.rb +0 -17
  98. data/lib/goliath/deprecated/async_aroundware.rb +0 -133
  99. data/lib/goliath/deprecated/mongo_receiver.rb +0 -84
  100. data/lib/goliath/deprecated/response_receiver.rb +0 -97
  101. data/spec/integration/rack_routes_spec.rb +0 -169
  102. data/spec/unit/rack/builder_spec.rb +0 -40
data/.gitignore CHANGED
@@ -1,9 +1,12 @@
1
1
  Gemfile.lock
2
2
 
3
+ bin/
3
4
  .bundle/
4
5
  doc/
6
+ .idea/
5
7
  pkg/
6
8
  _site
9
+ vendor/
7
10
 
8
11
  .yardoc
9
12
  .livereload
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/goliath/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
7
+ end
8
+
data/HISTORY.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # HISTORY
2
2
 
3
+ ## v1.0.0 (May 2x, 2012)
4
+
5
+ - removed router DSL for 1.0 release
6
+ - removed restriction on environments
7
+ - refactored validation handler logic
8
+ - refactored params coercion logic
9
+ - users can customize log formats
10
+ - cleanup of spec helpers
11
+ - many small bugfixes
12
+
3
13
  ## v0.9.3 (Oct 16, 2011)
4
14
 
5
15
  - new router DSL - much improved, see examples
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 PostRank Inc.
1
+ Copyright (c) 2011-2012 PostRank Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
data/README.md CHANGED
@@ -1,22 +1,28 @@
1
1
  # Goliath
2
2
 
3
- Goliath is an open source version of the non-blocking (asynchronous) Ruby web server framework powering PostRank. It is a lightweight framework designed to meet the following goals: bare metal performance, Rack API and middleware support, simple configuration, fully asynchronous processing, and readable and maintainable code (read: no callbacks).
3
+ [<img src="https://secure.travis-ci.org/postrank-labs/goliath.png?travis"/>](http://travis-ci.org/postrank-labs/goliath) [<img src="https://gemnasium.com/postrank-labs/goliath.png?travis"/>](https://gemnasium.com/postrank-labs/goliath)
4
4
 
5
- The framework is powered by an EventMachine reactor, a high-performance HTTP parser and Ruby 1.9 runtime. The one major advantage Goliath has over other asynchronous frameworks is the fact that by leveraging Ruby fibers introduced in Ruby 1.9+, it can untangle the complicated callback-based code into a format we are all familiar and comfortable with: linear execution, which leads to more maintainable and readable code.
5
+ Goliath is an open source version of the non-blocking (asynchronous) Ruby web server framework. It is a lightweight framework designed to meet the following goals: bare metal performance, Rack API and middleware support, simple configuration, fully asynchronous processing, and readable and maintainable code (read: no callbacks).
6
6
 
7
- Each HTTP request within Goliath is executed in its own Ruby fiber and all asynchronous I/O operations can transparently suspend and later resume the processing without requiring the developer to write any additional code. Both request processing and response processing can be done in fully asynchronous fashion: streaming uploads, firehose API's, request/response, and so on.
7
+ The framework is powered by an EventMachine reactor, a high-performance HTTP parser and Ruby 1.9+ runtime. The one major advantage Goliath has over other asynchronous frameworks is the fact that by leveraging Ruby fibers introduced in Ruby 1.9+, it can untangle the complicated callback-based code into a format we are all familiar and comfortable with: linear execution, which leads to more maintainable and readable code.
8
+
9
+ Each HTTP request within Goliath is executed within its own Ruby fiber and all asynchronous I/O operations can transparently suspend and later resume the processing without requiring the developer to write any additional code. Both request processing and response processing can be done in fully asynchronous fashion: streaming uploads, firehose API's, request/response, websockets, and so on.
8
10
 
9
11
  ## Installation & Prerequisites
10
12
 
11
13
  * Install Ruby 1.9 (via RVM or natively)
12
14
 
13
- $> gem install rvm
14
- $> rvm install 1.9.2
15
- $> rvm use 1.9.2
15
+ ```bash
16
+ $> gem install rvm
17
+ $> rvm install 1.9.3
18
+ $> rvm use 1.9.3
19
+ ```
16
20
 
17
21
  * Install Goliath:
18
22
 
19
- $> gem install goliath
23
+ ```bash
24
+ $> gem install goliath
25
+ ```
20
26
 
21
27
  ## Getting Started: Hello World
22
28
 
@@ -33,20 +39,13 @@ end
33
39
  > [97570:INFO] 2011-02-15 00:33:51 :: Starting server on 0.0.0.0:9000 in development mode. Watch out for stones.
34
40
  ```
35
41
 
36
- See examples directory for more, hands-on examples of building Goliath powered web-services. 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:
37
-
38
- * [Meet EventMachine: Part 1](http://peepcode.com/products/eventmachine) - introduction to EM, Fibers, etc.
39
- * [Meet EventMachine: Part 2](http://peepcode.com/products/eventmachine-ii) - building an API with Goliath
42
+ See examples directory for more hands-on examples of building Goliath powered web-services.
40
43
 
41
44
  ## Performance: MRI, JRuby, Rubinius
42
45
 
43
- Goliath is not tied to a single Ruby runtime - it is able to run on MRI Ruby, JRuby and Rubinius today. Depending on which platform you are working with, you will see different performance characteristics. At the moment, we recommend MRI Ruby 1.9.2 as the best performing VM: a roundtrip through the full Goliath stack on MRI 1.9.2p136 takes ~0.33ms (~3000 req/s).
44
-
45
- JRuby performance (with 1.9 mode enabled) is currently much worse than MRI Ruby 1.9.2, due to the fact that all JRuby fibers are mapped to native Java threads. However, there is [very promising](http://classparser.blogspot.com/2010/04/jruby-coroutines-really-fast.html), existing work that promises to make JRuby fibers even faster than those of MRI Ruby. Once this functionality is built into JRuby ([JRUBY-5461](http://jira.codehaus.org/browse/JRUBY-5461)), JRuby may well take the performance crown. At the moment, without the MLVM support, a request through the full Goliath stack takes ~6ms (166 req/s).
46
+ Goliath is not tied to a single Ruby runtime - it is able to run on MRI Ruby, JRuby and Rubinius today. Depending on which platform you are working with, you will see different performance characteristics. At the moment, we recommend MRI Ruby 1.9.3+ as the best performing VM: a roundtrip through the full Goliath stack on MRI 1.9.3 takes ~0.33ms (~3000 req/s).
46
47
 
47
- Rubinius + Goliath performance is tough to pin down - there is a lot of room for optimization within the Rubinius VM. Currently, requests can take as little as 0.2ms and later spike to 50ms+. Stay tuned!
48
-
49
- Goliath has been in production at PostRank for over a year, serving a sustained 500 requests/s for internal and external applications. Many of the Goliath processes have been running for months at a time (read: no memory leaks) and have served hundreds of gigabytes of data without restarts. To scale up and provide failover and redundancy, our individual Goliath servers at PostRank are usually deployed behind a reverse proxy (such as HAProxy).
48
+ Goliath has been used in production environments for 2+ years, across many different companies: PostRank (now Google), [OMGPOP](OMGPOP) (now Zynga), [GameSpy](http://www.poweredbygamespy.com/2011/09/09/growing-pains-they-hurt-so-good/), and many others.
50
49
 
51
50
  ## FAQ
52
51
 
@@ -68,20 +67,21 @@ Goliath has been in production at PostRank for over a year, serving a sustained
68
67
 
69
68
  ### Hands-on applications:
70
69
 
71
- * [Peepcode](http://peepcode.com/products/eventmachine) [screencasts](http://peepcode.com/products/eventmachine-ii)
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:
71
+
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
+
75
+ Additionally, you can also watch this presentation from GoGaRuCo 2011, which describes the design and motivation behind Goliath:
76
+
77
+ * [0-60 with Goliath: Building high performance web services](http://confreaks.com/videos/653-gogaruco2011-0-60-with-goliath-building-high-performance-ruby-web-services)
78
+
79
+ Other resources:
80
+
72
81
  * [Asynchronous HTTP, MySQL, etc](https://github.com/postrank-labs/goliath/wiki/Asynchronous-Processing)
73
82
  * [Response streaming with Goliath](https://github.com/postrank-labs/goliath/wiki/Streaming)
74
83
  * [Examples](https://github.com/postrank-labs/goliath/tree/master/examples)
75
84
 
76
- ## Coverage
77
-
78
- * [Goliath: Non-blocking, Ruby 1.9 Web Server](http://www.igvita.com/2011/03/08/goliath-non-blocking-ruby-19-web-server)
79
- * [Stage left: Enter Goliath - HTTP Proxy + MongoDB](http://everburning.com/news/stage-left-enter-goliath/)
80
- * [InfoQ: Meet the Goliath of Ruby Application Servers](http://www.infoq.com/articles/meet-goliath)
81
- * [Node.jsはコールバック・スパゲティを招くか](http://el.jibun.atmarkit.co.jp/rails/2011/03/nodejs-d123.html)
82
- * [Goliath on LinuxFr.org (french)](http://linuxfr.org/news/en-vrac-spécial-ruby-jruby-sinatra-et-goliath)
83
- * [Goliath et ses amis (slides in french)](http://nono.github.com/Presentations/20110416_Goliath/)
84
-
85
85
  ## Discussion and Support
86
86
 
87
87
  * [Source](https://github.com/postrank-labs/goliath)
@@ -90,5 +90,4 @@ Goliath has been in production at PostRank for over a year, serving a sustained
90
90
 
91
91
  ## License & Acknowledgments
92
92
 
93
- Goliath is distributed under the MIT license, for full details please see the LICENSE file.
94
- Rock favicon CC-BY from [Douglas Feer](http://www.favicon.cc/?action=icon&file_id=375421)
93
+ Goliath is distributed under the MIT license, for full details please see the LICENSE file.
data/Rakefile CHANGED
@@ -3,9 +3,17 @@ Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'yard'
5
5
  require 'rspec/core/rake_task'
6
+ require 'rake/testtask'
6
7
 
7
- task :default => [:spec]
8
- task :test => [:spec]
8
+ task :default => [:test]
9
+ task :test => [:spec, :unit]
10
+
11
+ desc "run the unit test"
12
+ Rake::TestTask.new(:unit) do |t|
13
+ t.libs << "test"
14
+ t.test_files = FileList['test/**/*_test.rb']
15
+ t.verbose = true
16
+ end
9
17
 
10
18
  desc "run spec tests"
11
19
  RSpec::Core::RakeTask.new('spec') do |t|
@@ -4,4 +4,5 @@ ActiveRecord::Base.establish_connection(:adapter => 'em_mysql2',
4
4
  :database => 'goliath_test',
5
5
  :username => 'goliath',
6
6
  :password => 'goliath',
7
- :host => 'localhost')
7
+ :host => 'localhost',
8
+ :pool => 5)
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # gem install activerecord
3
+ # gem install em-synchrony
4
4
  # gem install mysql2
5
5
 
6
6
  # create database goliath_test
@@ -9,11 +9,21 @@
9
9
  # create table users (id int not null auto_increment primary key, name varchar(255), email varchar(255));
10
10
  # insert into users (name, email) values ('dan', 'dj2@everyburning.com'), ('Ilya', 'ilya@igvita.com');
11
11
 
12
+ # To start server
13
+ # ruby ./srv.rb
14
+ #
15
+ # Example output:
16
+ # curl http://localhost:9000/?id=1
17
+ #=> "{\"user\":{\"email\":\"dj2@everyburning.com\",\"id\":1,\"name\":\"dan\"}}"
18
+ # curl http://localhost:9000/?id=2
19
+ #=> "{\"user\":{\"email\":\"ilya@igvita.com\",\"id\":2,\"name\":\"Ilya\"}}"
20
+
12
21
  $: << "../../lib" << "./lib"
13
22
 
14
23
  require 'goliath'
15
- require 'active_record'
16
- require 'yajl'
24
+ require 'em-synchrony/activerecord'
25
+
26
+ require 'yajl' if RUBY_PLATFORM != 'java'
17
27
 
18
28
  class User < ActiveRecord::Base
19
29
  end
@@ -27,7 +37,6 @@ class Srv < Goliath::API
27
37
  use Goliath::Rack::Validation::NumericRange, {:key => 'id', :min => 1}
28
38
 
29
39
  def response(env)
30
- User.find_by_sql("SELECT SLEEP(10)")
31
- [200, {}, User.find(params['id'])]
40
+ [200, {}, User.find(params['id']).to_json]
32
41
  end
33
42
  end
@@ -4,8 +4,8 @@
4
4
 
5
5
  require 'goliath'
6
6
  require 'em-synchrony/em-http'
7
-
8
- class TwilioResponse < Goliath::API
7
+
8
+ class Twilio < Goliath::API
9
9
  use Goliath::Rack::Params
10
10
  use Goliath::Rack::JSONP
11
11
 
@@ -18,11 +18,7 @@ class TwilioResponse < Goliath::API
18
18
 
19
19
  http = EM::HttpRequest.new(url).get head: HEADERS
20
20
  logger.debug "Received #{http.response_header.status} from Twilio"
21
-
21
+
22
22
  [200, {'X-Goliath' => 'Proxy','Content-Type' => 'application/javascript'}, http.response]
23
23
  end
24
24
  end
25
-
26
- class Twilio < Goliath::API
27
- get %r{^/(Local|TollFree)}, TwilioResponse
28
- end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ require 'goliath'
5
+
6
+ class ApiValidationAroundware
7
+ include Goliath::Rack::SimpleAroundware
8
+ class InvalidApiKeyError < Goliath::Validation::BadRequestError; end
9
+
10
+ def pre_process
11
+ validate_api_key!
12
+ env.logger.info "past api_key validation" #<-- this is output, then an empty response header & body as if it is just hanging...
13
+ Goliath::Connection::AsyncResponse
14
+ end
15
+
16
+ def post_process
17
+ [status, headers, body]
18
+ end
19
+
20
+ def validate_api_key!
21
+ server_api_key = env['config']['server_api_key'].to_s
22
+ if api_key != server_api_key
23
+ raise InvalidApiKeyError
24
+ end
25
+ end
26
+
27
+ # retreive the client's api_key
28
+ def api_key
29
+ env['HTTP_API_KEY'].to_s
30
+ end
31
+ end
32
+
33
+ class AwesomeApiWithLogging < Goliath::API
34
+ use Goliath::Rack::SimpleAroundwareFactory, ApiValidationAroundware
35
+ def response(env)
36
+ [200, {}, "Hello"]
37
+ end
38
+ end
@@ -3,7 +3,7 @@ $: << File.dirname(__FILE__)+'/../lib'
3
3
 
4
4
  require 'goliath'
5
5
  require 'em-synchrony/em-http'
6
- require 'yajl/json_gem'
6
+ require 'yajl/json_gem' if RUBY_PLATFORM != 'java'
7
7
 
8
8
  #
9
9
  # Here's a way to make an asynchronous request in the middleware, and only
@@ -45,7 +45,7 @@ BASE_URL = 'http://localhost:9002/'
45
45
  class RemoteRequestBarrier
46
46
  include Goliath::Rack::BarrierAroundware
47
47
  attr_accessor :sleep_1
48
-
48
+
49
49
  def pre_process
50
50
  # Request with delay_1 and drop_1 -- note: 'aget', because we want execution to continue
51
51
  req = EM::HttpRequest.new(BASE_URL).aget(:query => { :delay => env.params['delay_1'], :drop => env.params['drop_1'] })
@@ -2,7 +2,7 @@
2
2
  $:<< '../lib' << 'lib'
3
3
 
4
4
  require 'goliath'
5
- require 'yajl'
5
+ require 'yajl' if RUBY_PLATFORM != 'java'
6
6
 
7
7
  class AsyncUpload < Goliath::API
8
8
  use Goliath::Rack::Params # parse & merge query and body parameters
@@ -5,7 +5,7 @@ require 'em-mongo'
5
5
  require 'em-http'
6
6
  require 'em-synchrony/em-http'
7
7
  require 'em-synchrony/em-mongo'
8
- require 'yajl/json_gem'
8
+ require 'yajl/json_gem' if RUBY_PLATFORM != 'java'
9
9
 
10
10
  require File.join(File.dirname(__FILE__), 'http_log') # Use the HttpLog as our actual endpoint, but include this in the middleware
11
11
 
data/examples/clone.rb ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ require 'goliath'
5
+
6
+ class RandomAPI2 < Goliath::API
7
+ use Goliath::Rack::Params
8
+ use Goliath::Rack::Validation::Param, :key => 'user'
9
+
10
+ def response(env)
11
+ [200, {}, "Hello 2!"]
12
+ end
13
+ end
14
+
15
+ class Router < Goliath::API
16
+ map '/', RandomAPI2
17
+ end
18
+
19
+ # class PlainApi < Goliath::API
20
+ # use Goliath::Rack::Params
21
+ # use Goliath::Rack::Validation::Param, :key => 'user'
22
+
23
+ # def response(env)
24
+ # [200, {}, "Hello 2!"]
25
+ # end
26
+ # end
@@ -0,0 +1 @@
1
+ config['channel'] = EM::Channel.new
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ require 'goliath'
5
+
6
+ Goliath::Request.log_block = proc do |env, response, elapsed_time|
7
+ method = env[Goliath::Request::REQUEST_METHOD]
8
+ path = env[Goliath::Request::REQUEST_PATH]
9
+
10
+ env[Goliath::Request::RACK_LOGGER].info("#{method} #{path} in #{'%.2f' % elapsed_time} ms")
11
+ end
12
+
13
+ class SimpleAPI < Goliath::API
14
+ def response(env)
15
+ [200, {}, "It worked !"]
16
+ end
17
+ end
18
+
19
+ # [19843:INFO] 2012-02-12 18:03:44 :: GET /some/url/ in 4.35 ms
20
+ # [19843:INFO] 2012-02-12 18:03:49 :: GET /another/url/ in 4.24 ms
21
+ # [19843:INFO] 2012-02-12 18:04:01 :: PUT /another/url/ in 4.16 ms
@@ -4,54 +4,16 @@ $:<< '../lib' << 'lib'
4
4
  require 'goliath/api'
5
5
  require 'goliath/runner'
6
6
 
7
- # Example demonstrating how to use a custom rack builder with the
8
- # Goliath server and mixing Goliath APIs with normal Rack end points.
7
+ # Example demonstrating how to use a custom Goliath runner
9
8
  #
10
- # Note, that the same routing behavior is supported by Goliath, loost at
11
- # the rack_routes.rb example to see how to define custom routes.
12
9
 
13
- # Our custom Goliath API
14
- class HelloWorld < Goliath::API
10
+ class Custom < Goliath::API
15
11
  def response(env)
16
- [200, {}, "hello world!"]
17
- end
18
- end
19
-
20
- # Another Goliath API
21
- class Bonjour < Goliath::API
22
- def response(env)
23
- [200, {}, "bonjour!"]
24
- end
25
- end
26
-
27
- # Rack builder acting as a router
28
- router = Rack::Builder.new do
29
- # Rack end point
30
- map '/version' do
31
- use ::Rack::ContentLength
32
- run Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Version 0.1"]] }
33
- end
34
-
35
- # map the /hello_world uri to our Goliath API
36
- map "/hello_world" do
37
- use ::Rack::ContentLength
38
- run HelloWorld.new
39
- end
40
-
41
- # map the /bonjour uri to our other Goliath API
42
- map "/bonjour" do
43
- use ::Rack::ContentLength
44
- run Bonjour.new
45
- end
46
-
47
- # catch the root route and return a 404
48
- map "/" do
49
- use ::Rack::ContentLength
50
- run Proc.new {|env| [404, {"Content-Type" => "text/html"}, ["Try /version /hello_world or /bonjour"]] }
12
+ [200, {}, "hello!"]
51
13
  end
52
14
  end
53
15
 
54
16
  runner = Goliath::Runner.new(ARGV, nil)
55
- runner.app = router
56
- runner.run
57
-
17
+ runner.api = Custom.new
18
+ runner.app = Goliath::Rack::Builder.build(Custom, runner.api)
19
+ runner.run
@@ -4,6 +4,7 @@ require 'goliath'
4
4
 
5
5
  class EarlyAbort < Goliath::API
6
6
  include Goliath::Validation
7
+
7
8
  MAX_SIZE = 10
8
9
  TEST_FILE = "/tmp/goliath-test-error.log"
9
10
 
@@ -12,7 +13,8 @@ class EarlyAbort < Goliath::API
12
13
  env['async-headers'] = headers
13
14
 
14
15
  if env['HTTP_X_CRASH'] && env['HTTP_X_CRASH'] == 'true'
15
- raise Goliath::Validation::NotImplementedError.new("Can't handle requests with X-Crash: true.")
16
+ raise Goliath::Validation::NotImplementedError.new(
17
+ "Can't handle requests with X-Crash: true.")
16
18
  end
17
19
  end
18
20
 
@@ -22,7 +24,8 @@ class EarlyAbort < Goliath::API
22
24
  size = env['async-body'].size
23
25
 
24
26
  if size >= MAX_SIZE
25
- raise Goliath::Validation::BadRequestError.new("Payload size can't exceed #{MAX_SIZE} bytes. Received #{size.inspect} bytes.")
27
+ raise Goliath::Validation::BadRequestError.new(
28
+ "Payload size can't exceed #{MAX_SIZE} bytes. Received #{size.inspect} bytes.")
26
29
  end
27
30
  end
28
31
 
@@ -31,7 +34,7 @@ class EarlyAbort < Goliath::API
31
34
  end
32
35
 
33
36
  def response(env)
34
- File.open(TEST_FILE, "w+") { |f| f << "response that should not be here"}
37
+ File.open(TEST_FILE, "w+") { |f| f << "response that should not be here\n"}
35
38
  [200, {}, "OK"]
36
39
  end
37
40
  end
data/examples/echo.rb CHANGED
@@ -6,7 +6,7 @@ require 'goliath/plugins/latency'
6
6
 
7
7
  # Goliath uses multi-json, so pick your favorite JSON serializer
8
8
  # require 'json'
9
- require 'yajl'
9
+ require 'yajl' if RUBY_PLATFORM != 'java'
10
10
 
11
11
  class Echo < Goliath::API
12
12
  use Goliath::Rack::Tracer # log trace statistics
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ require 'goliath'
5
+ gem('fiber_pool', '1.0.0')
6
+ require 'fiber_pool'
7
+
8
+ fiber_pool = FiberPool.new(2)
9
+
10
+ Goliath::Request.execute_block = proc do |&block|
11
+ fiber_pool.spawn(&block)
12
+ end
13
+
14
+
15
+ class SimpleAPI < Goliath::API
16
+ def response(env)
17
+ start_time = Time.now.to_f
18
+ EM::Synchrony.sleep(1)
19
+
20
+ fiber_id = '%#x' % Fiber.current.object_id
21
+ [200, {}, "Request handled by fiber #{fiber_id}"]
22
+ end
23
+ end
24
+
25
+
26
+ # ab -n 5 -c 5 http://127.0.0.1:9000/
27
+
28
+ # [96681:INFO] 2012-01-14 11:35:43 :: Status: 200, Content-Length: 39, Response Time: 1015.46ms
29
+ # [96681:INFO] 2012-01-14 11:35:43 :: Status: 200, Content-Length: 39, Response Time: 1005.62ms
30
+ #
31
+ # [96681:INFO] 2012-01-14 11:35:44 :: Status: 200, Content-Length: 39, Response Time: 2001.74ms
32
+ # [96681:INFO] 2012-01-14 11:35:44 :: Status: 200, Content-Length: 39, Response Time: 2008.55ms
33
+ #
34
+ # [96681:INFO] 2012-01-14 11:35:45 :: Status: 200, Content-Length: 39, Response Time: 3005.45ms
35
+ #
@@ -0,0 +1,8 @@
1
+ require 'mysql2'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => 'em_mysql2',
4
+ :database => 'goliath_test',
5
+ :username => 'goliath',
6
+ :password => 'goliath',
7
+ :host => 'localhost',
8
+ :pool => 1)
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This example shows an ActiveRecord model being exposed as an API using Grape and then being served up using Goliath.
4
+
5
+ # 1. Install these gems
6
+ # gem install em-synchrony
7
+ # gem install mysql2
8
+ # gem install grape
9
+
10
+ # 2. Create a database
11
+ # create database goliath_test
12
+ # create user 'goliath'@'localhost' identified by 'goliath'
13
+ # grant all on goliath_test.* to 'goliath'@'localhost'
14
+ # create table users (id int not null auto_increment primary key, name varchar(255), email varchar(255));
15
+ # insert into users (name, email) values ('dan', 'dj2@everyburning.com'), ('Ilya', 'ilya@igvita.com');
16
+
17
+ # 3. Start server
18
+ # ruby ./server.rb
19
+
20
+ # 4. Try Examples
21
+ # curl http://localhost:9000/v1/users/2.json
22
+ # => {"email":"ilya@igvita.com","id":2,"name":"Ilya"}
23
+
24
+ # All users
25
+ # curl http://localhost:9000/v1/users
26
+
27
+ # Create a new user
28
+ # curl -X POST -d '{"user":{"name":"David Jones","email":"david@example.com"}}' http://localhost:9000/v1/users/create
29
+
30
+ $: << "../../lib" << "./lib"
31
+
32
+ require 'goliath'
33
+ require 'em-synchrony/activerecord'
34
+ require 'yajl' if RUBY_PLATFORM != 'java'
35
+ require 'grape'
36
+
37
+ class User < ActiveRecord::Base
38
+ end
39
+
40
+ class MyAPI < Grape::API
41
+
42
+ version 'v1', :using => :path
43
+ format :json
44
+
45
+ resource 'users' do
46
+ get "/" do
47
+ User.all
48
+ end
49
+
50
+ get "/:id" do
51
+ User.find(params['id'])
52
+ end
53
+
54
+ post "/create" do
55
+ User.create(params['user'])
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ class APIServer < Goliath::API
62
+
63
+ def response(env)
64
+ MyAPI.call(env)
65
+ end
66
+
67
+ end
data/examples/gziped.rb CHANGED
@@ -15,7 +15,7 @@ $:<< '../lib' << 'lib'
15
15
  require 'rack/deflater'
16
16
  require 'rack/rewrite'
17
17
  require 'goliath'
18
- require 'yajl'
18
+ require 'yajl' if RUBY_PLATFORM != 'java'
19
19
 
20
20
  class Gziped < Goliath::API
21
21
  # if client requested, compress the response
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ $:<< '../lib' << 'lib'
3
+
4
+ require 'goliath'
5
+ require 'multi_json'
6
+
7
+ class CustomJSON
8
+ def coerce(value, opts={})
9
+ begin
10
+ MultiJson.load(value)
11
+ rescue
12
+ return opts[:default] if opts[:default]
13
+ raise Goliath::Rack::Validation::FailedCoerce.new([400, {}, "Invalid JSON"])
14
+ end
15
+ end
16
+ end
17
+
18
+ class ParamsCoerce < Goliath::API
19
+ include Goliath::Rack::Types
20
+ use Goliath::Rack::Params
21
+ use Goliath::Rack::Validation::Param, :key => 'user_id', :as => Integer, :default => "admin"
22
+ use Goliath::Rack::Validation::Param, :key => 'flag', :as => Boolean, :message => "Flag needs to be a boolean"
23
+ use Goliath::Rack::Validation::Param, :key => 'amount', :as => Float
24
+ use Goliath::Rack::Validation::Param, :key => "json", :as => CustomJSON
25
+ use Goliath::Rack::Validation::Param, :key => "json_default", :as => CustomJSON,
26
+ :default => "nojson", :optional => true
27
+
28
+ use Goliath::Rack::Validation::Param, :key => 'name', :type => "Big Name", :message => "cant be found"
29
+
30
+ def response(env)
31
+ [200, {}, params.to_s]
32
+ end
33
+ end
34
+
35
+ # Example request: http://localhost:9000/?user_id=3d&flag=1&amount=3.0&json=[1,2,3]&name=mike
36
+