rhino 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8b47f9498fbbff61d889afd889a68ff4bc14ea81
4
+ data.tar.gz: a9c6b1a40eab3c1ee0e7a1960f1f84c60a00fe59
5
+ SHA512:
6
+ metadata.gz: da2fb8d7669d10f5afa4cbb4478463f25a87368b2f8d2da43ac55477899b7c54692e7a8c1c5857c84053e423dc82a18d5930352ef11659a0f8dccc4cf6acf3b9
7
+ data.tar.gz: 0a0914fb9196ee796a989fde3b19f00d159563719e697cff001fad467cbd669cfc73753434391ba860b8c362a94a068e01222536b1847d477ac3ec1184328588
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -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.
@@ -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.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rhino'
4
+
5
+ cli = Rhino::CLI.new
6
+ cli.parse
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,0 +1,41 @@
1
+ require 'socket'
2
+
3
+ module Rhino
4
+ class Server
5
+ attr_accessor :application
6
+ attr_accessor :sockets
7
+
8
+ def initialize(application, sockets)
9
+ self.application = application
10
+ self.sockets = sockets
11
+ end
12
+
13
+ def run
14
+ loop do
15
+ begin
16
+ monitor
17
+ rescue Interrupt
18
+ Rhino.logger.log("INTERRUPTED")
19
+ return
20
+ end
21
+ end
22
+ end
23
+
24
+ def monitor
25
+ selections, _, _ = IO.select(self.sockets)
26
+ io, _ = selections
27
+
28
+ begin
29
+ socket, _ = io.accept
30
+ http = Rhino::HTTP::new(socket)
31
+ http.handle(application)
32
+ rescue ::Errno::ECONNRESET , ::Errno::ENOTCONN
33
+ rescue::Exception => exception
34
+ Rhino.logger.log("EXCEPTION: #{exception.message}")
35
+ ensure
36
+ socket.close
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module Rhino
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rhino
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Sylvestre
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: This should probably never be used unless you are feeling lucky.
98
+ email:
99
+ - kevin@ksylvest.com
100
+ executables:
101
+ - rhino
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - Gemfile
106
+ - LICENSE
107
+ - README.rdoc
108
+ - bin/rhino
109
+ - lib/rhino.rb
110
+ - lib/rhino/cli.rb
111
+ - lib/rhino/config.rb
112
+ - lib/rhino/http.rb
113
+ - lib/rhino/launcher.rb
114
+ - lib/rhino/logger.rb
115
+ - lib/rhino/server.rb
116
+ - lib/rhino/version.rb
117
+ homepage: https://github.com/ksylvest/rhino
118
+ licenses: []
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.1
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: A web server written for fun.
140
+ test_files: []