toycol 0.0.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- data/.github/dependabot.yml +9 -0
- data/.github/workflows/main.yml +4 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +26 -1
- data/CHANGELOG.md +1 -1
- data/README.md +2 -4
- data/examples/duck/Gemfile +5 -0
- data/examples/duck/Protocolfile.duck +23 -0
- data/examples/duck/config_duck.ru +59 -0
- data/examples/rubylike/Gemfile +5 -0
- data/examples/rubylike/Protocolfile.rubylike +18 -0
- data/examples/rubylike/config_rubylike.ru +27 -0
- data/examples/safe_ruby/Gemfile +6 -0
- data/examples/safe_ruby/Protocolfile.safe_ruby +47 -0
- data/examples/safe_ruby/config_safe_ruby.ru +55 -0
- data/examples/safe_ruby_with_sinatra/Gemfile +10 -0
- data/examples/safe_ruby_with_sinatra/Protocolfile.safe_ruby_with_sinatra +44 -0
- data/examples/safe_ruby_with_sinatra/app.rb +28 -0
- data/examples/safe_ruby_with_sinatra/post.rb +34 -0
- data/examples/safe_ruby_with_sinatra/views/index.erb +32 -0
- data/examples/unsafe_ruby/Gemfile +5 -0
- data/examples/unsafe_ruby/Protocolfile.unsafe_ruby +15 -0
- data/examples/unsafe_ruby/config_unsafe_ruby.ru +24 -0
- data/exe/toycol +6 -0
- data/lib/rack/handler/toycol.rb +65 -0
- data/lib/toycol.rb +20 -1
- data/lib/toycol/client.rb +38 -0
- data/lib/toycol/command.rb +132 -0
- data/lib/toycol/const.rb +102 -0
- data/lib/toycol/helper.rb +27 -0
- data/lib/toycol/protocol.rb +126 -0
- data/lib/toycol/proxy.rb +116 -0
- data/lib/toycol/server.rb +124 -0
- data/lib/toycol/version.rb +1 -1
- data/toycol.gemspec +3 -7
- metadata +48 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 102026f42040d677807fb5fe1cd08797f564951096c15ac2c0150a757cc82926
|
4
|
+
data.tar.gz: d7b4312381d610821b197f688c871acbe26a6d70146c67441d23cbd52a0ed697
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 11eb3611d926c804f1f0d632f40daf2a37b7f663bdb449c69c2353d608dba87161723f4380d5dedfa78a46f1d264dde625b9bdd30ba6dda6e64eed12964f83cf
|
7
|
+
data.tar.gz: 9c38815ef3861ad35ac635f713fb2f6b459ee6cbc64cbe9da56ec355381a5f21c30189e28ebe92f961406b99d9349631b52571729fd8c290d94cd61c0eefd4ea
|
@@ -0,0 +1,29 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: "[BUG]"
|
5
|
+
labels: bug
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## Describe the bug
|
11
|
+
A clear and concise description of what the bug is.
|
12
|
+
|
13
|
+
## To Reproduce
|
14
|
+
Steps to reproduce the behavior:
|
15
|
+
1. Go to '...'
|
16
|
+
2. Click on '....'
|
17
|
+
3. Scroll down to '....'
|
18
|
+
4. See error
|
19
|
+
|
20
|
+
## Expected behavior
|
21
|
+
A clear and concise description of what you expected to happen.
|
22
|
+
|
23
|
+
## Environment
|
24
|
+
- Ruby Version:
|
25
|
+
- Rack Version:
|
26
|
+
- Puma Version:
|
27
|
+
|
28
|
+
## Additional context
|
29
|
+
Add any other context about the problem here.
|
data/.github/workflows/main.yml
CHANGED
@@ -5,12 +5,15 @@ on: [push,pull_request]
|
|
5
5
|
jobs:
|
6
6
|
build:
|
7
7
|
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [2.6, 2.7, 3.0, head]
|
8
11
|
steps:
|
9
12
|
- uses: actions/checkout@v2
|
10
13
|
- name: Set up Ruby
|
11
14
|
uses: ruby/setup-ruby@v1
|
12
15
|
with:
|
13
|
-
ruby-version:
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
14
17
|
bundler-cache: true
|
15
18
|
- name: Run the default task
|
16
19
|
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -9,7 +9,14 @@ AllCops:
|
|
9
9
|
- 'Rakefile'
|
10
10
|
- 'toycol.gemspec'
|
11
11
|
NewCops: enable
|
12
|
-
TargetRubyVersion:
|
12
|
+
TargetRubyVersion: 2.6
|
13
|
+
|
14
|
+
Style/Documentation:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Style/StringConcatenation:
|
18
|
+
Exclude:
|
19
|
+
- lib/toycol/server.rb
|
13
20
|
|
14
21
|
Style/StringLiterals:
|
15
22
|
Enabled: true
|
@@ -21,3 +28,21 @@ Style/StringLiteralsInInterpolation:
|
|
21
28
|
|
22
29
|
Layout/LineLength:
|
23
30
|
Max: 120
|
31
|
+
|
32
|
+
Layout/HashAlignment:
|
33
|
+
Exclude:
|
34
|
+
- lib/toycol/server.rb
|
35
|
+
|
36
|
+
Metrics:
|
37
|
+
Exclude:
|
38
|
+
- lib/toycol/proxy.rb
|
39
|
+
- lib/toycol/server.rb
|
40
|
+
|
41
|
+
Metrics/AbcSize:
|
42
|
+
Max: 30
|
43
|
+
|
44
|
+
Metrics/ClassLength:
|
45
|
+
Max: 400
|
46
|
+
|
47
|
+
Metrics/MethodLength:
|
48
|
+
Max: 15
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Toycol
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Toy Application Protocol framework
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -22,7 +20,7 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
|
23
|
+
WIP
|
26
24
|
|
27
25
|
## Development
|
28
26
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Toycol::Protocol.define(:duck) do
|
4
|
+
custom_status_codes(
|
5
|
+
600 => "I'm afraid you are not a duck..."
|
6
|
+
)
|
7
|
+
additional_request_methods "OTHER"
|
8
|
+
|
9
|
+
request.path do |message|
|
10
|
+
%r{(?<path>/\w*)}.match(message)[:path]
|
11
|
+
end
|
12
|
+
|
13
|
+
request.query do |message|
|
14
|
+
/\?(?<query>.+)/.match(message) { |m| m[:query] }
|
15
|
+
end
|
16
|
+
|
17
|
+
request.http_method do |message|
|
18
|
+
case message.scan(/quack/).size
|
19
|
+
when 2 then "GET"
|
20
|
+
else "OTHER"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
require "toycol"
|
5
|
+
|
6
|
+
Toycol::Protocol.use(:duck)
|
7
|
+
|
8
|
+
# Duck protocol application
|
9
|
+
class App
|
10
|
+
def call(env)
|
11
|
+
case env["REQUEST_METHOD"]
|
12
|
+
when "GET"
|
13
|
+
case env["PATH_INFO"]
|
14
|
+
when "/posts"
|
15
|
+
return app_for_get_with_query if env["QUERY_STRING"] == "user_id=1"
|
16
|
+
|
17
|
+
app_for_get
|
18
|
+
when "/" then app_for_get_to_root
|
19
|
+
end
|
20
|
+
when "OTHER" then app_for_other
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def app_for_get_with_query
|
27
|
+
[
|
28
|
+
200,
|
29
|
+
{ "Content-Type" => "text/html" },
|
30
|
+
["quack quack, I am the No.1 duck\n"]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
def app_for_get
|
35
|
+
[
|
36
|
+
200,
|
37
|
+
{ "Content-Type" => "text/html" },
|
38
|
+
["quack quack, quack quack, quack, quack\n", "quack quack, I am the No.1 duck\n"]
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def app_for_get_to_root
|
43
|
+
[
|
44
|
+
200,
|
45
|
+
{ "Content-Type" => "text/html" },
|
46
|
+
["Hello, This app is running on Sample duck protocol.\n"]
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def app_for_other
|
51
|
+
[
|
52
|
+
600,
|
53
|
+
{ "Content-Type" => "text/html" },
|
54
|
+
["Sorry, this application is only for ducks...\n"]
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
run App.new
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Toycol::Protocol.define(:rubylike) do
|
2
|
+
custom_status_codes(
|
3
|
+
700 => "Hmm...",
|
4
|
+
)
|
5
|
+
|
6
|
+
additional_request_methods "OTHER"
|
7
|
+
|
8
|
+
request.path do |message|
|
9
|
+
/['"](?<path>.+)['"]/.match(message)[:path]
|
10
|
+
end
|
11
|
+
|
12
|
+
request.http_method do |message|
|
13
|
+
case /\.(?<method>[A-z]+)/.match(message)&.captures&.first
|
14
|
+
when "get" then "GET"
|
15
|
+
else "OTHER"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
require "toycol"
|
5
|
+
|
6
|
+
Toycol::Protocol.use(:rubylike)
|
7
|
+
|
8
|
+
class App
|
9
|
+
def call(env)
|
10
|
+
case env["REQUEST_METHOD"]
|
11
|
+
when "GET"
|
12
|
+
[
|
13
|
+
200,
|
14
|
+
{ "Content-Type" => "text/html" },
|
15
|
+
["I love ruby!\n", "I love RubyKaigi!\n"]
|
16
|
+
]
|
17
|
+
when "OTHER"
|
18
|
+
[
|
19
|
+
700,
|
20
|
+
{ "Content-Type" => "text/html" },
|
21
|
+
["Sorry, but I'd like you to speak more like a Ruby programmer...\n"]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
run App.new
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module SafeRuby
|
2
|
+
PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[A-z]+)/
|
3
|
+
QUERY_REGEX = /query?.*\{(?<query>.*)\}/
|
4
|
+
INPUT_REGEX = /input?.*\{(?<input>.*)\}/
|
5
|
+
end
|
6
|
+
|
7
|
+
Toycol::Protocol.define(:safe_ruby) do |message|
|
8
|
+
using Module.new {
|
9
|
+
refine String do
|
10
|
+
# Ex. '/posts'.get
|
11
|
+
# Ex. (with query string) '/posts'.get(query: { user_id: 2 })
|
12
|
+
def get(options = {})
|
13
|
+
Toycol::Protocol.request.query { options[:query] } if options[:query]
|
14
|
+
Toycol::Protocol.request.http_method { "GET" }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Ex. '/posts'.post(input: { user_id: 1, body: 'This is a post request' })
|
18
|
+
def post(options = {})
|
19
|
+
Toycol::Protocol.request.input { options[:input] } if options[:input]
|
20
|
+
Toycol::Protocol.request.http_method { "POST" }
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_as_queries
|
24
|
+
split(",").map { |str| str.scan(/\w+/).join("=") }
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_as_inputs
|
28
|
+
split(",").map { |str| str.split(":").map { |s| s.strip! && s.gsub(/['"]/, "") }.join("=") }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
33
|
+
path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }
|
34
|
+
query = SafeRuby::QUERY_REGEX.match(message) { |m| m[:query].parse_as_queries }&.join("&")
|
35
|
+
input = SafeRuby::INPUT_REGEX.match(message) { |m| m[:input].parse_as_inputs }&.join("&")
|
36
|
+
args = {}
|
37
|
+
|
38
|
+
request.path { path }
|
39
|
+
|
40
|
+
%i[query input].each do |k|
|
41
|
+
if v = binding.local_variable_get(k)
|
42
|
+
args[*k] = v
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
request_path.public_send(method, args)
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
require "toycol"
|
5
|
+
|
6
|
+
Toycol::Protocol.use(:safe_ruby)
|
7
|
+
|
8
|
+
class App
|
9
|
+
def call(env)
|
10
|
+
case env["REQUEST_METHOD"]
|
11
|
+
when "GET"
|
12
|
+
case env["PATH_INFO"]
|
13
|
+
when "/posts"
|
14
|
+
return app_for_get_with_query if env["QUERY_STRING"] == "user_id=2"
|
15
|
+
|
16
|
+
app_for_get
|
17
|
+
end
|
18
|
+
when "POST"
|
19
|
+
input = env["rack.input"].gets
|
20
|
+
created = input.split("&").map { |str| str.split("=") }.to_h
|
21
|
+
|
22
|
+
app_for_post(user_id: created["user_id"], body: created["body"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def app_for_get_with_query
|
29
|
+
[
|
30
|
+
200,
|
31
|
+
{ "Content-Type" => "text/html" },
|
32
|
+
["User<2> I love RubyKaigi!\n"]
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
def app_for_get
|
37
|
+
[
|
38
|
+
200,
|
39
|
+
{ "Content-Type" => "text/html" },
|
40
|
+
["User<1> I love Ruby!\n", "User<2> I love RubyKaigi!\n"]
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
def app_for_post(user_id:, body:)
|
45
|
+
[
|
46
|
+
201,
|
47
|
+
{ "Content-Type" => "text/html", "Location" => "/posts" },
|
48
|
+
["User<1> I love Ruby!\n",
|
49
|
+
"User<2> I love RubyKaigi!\n",
|
50
|
+
"User<#{user_id}> #{body}\n"]
|
51
|
+
]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
run App.new
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module SafeRubyWithSinatra
|
2
|
+
PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[A-z]+)/
|
3
|
+
QUERY_REGEX = /query?.*\{(?<query>.*)\}/
|
4
|
+
INPUT_REGEX = /input?.*\{(?<input>.*)\}/
|
5
|
+
end
|
6
|
+
|
7
|
+
Toycol::Protocol.define(:safe_ruby_with_sinatra) do |message|
|
8
|
+
using Module.new {
|
9
|
+
refine String do
|
10
|
+
def get(options = {})
|
11
|
+
Toycol::Protocol.request.query { options[:query] } if options[:query]
|
12
|
+
Toycol::Protocol.request.http_method { "GET" }
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(options = {})
|
16
|
+
Toycol::Protocol.request.input { options[:input] } if options[:input]
|
17
|
+
Toycol::Protocol.request.http_method { "POST" }
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_as_queries
|
21
|
+
split(",").map { |str| str.scan(/\w+/).join("=") }
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_as_inputs
|
25
|
+
split(",").map { |str| str.split(":").map { |s| s.strip! && s.gsub(/['"]/, "") }.join("=") }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
}
|
29
|
+
|
30
|
+
path, method = SafeRubyWithSinatra::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }
|
31
|
+
query = SafeRubyWithSinatra::QUERY_REGEX.match(message) { |m| m[:query].parse_as_queries }&.join("&")
|
32
|
+
input = SafeRubyWithSinatra::INPUT_REGEX.match(message) { |m| m[:input].parse_as_inputs }&.join("&")
|
33
|
+
args = {}
|
34
|
+
|
35
|
+
request.path { path }
|
36
|
+
|
37
|
+
%i[query input].each do |k|
|
38
|
+
if v = binding.local_variable_get(k)
|
39
|
+
args[*k] = v
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
request_path.public_send(method, args)
|
44
|
+
end
|