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.
- data/.gitignore +3 -0
- data/Guardfile +8 -0
- data/HISTORY.md +10 -0
- data/LICENSE +1 -1
- data/README.md +28 -29
- data/Rakefile +10 -2
- data/examples/activerecord/config/srv.rb +2 -1
- data/examples/activerecord/srv.rb +14 -5
- data/examples/api_proxy.rb +3 -7
- data/examples/around.rb +38 -0
- data/examples/async_aroundware_demo.rb +2 -2
- data/examples/async_upload.rb +1 -1
- data/examples/auth_and_rate_limit.rb +1 -1
- data/examples/clone.rb +26 -0
- data/examples/config/websocket.rb +1 -0
- data/examples/custom_logs.rb +21 -0
- data/examples/custom_server.rb +6 -44
- data/examples/early_abort.rb +6 -3
- data/examples/echo.rb +1 -1
- data/examples/fiber_pool.rb +35 -0
- data/examples/grape/config/apiserver.rb +8 -0
- data/examples/grape/server.rb +67 -0
- data/examples/gziped.rb +1 -1
- data/examples/params.rb +36 -0
- data/examples/rasterize/rasterize.rb +1 -2
- data/examples/router.rb +15 -0
- data/examples/template.rb +14 -7
- data/examples/test.rb +31 -0
- data/examples/upload.rb +17 -0
- data/examples/views/joke.markdown +4 -4
- data/examples/websocket.rb +39 -0
- data/examples/ws/favicon.ico +0 -0
- data/examples/ws/index.erb +50 -0
- data/goliath.gemspec +24 -6
- data/lib/goliath/api.rb +15 -82
- data/lib/goliath/application.rb +3 -17
- data/lib/goliath/connection.rb +16 -9
- data/lib/goliath/constants.rb +2 -0
- data/lib/goliath/env.rb +2 -3
- data/lib/goliath/goliath.rb +24 -34
- data/lib/goliath/plugins/latency.rb +7 -3
- data/lib/goliath/rack/builder.rb +1 -37
- data/lib/goliath/rack/favicon.rb +31 -0
- data/lib/goliath/rack/formatters/json.rb +1 -1
- data/lib/goliath/rack/heartbeat.rb +1 -0
- data/lib/goliath/rack/jsonp.rb +1 -0
- data/lib/goliath/rack/params.rb +2 -17
- data/lib/goliath/rack/render.rb +0 -1
- data/lib/goliath/rack/templates.rb +18 -7
- data/lib/goliath/rack/types/base.rb +24 -0
- data/lib/goliath/rack/types/boolean.rb +18 -0
- data/lib/goliath/rack/types/core.rb +19 -0
- data/lib/goliath/rack/types/symbol.rb +16 -0
- data/lib/goliath/rack/types.rb +10 -0
- data/lib/goliath/rack/validation/coerce.rb +48 -0
- data/lib/goliath/rack/validation/param.rb +113 -0
- data/lib/goliath/rack/validation/request_method.rb +1 -1
- data/lib/goliath/rack/validation/required.rb +47 -0
- data/lib/goliath/rack/validation/required_param.rb +42 -17
- data/lib/goliath/rack/validation.rb +3 -0
- data/lib/goliath/rack.rb +2 -2
- data/lib/goliath/request.rb +41 -9
- data/lib/goliath/response.rb +0 -2
- data/lib/goliath/runner.rb +55 -4
- data/lib/goliath/server.rb +7 -3
- data/lib/goliath/test_helper.rb +70 -16
- data/lib/goliath/test_helper_ws.rb +42 -0
- data/lib/goliath/version.rb +1 -1
- data/lib/goliath/websocket.rb +80 -0
- data/pkg/goliath-0.9.4.gem +0 -0
- data/pkg/goliath-1.0.0.beta.1.gem +0 -0
- data/spec/integration/async_request_processing.rb +1 -1
- data/spec/integration/early_abort_spec.rb +3 -10
- data/spec/integration/echo_spec.rb +8 -8
- data/spec/integration/jsonp_spec.rb +31 -0
- data/spec/integration/keepalive_spec.rb +2 -2
- data/spec/integration/template_spec.rb +10 -5
- data/spec/integration/test_helper_spec.rb +33 -0
- data/spec/integration/valid_spec.rb +35 -5
- data/spec/integration/websocket_spec.rb +44 -0
- data/spec/unit/api_spec.rb +2 -19
- data/spec/unit/connection_spec.rb +3 -0
- data/spec/unit/rack/formatters/json_spec.rb +3 -3
- data/spec/unit/rack/heartbeat_spec.rb +13 -0
- data/spec/unit/rack/params_spec.rb +2 -8
- data/spec/unit/rack/validation/param_spec.rb +443 -0
- data/spec/unit/rack/validation/request_method_spec.rb +5 -0
- data/spec/unit/rack/validation/required_param_spec.rb +71 -1
- data/spec/unit/runner_spec.rb +21 -7
- data/test/echo_test.rb +25 -0
- data/test/test_helper.rb +5 -0
- metadata +316 -78
- data/examples/env_use_statements.rb +0 -20
- data/examples/favicon.rb +0 -40
- data/examples/rack_routes.rb +0 -125
- data/examples/rasterize/thumb/f7ad4cb03e5bfd0e2c43db8e598fb3cd.png +0 -0
- data/examples/valid.rb +0 -17
- data/lib/goliath/deprecated/async_aroundware.rb +0 -133
- data/lib/goliath/deprecated/mongo_receiver.rb +0 -84
- data/lib/goliath/deprecated/response_receiver.rb +0 -97
- data/spec/integration/rack_routes_spec.rb +0 -169
- data/spec/unit/rack/builder_spec.rb +0 -40
data/.gitignore
CHANGED
data/Guardfile
ADDED
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
data/README.md
CHANGED
@@ -1,22 +1,28 @@
|
|
1
1
|
# Goliath
|
2
2
|
|
3
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
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
|
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.
|
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
|
-
|
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
|
-
|
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 => [:
|
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|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# gem install
|
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 '
|
16
|
-
|
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.
|
31
|
-
[200, {}, User.find(params['id'])]
|
40
|
+
[200, {}, User.find(params['id']).to_json]
|
32
41
|
end
|
33
42
|
end
|
data/examples/api_proxy.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
|
5
5
|
require 'goliath'
|
6
6
|
require 'em-synchrony/em-http'
|
7
|
-
|
8
|
-
class
|
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
|
data/examples/around.rb
ADDED
@@ -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'] })
|
data/examples/async_upload.rb
CHANGED
@@ -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
|
data/examples/custom_server.rb
CHANGED
@@ -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
|
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
|
-
|
14
|
-
class HelloWorld < Goliath::API
|
10
|
+
class Custom < Goliath::API
|
15
11
|
def response(env)
|
16
|
-
[200, {}, "hello
|
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.
|
56
|
-
runner.
|
57
|
-
|
17
|
+
runner.api = Custom.new
|
18
|
+
runner.app = Goliath::Rack::Builder.build(Custom, runner.api)
|
19
|
+
runner.run
|
data/examples/early_abort.rb
CHANGED
@@ -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(
|
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(
|
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,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
data/examples/params.rb
ADDED
@@ -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
|
+
|