sloth-reel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 706dfba4a0493e2f17b2eaddbe2ff554bec480daf299e594b7ef6097bc93dbd5
4
+ data.tar.gz: 26d80753d69cadacf314de7f84ae7e14fb3b2d42d095b6ea4ee7b043c8e815a0
5
+ SHA512:
6
+ metadata.gz: d209cf1c4e7513e622654073940802ae537da1af7225bae0ebe5bb1ccbfe3041eb89c50cdfbf56a39639fa9ebadbe29d5921240e37148618885d61c5572efa75
7
+ data.tar.gz: d3f2e8fde962829942ccc4f9035cdeae2febedac7ceb60d1ace350cabf8b18eacd0d81d4a69afafaee159ed18260149dbad49cf3866a34b744bb7d0abffac4ee
@@ -0,0 +1,23 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/
16
+ *.pid
17
+ *.log
18
+ *.log.*
19
+ /var/
20
+ /log/
21
+ *.swp
22
+ /.rspec_status
23
+
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.2
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in snmp_handler.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,65 @@
1
+ = Reels
2
+
3
+ Httpd and WebSocket sloth framework based on Celluloid, Reel, Rack and Sinatra.
4
+
5
+ == Features
6
+
7
+ * Handle Sinatra asynchronously.
8
+ * Coordinate Sinatra and WebSocket.
9
+ * The implementation is a monkey patch to Reel, Sinatra and WebSocket.
10
+ * It is not a stable version because it depends on the celluloid development version.
11
+
12
+ == Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ [source,ruby]
17
+ ----
18
+ gem 'sloth-reel'
19
+ ----
20
+
21
+ And then execute:
22
+
23
+ $ bundle install
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install sloth-reel
28
+ or
29
+ $ gem install -l sloth-reel-x.x.x.gem
30
+
31
+ == Usage
32
+
33
+ === Example 1
34
+
35
+ [source,ruby]
36
+ ----
37
+ require 'sloth/reel'
38
+
39
+ class WebApp < Sinatra::Base
40
+ get "/" do
41
+ '<html> <body> <form method="POST"> <input type="submit" value="Hello." /> </form> </body> </html>'
42
+ end
43
+
44
+ post "/" do
45
+ '<html> <body> Howdy. </body> </html>'
46
+ end
47
+ end
48
+
49
+ Reel::Rack::Server.new( WebApp.new, Host: "0.0.0.0", Port: 3000 )
50
+
51
+ sleep
52
+ ----
53
+
54
+ == Reference
55
+
56
+
57
+ == Contributing
58
+
59
+ Bug reports and pull requests are welcome on GitHub at https://github.com/arimay/sloth-reel.
60
+
61
+ == License
62
+
63
+ The gem is available as open source under the terms of the http://opensource.org/licenses/MIT[MIT License].
64
+
65
+ Copyright (c) ARIMA Yasuhiro <arima.yasuhiro@gmail.com>
@@ -0,0 +1,65 @@
1
+ = Reels
2
+
3
+ Httpd と WebSocket のナマケモノフレームワーク. Celluloid, Reel, Rack, Sinatra に基づく.
4
+
5
+ == 特徴
6
+
7
+ * 非同期で Sinatra を扱う.
8
+ * Sinatra と WebSocket を協調させる.
9
+ * 実装は Reel、Sinatra、および WebSocket へのモンキーパッチ.
10
+ * Celluloid 開発版に依存するため、安定版ではない。
11
+
12
+ == 導入
13
+
14
+ アプリの Gemfile にこの行を追加
15
+
16
+ [source,ruby]
17
+ ----
18
+ gem 'sloth-reel'
19
+ ----
20
+
21
+ それから実行
22
+
23
+ $ bundle install
24
+
25
+ または次のように手動で導入
26
+
27
+ $ gem install sloth-reel
28
+ or
29
+ $ gem install -l sloth-reel-x.x.x.gem
30
+
31
+ == 使い方
32
+
33
+ === Example 1
34
+
35
+ [source,ruby]
36
+ ----
37
+ require 'sloth/reel'
38
+
39
+ class WebApp < Sinatra::Base
40
+ get "/" do
41
+ '<html> <body> <form method="POST"> <input type="submit" value="Hello." /> </form> </body> </html>'
42
+ end
43
+
44
+ post "/" do
45
+ '<html> <body> Howdy. </body> </html>'
46
+ end
47
+ end
48
+
49
+ Reel::Rack::Server.new( WebApp.new, Host: "0.0.0.0", Port: 3000 )
50
+
51
+ sleep
52
+ ----
53
+
54
+ == リファレンス
55
+
56
+
57
+ == 貢献
58
+
59
+ 不具合報告とプルリクエストは GitHub https://github.com/arimay/sloth-reel まで.
60
+
61
+ == ライセンス
62
+
63
+ この Gem は、 http://opensource.org/licenses/MIT[MITライセンス] の条件に基づいてオープンソースとして入手できる.
64
+
65
+ Copyright (c) ARIMA Yasuhiro <arima.yasuhiro@gmail.com>
@@ -0,0 +1,96 @@
1
+ require "bundler/gem_helper"
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ class Bundler::GemHelper
10
+
11
+ def git_archive( dir = "../zip" )
12
+ FileUtils.mkdir_p dir
13
+ dest_path = File.join(dir, "#{name}-#{version}.zip")
14
+ cmnd = "git archive --format zip --prefix=#{name}/ HEAD > #{dest_path}"
15
+
16
+ _, code = sh_with_status( cmnd )
17
+ raise "Couldn't archive gem," unless code == 0
18
+
19
+ Bundler.ui.confirm "#{name} #{version} archived to #{dest_path}."
20
+ end
21
+
22
+ def git_push
23
+ ver = version.to_s
24
+
25
+ cmnd = "git push origin #{ver} "
26
+ _, code = sh_with_status( cmnd )
27
+ raise "Couldn't git push origin." unless code == 0
28
+
29
+ cmnd = "git push "
30
+ _, code = sh_with_status( cmnd )
31
+ raise "Couldn't git push." unless code == 0
32
+
33
+ Bundler.ui.confirm "Git Push #{ver}."
34
+ end
35
+
36
+ def update_version( new_version )
37
+ version_filename = %x[ find . -type f -name "version.rb" | grep -v vendor | head -1 ].chomp
38
+ version_pathname = File.expand_path( version_filename )
39
+ lines = File.open( version_pathname ).read
40
+ lines = lines.gsub( /VERSION\s*=\s*\"\d+\.\d+\.\d+\"/, "VERSION = \"#{new_version}\"" )
41
+ File.open( version_pathname, "w" ) do |file|
42
+ file.write( lines )
43
+ end
44
+
45
+ cmnd = "git add #{version_pathname} "
46
+ _, code = sh_with_status( cmnd )
47
+ raise "Couldn't git add," unless code == 0
48
+
49
+ cmnd = "git commit -m '#{new_version}' "
50
+ _, code = sh_with_status( cmnd )
51
+ raise "Couldn't git commit." unless code == 0
52
+
53
+ cmnd = "git tag #{new_version} "
54
+ _, code = sh_with_status( cmnd )
55
+ raise "Couldn't git tag." unless code == 0
56
+
57
+ Bundler.ui.confirm "Update Tags to #{new_version}."
58
+ end
59
+
60
+ end
61
+
62
+ Bundler::GemHelper.new(Dir.pwd).instance_eval do
63
+
64
+ desc "Archive #{name}-#{version}.zip from repository"
65
+ task 'zip' do
66
+ git_archive
67
+ end
68
+
69
+ desc "Git Push"
70
+ task 'push' do
71
+ git_push
72
+ end
73
+
74
+ desc "Update Version Tiny"
75
+ task 'tiny' do
76
+ major, minor, tiny = version.to_s.split('.')
77
+ new_version = [major, minor, tiny.to_i + 1].join('.')
78
+ update_version( new_version )
79
+ end
80
+
81
+ desc "Update Version Minor"
82
+ task 'minor' do
83
+ major, minor, _tiny = version.to_s.split('.')
84
+ new_version = [major, minor.to_i + 1, 0].join('.')
85
+ update_version( new_version )
86
+ end
87
+
88
+ desc "Update Version Major"
89
+ task 'major' do
90
+ major, _minor, _tiny = version.to_s.split('.')
91
+ new_version = [major.to_i + 1, 0, 0].join('.')
92
+ update_version( new_version )
93
+ end
94
+
95
+ end
96
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sloth/reel"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,127 @@
1
+ # Adapted from code orinially Copyright (c) 2013 Jonathan Stott
2
+
3
+ require 'reel'
4
+ require 'rack'
5
+
6
+ module Reel
7
+ module Rack
8
+ class Server < Reel::Server::HTTP
9
+ include Celluloid::Internals::Logger
10
+
11
+ attr_reader :app
12
+
13
+ def initialize(app, options)
14
+ raise ArgumentError, "no host given" unless options[:Host]
15
+ raise ArgumentError, "no port given" unless options[:Port]
16
+
17
+ info "A Reel good HTTP server! (Codename \"#{::Reel::CODENAME}\")"
18
+ info "Listening on http://#{options[:Host]}:#{options[:Port]}"
19
+
20
+ super(options[:Host], options[:Port], options, &method(:on_connection))
21
+ @app = app
22
+ end
23
+
24
+ def on_connection(connection)
25
+ connection.each_request do |request|
26
+ if request.websocket?
27
+ connection.detach
28
+ route_websocket request
29
+ return
30
+ else
31
+ route_request request
32
+ end
33
+ end
34
+ end
35
+
36
+ def route_websocket( request )
37
+ options = {
38
+ :method => request.method,
39
+ :input => request.body.to_s,
40
+ "websocket" => request.websocket,
41
+ }.merge(convert_headers(request.headers))
42
+
43
+ normalize_env(options)
44
+
45
+ app.call ::Rack::MockRequest.env_for(request.url, options)
46
+ nil
47
+ end
48
+
49
+ # Compile the regex once
50
+ CONTENT_LENGTH_HEADER = %r{^content-length$}i
51
+
52
+ def route_request(request)
53
+ options = {
54
+ :method => request.method,
55
+ :input => request.body.to_s,
56
+ "REMOTE_ADDR" => request.remote_addr
57
+ }.merge(convert_headers(request.headers))
58
+
59
+ normalize_env(options)
60
+
61
+ status, headers, body = app.call ::Rack::MockRequest.env_for(request.url, options)
62
+
63
+ if body.respond_to? :each
64
+ # If Content-Length was specified we can send the response all at once
65
+ if headers.keys.detect { |h| h =~ CONTENT_LENGTH_HEADER }
66
+ # Can't use collect here because Rack::BodyProxy/Rack::Lint isn't a real Enumerable
67
+ full_body = ''
68
+ body.each { |b| full_body << b }
69
+ request.respond status_symbol(status), headers, full_body
70
+ else
71
+ request.respond status_symbol(status), headers.merge(:transfer_encoding => :chunked)
72
+ body.each { |chunk| request << chunk }
73
+ request.finish_response
74
+ end
75
+ else
76
+ Logger.error("don't know how to render: #{body.inspect}")
77
+ request.respond :internal_server_error, "An error occurred processing your request"
78
+ end
79
+
80
+ body.close if body.respond_to? :close
81
+ end
82
+
83
+ # Those headers must not start with 'HTTP_'.
84
+ NO_PREFIX_HEADERS=%w[CONTENT_TYPE CONTENT_LENGTH].freeze
85
+
86
+ def convert_headers(headers)
87
+ Hash[headers.map { |key, value|
88
+ header = key.upcase.gsub('-','_')
89
+
90
+ if NO_PREFIX_HEADERS.member?(header)
91
+ [header, value]
92
+ else
93
+ ['HTTP_' + header, value]
94
+ end
95
+ }]
96
+ end
97
+
98
+ # Copied from lib/puma/server.rb
99
+ def normalize_env(env)
100
+ if host = env["HTTP_HOST"]
101
+ if colon = host.index(":")
102
+ env["SERVER_NAME"] = host[0, colon]
103
+ env["SERVER_PORT"] = host[colon+1, host.bytesize]
104
+ else
105
+ env["SERVER_NAME"] = host
106
+ env["SERVER_PORT"] = default_server_port(env)
107
+ end
108
+ else
109
+ env["SERVER_NAME"] = "localhost"
110
+ env["SERVER_PORT"] = default_server_port(env)
111
+ end
112
+ end
113
+
114
+ def default_server_port(env)
115
+ env['HTTP_X_FORWARDED_PROTO'] == 'https' ? 443 : 80
116
+ end
117
+
118
+ def status_symbol(status)
119
+ if status.is_a?(Integer)
120
+ Reel::Response::STATUS_CODES[status].downcase.gsub(/\s|-/, '_').to_sym
121
+ else
122
+ status.to_sym
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,78 @@
1
+ module Reel
2
+ # Base class for Reel servers.
3
+ #
4
+ # This class is a Celluloid::IO actor which provides a barebones server
5
+ # which does not open a socket itself, it just begin handling connections once
6
+ # initialized with a specific kind of protocol-based server.
7
+
8
+ # For specific protocol support, use:
9
+
10
+ # Reel::Server::HTTP
11
+ # Reel::Server::HTTPS
12
+ # Coming soon: Reel::Server::UNIX
13
+
14
+ class Server
15
+ include Celluloid::IO
16
+ # How many connections to backlog in the TCP accept queue
17
+ DEFAULT_BACKLOG = 100
18
+ MAX_CONNECTION = 16
19
+
20
+ execute_block_on_receiver :initialize
21
+ finalizer :shutdown
22
+
23
+ def initialize(server, options={}, &callback)
24
+ @spy = STDOUT if options[:spy]
25
+ @options = options
26
+ @callback = callback
27
+ @server = server
28
+ @max_connection = options[:max_connection] || MAX_CONNECTION
29
+
30
+ @server.listen(options.fetch(:backlog, DEFAULT_BACKLOG))
31
+
32
+ async.run
33
+ end
34
+
35
+ def shutdown
36
+ @server.close if @server
37
+ info "Terminate."
38
+ end
39
+
40
+ def run
41
+ queue = Queue.new
42
+ (1..@max_connection).each do
43
+ Thread.start do
44
+ Thread.current.report_on_exception = false rescue nil
45
+ while sock = queue.pop
46
+ handle_connection sock
47
+ end
48
+ end
49
+ end
50
+ info "Ready."
51
+ loop do
52
+ queue.push @server.accept
53
+ end
54
+ ensure
55
+ info "Terminate."
56
+ end
57
+
58
+ def handle_connection(socket)
59
+ if @spy
60
+ require 'reel/spy'
61
+ socket = Reel::Spy.new(socket, @spy)
62
+ end
63
+
64
+ connection = Connection.new(socket)
65
+
66
+ begin
67
+ @callback.call(connection)
68
+ ensure
69
+ if connection.attached?
70
+ connection.close rescue nil
71
+ end
72
+ end
73
+ rescue RequestError, EOFError
74
+ # Client disconnected prematurely
75
+ # TODO: log this?
76
+ end
77
+ end
78
+ end