Skip to content

Commit 7825f6f

Browse files
committed
import
0 parents  commit 7825f6f

26 files changed

+680
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.bundle
2+
pkg
3+
tmp
4+
coverage

.rspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--color

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
language: ruby
2+
rvm:
3+
- 2.3.1

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source "https://rubygems.org"
2+
3+
gemspec

Gemfile.lock

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
PATH
2+
remote: .
3+
specs:
4+
rhino (0.0.1)
5+
rack
6+
slop
7+
8+
GEM
9+
remote: https://rubygems.org/
10+
specs:
11+
coveralls (0.8.15)
12+
json (>= 1.8, < 3)
13+
simplecov (~> 0.12.0)
14+
term-ansicolor (~> 1.3)
15+
thor (~> 0.19.1)
16+
tins (>= 1.6.0, < 2)
17+
diff-lcs (1.2.5)
18+
docile (1.1.5)
19+
json (2.0.2)
20+
rack (2.0.1)
21+
rake (11.3.0)
22+
rspec (3.5.0)
23+
rspec-core (~> 3.5.0)
24+
rspec-expectations (~> 3.5.0)
25+
rspec-mocks (~> 3.5.0)
26+
rspec-core (3.5.3)
27+
rspec-support (~> 3.5.0)
28+
rspec-expectations (3.5.0)
29+
diff-lcs (>= 1.2.0, < 2.0)
30+
rspec-support (~> 3.5.0)
31+
rspec-mocks (3.5.0)
32+
diff-lcs (>= 1.2.0, < 2.0)
33+
rspec-support (~> 3.5.0)
34+
rspec-support (3.5.0)
35+
simplecov (0.12.0)
36+
docile (~> 1.1.0)
37+
json (>= 1.8, < 3)
38+
simplecov-html (~> 0.10.0)
39+
simplecov-html (0.10.0)
40+
slop (4.4.1)
41+
term-ansicolor (1.3.2)
42+
tins (~> 1.0)
43+
thor (0.19.1)
44+
tins (1.12.0)
45+
46+
PLATFORMS
47+
ruby
48+
49+
DEPENDENCIES
50+
bundler
51+
coveralls
52+
rhino!
53+
rake
54+
rspec
55+
56+
BUNDLED WITH
57+
1.13.1

LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2015 - 2016 Kevin Sylvestre
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.rdoc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
= Rhino
2+
3+
Rhino is a simple Ruby server that can run rack apps. It is written as a fun experiment.
4+
5+
== Installation
6+
7+
gem install rhino
8+
9+
== Usage
10+
11+
rhino
12+
13+
== Advanced
14+
15+
rhino [options] [./config.ru]
16+
-h, --help help
17+
-v, --version version
18+
-b, --bind bind (default: 0.0.0.0)
19+
-p, --port port (default: 5000)
20+
--backlog backlog (default: 64)
21+
--reuseaddr reuseaddr (default: true)
22+
23+
== Status
24+
25+
{<img src="https://img.shields.io/travis/ksylvest/rhino.svg" />}[https://travis-ci.org/ksylvest/rhino]
26+
{<img src="https://img.shields.io/gemnasium/ksylvest/rhino.svg" />}[https://gemnasium.com/ksylvest/rhino]
27+
{<img src="https://img.shields.io/coveralls/ksylvest/rhino.svg" />}[https://coveralls.io/r/ksylvest/rhino]
28+
{<img src="https://img.shields.io/codeclimate/github/ksylvest/rhino.svg" />}[https://codeclimate.com/github/ksylvest/rhino]
29+
30+
== Copyright
31+
32+
Copyright (c) 2015 - 2016 Kevin Sylvestre. See LICENSE for details.

Rakefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
require "bundler/gem_tasks"
2+
require "rspec/core/rake_task"
3+
4+
RSpec::Core::RakeTask.new(:spec)
5+
6+
task default: :spec

bin/rhino

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'rhino'
4+
5+
cli = Rhino::CLI.new
6+
cli.parse

lib/rhino.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require "rack"
2+
3+
require "rhino/cli"
4+
require "rhino/config"
5+
require "rhino/http"
6+
require "rhino/launcher"
7+
require "rhino/logger"
8+
require "rhino/server"
9+
require "rhino/version"
10+
11+
module Rhino
12+
def self.config
13+
@@config ||= Rhino::Config.new
14+
end
15+
16+
def self.logger
17+
@@logger ||= Rhino::Logger.new
18+
end
19+
end

lib/rhino/cli.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require "slop"
2+
3+
module Rhino
4+
5+
# A wrapper for command line interaction that encompasses option parsing, version, help, and execution. This class is
6+
# instantiated from the binary. The class also handles bootstrapping the launcher.
7+
#
8+
# Usage:
9+
#
10+
# cli = Rhino::CLI.new
11+
# cli.parse()
12+
#
13+
class CLI
14+
BANNER = "usage: rhino [options] [./config.ru]".freeze
15+
16+
def parse(items = ARGV)
17+
config = Slop.parse(items) do |options|
18+
options.banner = BANNER
19+
20+
options.on "-h", "--help", 'help' do
21+
return help(options)
22+
end
23+
24+
options.on "-v", "--version", 'version' do
25+
version
26+
exit
27+
end
28+
29+
options.string "-b", "--bind", 'bind (default: 0.0.0.0)', default: "0.0.0.0"
30+
options.integer "-p", "--port", 'port (default: 5000)', default: 5000
31+
options.integer "--backlog", 'backlog (default: 64)', default: 64
32+
options.boolean "--reuseaddr", 'reuseaddr (default: true)', default: true
33+
end
34+
35+
run(config)
36+
end
37+
38+
private
39+
40+
def help(options)
41+
Rhino.logger.log("#{options}")
42+
end
43+
44+
def version
45+
Rhino.logger.log("#{VERSION}")
46+
end
47+
48+
def run(options)
49+
config, _ = options.arguments
50+
Launcher.new(options[:port], options[:bind], options[:reuseaddr], options[:backlog], config || './config.ru').run
51+
end
52+
53+
end
54+
end

lib/rhino/config.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Rhino
2+
class Config
3+
attr_accessor :multithread
4+
attr_accessor :multiprocess
5+
attr_accessor :run_once
6+
end
7+
end

lib/rhino/http.rb

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
require "uri"
2+
require "rack"
3+
require "time"
4+
5+
module Rhino
6+
class HTTP
7+
RESERVED = /\A(Date|Connection)\Z/i.freeze
8+
VERSION = "HTTP/1.1".freeze
9+
CRLF = "\r\n".freeze
10+
11+
class Exception < ::Exception
12+
end
13+
14+
attr_accessor :socket
15+
16+
def initialize(socket)
17+
self.socket = socket
18+
end
19+
20+
def parse
21+
rl = socket.gets
22+
matches = /\A(?<method>\S+)\s+(?<uri>\S+)\s+(?<version>\S+)#{CRLF}\Z/.match(rl)
23+
raise Exception.new("invalid request line: #{rl.inspect}") if !matches
24+
begin
25+
uri = URI.parse(matches[:uri])
26+
rescue URI::InvalidURIError
27+
raise Exception.new("invalid URI in request line: #{rl.inspect}")
28+
end
29+
30+
env = {
31+
"rack.errors" => $stderr,
32+
"rack.input" => socket,
33+
"rack.version" => Rack::VERSION,
34+
"rack.multithread" => !!Rhino.config.multithread,
35+
"rack.multiprocess" => !!Rhino.config.multiprocess,
36+
"rack.run_once" => !!Rhino.config.run_once,
37+
"rack.url_scheme" => uri.scheme || "http",
38+
"REQUEST_METHOD" => matches[:method],
39+
"REQUEST_URI" => matches[:uri],
40+
"HTTP_VERSION" => matches[:version],
41+
"QUERY_STRING" => uri.query || "",
42+
"SERVER_PORT" => uri.port || 80,
43+
"SERVER_NAME" => uri.host || "localhost",
44+
"PATH_INFO" => uri.path || "",
45+
"SCRIPT_NAME" => "",
46+
}
47+
48+
while matches = /\A(?<key>[^:]+):\s+(?<value>.*)#{CRLF}\Z/.match(line = socket.gets) do
49+
50+
case matches[:key]
51+
when Rack::CONTENT_TYPE then env["CONTENT_TYPE"] = matches[:value]
52+
when Rack::CONTENT_LENGTH then env["CONTENT_LENGTH"] = Integer(matches[:value])
53+
else env["HTTP_" + matches[:key].tr("-", "_").upcase] ||= matches[:value]
54+
end
55+
end
56+
57+
input = socket.read(env["CONTENT_LENGTH"] || 0)
58+
59+
env["rack.input"] = StringIO.new(input)
60+
61+
return env
62+
end
63+
64+
def handle(application)
65+
env = parse
66+
status, headers, body = application.call(env)
67+
time = Time.now.httpdate
68+
69+
socket.write "#{VERSION} #{status} #{Rack::Utils::HTTP_STATUS_CODES.fetch(status) { 'UNKNOWN' }}#{CRLF}"
70+
socket.write "Date: #{time}#{CRLF}"
71+
socket.write "Connection: close#{CRLF}"
72+
73+
headers.each do |key, value|
74+
if !RESERVED.match(key)
75+
socket.write "#{key}: #{value}#{CRLF}"
76+
end
77+
end
78+
79+
socket.write(CRLF)
80+
81+
body.each do |chunk|
82+
socket.write(chunk)
83+
end
84+
85+
Rhino.logger.log("[#{time}] '#{env["REQUEST_METHOD"]} #{env["REQUEST_URI"]} #{env["HTTP_VERSION"]}' #{status}")
86+
end
87+
end
88+
end

lib/rhino/launcher.rb

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module Rhino
2+
class Launcher
3+
attr_accessor :port
4+
attr_accessor :bind
5+
attr_accessor :backlog
6+
attr_accessor :config
7+
attr_accessor :reuseaddr
8+
9+
def initialize(port, bind, reuseaddr, backlog, config)
10+
self.port = port
11+
self.bind = bind
12+
self.reuseaddr = reuseaddr
13+
self.backlog = backlog
14+
self.config = config
15+
end
16+
17+
def run
18+
Rhino.logger.log("Rhino")
19+
Rhino.logger.log("#{bind}:#{port}")
20+
21+
begin
22+
socket = Socket.new(:INET, :STREAM)
23+
socket.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, reuseaddr)
24+
socket.bind(Addrinfo.tcp(self.bind, self.port))
25+
socket.listen(self.backlog)
26+
27+
server = Rhino::Server.new(application, [socket])
28+
server.run
29+
ensure
30+
socket.close
31+
end
32+
end
33+
34+
private
35+
36+
def application
37+
raw = File.read(self.config)
38+
builder = <<~BUILDER
39+
Rack::Builder.new do
40+
#{raw}
41+
end
42+
BUILDER
43+
@application ||= eval(builder, nil, self.config)
44+
end
45+
46+
end
47+
end

lib/rhino/logger.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Rhino
2+
class Logger
3+
attr_accessor :stream
4+
5+
def initialize(stream = STDOUT)
6+
self.stream = stream
7+
end
8+
9+
def log(message)
10+
self.stream.puts "#{message}"
11+
end
12+
13+
end
14+
end

0 commit comments

Comments
 (0)