sloth-reel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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