toycol 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cfdb9a6d989ee0d0939ff53ded3c9603e08326194e6d8546e09ed0e292557022
4
- data.tar.gz: d9b9779d6c2ca6c01710c6ecd1758384654aac1835ea343345ebc68a155ea8cc
3
+ metadata.gz: 94c69c1ed2726e69431304ff3c4fb727581b7893160d5dbe085731f4d85dfce2
4
+ data.tar.gz: 94fc2c13fbd5149a47669a521dfa40377e1edf890a92b33468bf534c6d34ed45
5
5
  SHA512:
6
- metadata.gz: 59d599b9b4c781428193c5574d5c093492875046d57d842ca120195a548c752ab343dee372e6c2042cf82009bb7066978c520f099ba3196d0a788fe60248f8e7
7
- data.tar.gz: 02033640bf6077a6125578bc6ec453c536639eb8882adbef3fd6eec6f0a2ffbedfe32dc9ef7dbb7deda6d85fafd25cdb006a9baf8e57ac4f4d87289fc38638f5
6
+ metadata.gz: c5c3d01ea2c3b41491e3d365b781d92677ef027ce9fefa1d45b3ab360e947416035c39d5c9d32dd29ae01c309dfa27338097e5a63dfcd248aa9647b45784bb28
7
+ data.tar.gz: d09afa0497d2b40fcc0b404a19ceee343b9f132a434cbf80feef1cac3bbef060397bccc15503beb507be6e0b03d33b6d5f5cdbe96c5499a093b3a8a27dbd8f24
@@ -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.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: "[FEATURE]"
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Is your feature request related to a problem? Please describe.
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ ## Describe the solution you'd like
14
+ A clear and concise description of what you want to happen.
15
+
16
+ ## Describe alternatives you've considered
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ ## Additional context
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "toycol", path: "../../"
@@ -0,0 +1,13 @@
1
+ Toycol::Protocol.define do
2
+ request.path do |message|
3
+ %r{(?<path>\/\w*)}.match(message)[:path]
4
+ end
5
+
6
+ request.query do |message|
7
+ %r{\?(?<query>.+)}.match(message) { |m| m[:query] }
8
+ end
9
+
10
+ request.http_method do |message|
11
+ "GET"
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "toycol"
5
+
6
+ Toycol::Protocol.use
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
+ ["This app has no protocol name\n"]
16
+ ]
17
+ end
18
+ end
19
+ end
20
+
21
+ run App.new
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  Toycol::Protocol.define(:duck) do
4
2
  custom_status_codes(
5
3
  600 => "I'm afraid you are not a duck..."
@@ -7,11 +5,11 @@ Toycol::Protocol.define(:duck) do
7
5
  additional_request_methods "OTHER"
8
6
 
9
7
  request.path do |message|
10
- %r{(?<path>/\w*)}.match(message)[:path]
8
+ %r{(?<path>\/\w*)}.match(message)[:path]
11
9
  end
12
10
 
13
11
  request.query do |message|
14
- /\?(?<query>.+)/.match(message) { |m| m[:query] }
12
+ %r{\<3(?<query>.+)}.match(message) { |m| m[:query] }
15
13
  end
16
14
 
17
15
  request.http_method do |message|
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack"
4
3
  require "rack/handler"
5
4
 
6
5
  module Rack
7
6
  module Handler
8
7
  class Toycol
8
+ extend ::Toycol::Helper
9
+
9
10
  class << self
10
11
  attr_writer :preferred_background_server, :host, :port
11
12
 
@@ -27,18 +28,20 @@ module Rack
27
28
  def select_background_server
28
29
  case @preferred_background_server
29
30
  when "puma"
30
- return "puma" if puma_requireable?
31
+ return "puma" if try_require_puma_handler
31
32
 
32
- puts "Puma is not installed in your environment."
33
- raise LoadError
33
+ raise LoadError, "Puma is not installed in your environment."
34
34
  when nil
35
- puma_requireable? ? "puma" : "build_in"
35
+ try_require_puma_handler ? "puma" : "builtin"
36
36
  else
37
- "build_in"
37
+ "builtin"
38
38
  end
39
+ rescue LoadError
40
+ Process.kill(:INT, Process.ppid)
41
+ abort
39
42
  end
40
43
 
41
- def puma_requireable?
44
+ def try_require_puma_handler
42
45
  require "rack/handler/puma"
43
46
  true
44
47
  rescue LoadError
@@ -48,10 +51,10 @@ module Rack
48
51
  def run_background_server
49
52
  case select_background_server
50
53
  when "puma"
51
- puts "Toycol starts Puma in single mode, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
54
+ logger "Start Puma in single mode, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
52
55
  Rack::Handler::Puma.run(@app, **{ Host: ::Toycol::UNIX_SOCKET_PATH, Silent: true })
53
56
  else
54
- puts "Toycol starts build-in server, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
57
+ logger "Start built-in server, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
55
58
  ::Toycol::Server.run(@app, **{ Path: ::Toycol::UNIX_SOCKET_PATH, Port: @port })
56
59
  end
57
60
  end
data/lib/toycol.rb CHANGED
@@ -1,27 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "optparse"
5
+ require "rack"
6
+ require "socket"
7
+ require "stringio"
4
8
 
5
9
  require_relative "toycol/const"
6
10
  require_relative "toycol/helper"
7
11
  require_relative "toycol/protocol"
8
12
  require_relative "toycol/proxy"
9
13
  require_relative "toycol/server"
10
- require_relative "rack/handler/toycol"
11
-
12
- Dir["#{FileUtils.pwd}/Protocolfile*"].sort.each { |f| load f }
13
-
14
+ require_relative "toycol/client"
15
+ require_relative "toycol/template_generator"
14
16
  require_relative "toycol/command"
15
17
  require_relative "toycol/version"
16
18
 
19
+ require_relative "rack/handler/toycol"
20
+
17
21
  module Toycol
18
22
  class Error < StandardError; end
19
23
 
20
- class UnauthorizedMethodError < Error; end
24
+ class UnauthorizeError < Error; end
21
25
 
22
- class UnauthorizedRequestError < Error; end
26
+ class UndefinementError < Error; end
23
27
 
24
- class UndefinedRequestMethodError < Error; end
28
+ class DuplicateProtocolError < Error; end
25
29
 
26
- class UnknownStatusCodeError < Error; end
30
+ class HTTPError < Error; end
27
31
  end
32
+
33
+ Dir["#{FileUtils.pwd}/Protocolfile*"].sort.each { |f| load f }
data/lib/toycol/client.rb CHANGED
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "socket"
4
-
5
3
  module Toycol
6
4
  class Client
5
+ extend Helper
6
+
7
7
  @port = 9292
8
+ @host = "localhost"
8
9
  CHUNK_SIZE = 1024 * 16
9
10
 
10
11
  class << self
11
- attr_writer :port
12
+ attr_writer :port, :host
12
13
 
13
14
  def execute!(request_message, &block)
14
- socket = TCPSocket.new("localhost", @port)
15
+ socket = TCPSocket.new(@host, @port)
15
16
  socket.write(request_message)
16
- puts "[Toycol] Sent request message: #{request_message}\n---"
17
+ logger "Sent request message: #{request_message}\n---"
17
18
 
18
19
  response_message = []
19
20
  response_message << socket.readpartial(CHUNK_SIZE) until socket.eof?
@@ -29,7 +30,7 @@ module Toycol
29
30
 
30
31
  def default_proc
31
32
  proc do |message|
32
- puts "[Toycol] Received response message:\n\n"
33
+ logger "Received response message:\n\n"
33
34
  puts message
34
35
  end
35
36
  end
@@ -1,27 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "optparse"
4
- require_relative "./client"
5
-
6
3
  module Toycol
7
4
  class Command
8
5
  class Options
6
+ @options = {}
7
+
9
8
  class << self
10
9
  def parse!(argv)
11
- options = {}
12
10
  option_parser = create_option_parser
13
11
  sub_command_option_parser = create_sub_command_option_parser
14
12
 
15
13
  begin
16
14
  option_parser.order!(argv)
17
- options[:command] = argv.shift
18
- options[:request_message] = argv.shift if %w[client c].include?(options[:command]) && argv.first != "-h"
19
- sub_command_option_parser[options[:command]].parse!(argv)
15
+ @options[:command] = argv.shift
16
+ @options[:request_message] = argv.shift if request_message?(argv.first)
17
+ @options[:protocol_name] = argv.shift if protocol_name?(argv.first)
18
+ sub_command_option_parser[@options[:command]].parse!(argv)
20
19
  rescue OptionParser::MissingArgument, OptionParser::InvalidOption, ArgumentError => e
21
20
  abort e.message
22
21
  end
23
22
 
24
- options
23
+ @options
25
24
  end
26
25
 
27
26
  def create_option_parser
@@ -45,26 +44,46 @@ module Toycol
45
44
 
46
45
  def create_sub_command_option_parser
47
46
  sub_command_parser = Hash.new { |_k, v| raise ArgumentError, "'#{v}' is not sub command" }
48
- sub_command_parser["client"] = client_option_parser
49
- sub_command_parser["c"] = client_option_parser
50
- sub_command_parser["server"] = server_option_parser
51
- sub_command_parser["s"] = server_option_parser
47
+ sub_command_parser["client"] = client_option_parser
48
+ sub_command_parser["c"] = client_option_parser
49
+ sub_command_parser["server"] = server_option_parser
50
+ sub_command_parser["s"] = server_option_parser
51
+ sub_command_parser["generate"] = generator_option_parser
52
+ sub_command_parser["g"] = generator_option_parser
52
53
  sub_command_parser
53
54
  end
54
55
 
55
56
  private
56
57
 
58
+ def request_message?(arg)
59
+ %w[client c].include?(@options[:command]) \
60
+ && arg != "-p" \
61
+ && arg != "-h"
62
+ end
63
+
64
+ def protocol_name?(arg)
65
+ %w[geberate g].include?(@options[:command]) \
66
+ && arg != "-t" \
67
+ && arg != "-h"
68
+ end
69
+
57
70
  def sub_command_summaries
58
71
  [
59
72
  { name: "client REQUEST_MESSAGE -p PORT", summary: "Send request message to server" },
60
- { name: "server -u SERVER_NAME", summary: "Start proxy and background server" }
73
+ { name: "server -u SERVER_NAME", summary: "Start proxy and background server" },
74
+ { name: "generate NAME -t TYPE", summary: "Generate new protocol or Rack app" }
61
75
  ]
62
76
  end
63
77
 
64
78
  def client_option_parser
65
79
  OptionParser.new do |opt|
66
- opt.on("-p PORT_NUMBER", "--port PORT_NUMBER", "listen on PORT (default: 9292)") do |port|
67
- ::Toycol::Client.port = port
80
+ opt.banner = "Usage: #{opt.program_name} client [-h|--help] REQUEST_MESSAGE [arg...]"
81
+ opt.on("-o HOST", "--host HOST", "connect to HOST (default: localhost)") do |host|
82
+ Client.host = host
83
+ end
84
+
85
+ opt.on("-p PORT_NUMBER", "--port PORT_NUMBER", "connect to PORT (default: 9292)") do |port|
86
+ Client.port = port
68
87
  end
69
88
 
70
89
  opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
@@ -73,6 +92,7 @@ module Toycol
73
92
 
74
93
  def server_option_parser
75
94
  OptionParser.new do |opt|
95
+ opt.banner = "Usage: #{opt.program_name} server [-h|--help] APPLICATION_PATH [arg...]"
76
96
  opt.on("-o HOST", "--host HOST", "bind to HOST (default: localhost)") do |host|
77
97
  ::Rack::Handler::Toycol.host = host
78
98
  end
@@ -81,7 +101,7 @@ module Toycol
81
101
  ::Rack::Handler::Toycol.port = port
82
102
  end
83
103
 
84
- opt.on("-u SERVER_NAME", "--use SERVER_NAME", "switch using SERVER(puma/build_in)") do |server_name|
104
+ opt.on("-u SERVER_NAME", "--use SERVER_NAME", "switch using SERVER(puma/builtin)") do |server_name|
85
105
  ::Rack::Handler::Toycol.preferred_background_server = server_name
86
106
  end
87
107
 
@@ -89,16 +109,14 @@ module Toycol
89
109
  end
90
110
  end
91
111
 
92
- def client_command_help_messages
93
- [
94
- { name: "client -p=PORT_NUMBER", summary: "Send request to server" }
95
- ]
96
- end
112
+ def generator_option_parser
113
+ OptionParser.new do |opt|
114
+ opt.on("-t TYPE", "--type TYPE", "generate TYPE of template (default: :all)") do |type|
115
+ @options[:template_type] = type
116
+ end
97
117
 
98
- def server_command_help_messages
99
- [
100
- { name: "server -u=SERVER_NAME", summary: "Start proxy & background server" }
101
- ]
118
+ opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
119
+ end
102
120
  end
103
121
 
104
122
  def help_command(parser)
@@ -122,10 +140,13 @@ module Toycol
122
140
 
123
141
  case command
124
142
  when "client", "c"
125
- ::Toycol::Client.execute!(options[:request_message])
143
+ Client.execute!(options[:request_message])
126
144
  when "server", "s"
127
145
  ARGV.push("-q", "-s", "toycol")
128
146
  Rack::Server.start
147
+ when "generate", "g"
148
+ type = options[:template_type] || "all"
149
+ TemplateGenerator.generate!(type: type, name: options[:protocol_name])
129
150
  end
130
151
  end
131
152
  end
data/lib/toycol/helper.rb CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Toycol
4
4
  module Helper
5
+ def logger(message)
6
+ puts "[Toycol] #{message}"
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def safe_execution!(&block)
@@ -10,8 +14,8 @@ module Toycol
10
14
 
11
15
  def safe_executionable_tp
12
16
  @safe_executionable_tp ||= TracePoint.new(:script_compiled) do |tp|
13
- if tp.binding.receiver == Toycol::Protocol && tp.method_id.to_s.match?(unauthorized_methods_regex)
14
- raise Toycol::UnauthorizedMethodError, <<~ERROR
17
+ if tp.binding.receiver == Protocol && tp.method_id.to_s.match?(unauthorized_methods_regex)
18
+ raise UnauthorizeError, <<~ERROR
15
19
  - Unauthorized method was called!
16
20
  You can't use methods that may cause injections in your protocol.
17
21
  Ex. Kernel.#eval, Kernel.#exec, Kernel.#require and so on.
@@ -5,23 +5,30 @@ module Toycol
5
5
  class Protocol
6
6
  @definements = {}
7
7
  @protocol_name = nil
8
- @http_status_codes = Toycol::DEFAULT_HTTP_STATUS_CODES.dup
9
- @http_request_methods = Toycol::DEFAULT_HTTP_REQUEST_METHODS.dup
8
+ @http_status_codes = DEFAULT_HTTP_STATUS_CODES.dup
9
+ @http_request_methods = DEFAULT_HTTP_REQUEST_METHODS.dup
10
10
  @custom_status_codes = nil
11
11
  @additional_request_methods = nil
12
12
 
13
13
  class << self
14
- # For protocol definition
15
- def define(protocol_name = nil, &block)
14
+ attr_reader :protocol_name
15
+
16
+ # For Protocolfile to define new protocol
17
+ def define(protocol_name = :default, &block)
18
+ if @definements[protocol_name]
19
+ raise DuplicateProtocolError,
20
+ "#{protocol_name || "Anonymous"} protocol has already been defined"
21
+ end
22
+
16
23
  @definements[protocol_name] = block
17
24
  end
18
25
 
19
- # For application which use the protocol
20
- def use(protocol_name)
26
+ # For application to select which protocol to use
27
+ def use(protocol_name = :default)
21
28
  @protocol_name = protocol_name
22
29
  end
23
30
 
24
- # For server which use the protocol
31
+ # For proxy server to interpret protocol definitions and parse messages
25
32
  def run!(message)
26
33
  @request_message = message.chomp
27
34
 
@@ -67,52 +74,54 @@ module Toycol
67
74
  end
68
75
  end
69
76
 
70
- # For server: Get the request path
77
+ # For proxy server: Fetch the request path
71
78
  def request_path
72
79
  request_path = request.instance_variable_get("@path").call(request_message)
73
80
 
74
- raise UnauthorizedRequestError, "This request path is too long" if request_path.size >= 2048
75
-
76
- if request_path.scan(%r{[/\w\d\-_]}).size < request_path.size
77
- raise UnauthorizedRequestError,
81
+ if request_path.size >= 2048
82
+ raise UnauthorizeError,
83
+ "This request path is too long"
84
+ elsif request_path.scan(%r{[/\w\d\-_]}).size < request_path.size
85
+ raise UnauthorizeError,
78
86
  "This request path contains unauthorized character"
79
87
  end
80
88
 
81
89
  request_path
82
90
  end
83
91
 
84
- # For server: Get the request method
92
+ # For proxy server: Fetch the request method
85
93
  def request_method
86
94
  @http_request_methods.concat @additional_request_methods if @additional_request_methods
87
95
  request_method = request.instance_variable_get("@http_method").call(request_message)
88
96
 
89
97
  unless @http_request_methods.include? request_method
90
- raise UndefinedRequestMethodError, "This request method is undefined"
98
+ raise UndefinementError,
99
+ "This request method is undefined"
91
100
  end
92
101
 
93
102
  request_method
94
103
  end
95
104
 
96
- # For server: Get the query string
105
+ # For proxy server: Fetch the query string
97
106
  def query
98
107
  return unless (parse_query_block = request.instance_variable_get("@query"))
99
108
 
100
109
  parse_query_block.call(request_message)
101
110
  end
102
111
 
103
- # For server: Get the input body
112
+ # For proxy server: Fetch the input body
104
113
  def input
105
114
  return unless (parsed_input_block = request.instance_variable_get("@input"))
106
115
 
107
116
  parsed_input_block.call(request_message)
108
117
  end
109
118
 
110
- # For server: Get the message of status code
119
+ # For proxy server: fetch the message of status code
111
120
  def status_message(status)
112
121
  @http_status_codes.merge!(@custom_status_codes) if @custom_status_codes
113
122
 
114
123
  unless (message = @http_status_codes[status])
115
- raise UnknownStatusCodeError, "Application returns unknown status code"
124
+ raise HTTPError, "Application returns unknown status code"
116
125
  end
117
126
 
118
127
  message
data/lib/toycol/proxy.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "socket"
4
-
5
3
  module Toycol
6
4
  class Proxy
7
5
  include Helper
@@ -13,15 +11,15 @@ module Toycol
13
11
  @path = nil
14
12
  @query = nil
15
13
  @input = nil
16
- @protocol = ::Toycol::Protocol
14
+ @protocol = Protocol
17
15
  @proxy = TCPServer.new(@host, @port)
18
16
  end
19
17
 
20
18
  CHUNK_SIZE = 1024 * 16
21
19
 
22
20
  def start
23
- puts <<~MESSAGE
24
- Toycol is running on #{@host}:#{@port}
21
+ logger <<~MESSAGE
22
+ Start proxy server on #{@protocol.protocol_name} protocol, listening on #{@host}:#{@port}
25
23
  => Use Ctrl-C to stop
26
24
  MESSAGE
27
25
 
@@ -33,13 +31,13 @@ module Toycol
33
31
  while !@client.closed? && !@client.eof?
34
32
  begin
35
33
  request = @client.readpartial(CHUNK_SIZE)
36
- puts "[Toycol] Received message: #{request.inspect.chomp}"
34
+ logger "Received message: #{request.inspect.chomp}"
37
35
 
38
36
  safe_execution! { @protocol.run!(request) }
39
37
  assign_parsed_attributes!
40
38
 
41
39
  http_request_message = build_http_request_message
42
- puts "[Toycol] Message has been translated to HTTP request message: #{http_request_message.inspect}"
40
+ logger "Message has been translated to HTTP request message: #{http_request_message.inspect}"
43
41
  transfer_to_server(http_request_message)
44
42
  rescue StandardError => e
45
43
  puts "#{e.class} #{e.message} - closing socket."
@@ -82,15 +80,15 @@ module Toycol
82
80
  end
83
81
 
84
82
  def transfer_to_server(request_message)
85
- UNIXSocket.open(Toycol::UNIX_SOCKET_PATH) do |server|
83
+ UNIXSocket.open(UNIX_SOCKET_PATH) do |server|
86
84
  server.write request_message
87
85
  server.close_write
88
- puts "[Toycol] Successed to Send HTTP request message to server"
86
+ logger "Successed to Send HTTP request message to server"
89
87
 
90
88
  response_message = []
91
89
  response_message << server.readpartial(CHUNK_SIZE) until server.eof?
92
90
  response_message = response_message.join
93
- puts "[Toycol] Received response message from server: #{response_message.lines.first}"
91
+ logger "Received response message from server: #{response_message.lines.first}"
94
92
 
95
93
  response_line = response_message.lines.first
96
94
  status_number = response_line[9..11]
@@ -98,18 +96,18 @@ module Toycol
98
96
 
99
97
  if (custom_message = @protocol.status_message(status_number.to_i)) != status_message
100
98
  response_message = response_message.sub(status_message, custom_message)
101
- puts "[Toycol] Status message has been translated to custom status message: #{custom_message}"
99
+ logger "Status message has been translated to custom status message: #{custom_message}"
102
100
  end
103
101
 
104
102
  @client.write response_message
105
103
  @client.close_write
106
- puts "[Toycol] Finished to response to client"
104
+ logger "Finished to response to client"
107
105
  server.close
108
106
  end
109
107
  end
110
108
 
111
109
  def shutdown
112
- puts "[Toycol] Catched SIGINT -> Stop to server"
110
+ logger "Caught SIGINT -> Stop to server"
113
111
  exit
114
112
  end
115
113
  end
data/lib/toycol/server.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "stringio"
4
-
5
3
  module Toycol
6
4
  class Server
7
5
  BACKLOG = 1024
@@ -50,19 +48,19 @@ module Toycol
50
48
 
51
49
  def default_env
52
50
  {
53
- ::Toycol::PATH_INFO => "",
54
- ::Toycol::QUERY_STRING => "",
55
- ::Toycol::REQUEST_METHOD => "",
56
- ::Toycol::SERVER_NAME => "toycol_server",
57
- ::Toycol::SERVER_PORT => @port.to_s,
58
- ::Toycol::CONTENT_LENGTH => "",
59
- ::Toycol::RACK_VERSION => Rack::VERSION,
60
- ::Toycol::RACK_INPUT => stringio(""),
61
- ::Toycol::RACK_ERRORS => $stderr,
62
- ::Toycol::RACK_MULTITHREAD => false,
63
- ::Toycol::RACK_MULTIPROCESS => false,
64
- ::Toycol::RACK_RUN_ONCE => false,
65
- ::Toycol::RACK_URL_SCHEME => "http"
51
+ PATH_INFO => "",
52
+ QUERY_STRING => "",
53
+ REQUEST_METHOD => "",
54
+ SERVER_NAME => "toycol_server",
55
+ SERVER_PORT => @port.to_s,
56
+ CONTENT_LENGTH => "0",
57
+ RACK_VERSION => Rack::VERSION,
58
+ RACK_INPUT => stringio(""),
59
+ RACK_ERRORS => $stderr,
60
+ RACK_MULTITHREAD => false,
61
+ RACK_MULTIPROCESS => false,
62
+ RACK_RUN_ONCE => false,
63
+ RACK_URL_SCHEME => "http"
66
64
  }
67
65
  end
68
66
 
@@ -71,7 +69,7 @@ module Toycol
71
69
  end
72
70
 
73
71
  def response_status_code
74
- "HTTP/1.1 #{@returned_status} #{::Toycol::DEFAULT_HTTP_STATUS_CODES[@returned_status.to_i] || "CUSTOM"}\r\n"
72
+ "HTTP/1.1 #{@returned_status} #{DEFAULT_HTTP_STATUS_CODES[@returned_status.to_i] || "CUSTOM"}\r\n"
75
73
  end
76
74
 
77
75
  def response_headers
@@ -111,10 +109,11 @@ module Toycol
111
109
  @env[REQUEST_METHOD] = request_method
112
110
  @env[PATH_INFO] = request_path
113
111
  @env[QUERY_STRING] = query_string || ""
112
+ @env[CONTENT_LENGTH]
114
113
 
115
114
  request_headers.each do |request_header|
116
115
  k, v = request_header.split(":").map(&:strip)
117
- @env[k.tr("-", "_").upcase] = v
116
+ @env[k.tr("-", "_").upcase.to_s] = v
118
117
  end
119
118
 
120
119
  @env[RACK_INPUT] = stringio(request_body)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toycol
4
+ class TemplateGenerator
5
+ include Helper
6
+
7
+ class << self
8
+ def generate!(type:, name:)
9
+ raise Error, "Unknown Type: This type of template can't be generated" unless valid? type
10
+
11
+ if type == "all"
12
+ new(name, "protocol").generate!
13
+ new(name, "app").generate!
14
+ else
15
+ new(name, type).generate!
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def valid?(type)
22
+ %w[all app protocol].include? type
23
+ end
24
+ end
25
+
26
+ def initialize(name, type)
27
+ @name = name
28
+ @type = type
29
+ end
30
+
31
+ def generate!
32
+ raise Error, "#{filename} already exists" unless Dir.glob(filename).empty?
33
+
34
+ File.open(filename, "w") { |f| f.print template_text_for_new }
35
+ logger "Generate #{filename} in #{FileUtils.pwd}"
36
+ end
37
+
38
+ private
39
+
40
+ def filename
41
+ @filename ||= case @type
42
+ when "protocol" then "Protocolfile#{@name ? ".#{@name}" : nil}"
43
+ when "app" then "config#{@name ? "_#{@name}" : nil}.ru"
44
+ end
45
+ end
46
+
47
+ def template_text_for_new
48
+ if @name
49
+ template_text.sub(":PROTOCOL_NAME", ":#{@name}")
50
+ else
51
+ template_text.sub("\(:PROTOCOL_NAME\)", "")
52
+ end
53
+ end
54
+
55
+ def template_text
56
+ case @type
57
+ when "protocol" then File.open("#{__dir__}/templates/protocol.txt", "r", &:read)
58
+ when "app" then File.open("#{__dir__}/templates/application.txt", "r", &:read)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ require "rack"
2
+ require "toycol"
3
+
4
+ Toycol::Protocol.use(:PROTOCOL_NAME)
5
+
6
+ class App
7
+ def call(env)
8
+ # Define your app on request method, request path, request query etc
9
+ # For example:
10
+ # case env["REQUEST_METHOD"]
11
+ # when "GET"
12
+ # [
13
+ # 200,
14
+ # { "Content-Type" => "text/html" },
15
+ # ["Hello, This app is running by new protocol."]
16
+ # ]
17
+ # when "OTHER"
18
+ # [
19
+ # 600,
20
+ # { "Content-Type" => "text/html" },
21
+ # ["This is response message for additional request method"]
22
+ # ]
23
+ # end
24
+ end
25
+ end
26
+
27
+ run App.new
@@ -0,0 +1,39 @@
1
+ Toycol::Protocol.define(:PROTOCOL_NAME) do
2
+ # For example
3
+ # client would send:
4
+ # quack, quack /posts<3user_id=1
5
+ # server would interpret client message:
6
+ # GET /posts?user_id=1
7
+
8
+
9
+ # [OPTIONAL] You can define your additional request methods:
10
+ # For example:
11
+ # additional_request_methods "OTHER"
12
+
13
+ # [OPTIONAL] You can define your own response status code:
14
+ # For example:
15
+ # custom_status_codes(
16
+ # 600 => "I'm afraid you are not a duck..."
17
+ # )
18
+
19
+ # [REQUIRED] Define how you parse request path from request message
20
+ request.path do |message|
21
+ # For example:
22
+ # %r{(?<path>\/\w*)}.match(message)[:path]
23
+ end
24
+
25
+ # [REQUIRED] Define how you parse query from request message
26
+ request.query do |message|
27
+ # For example:
28
+ # %r{\<3(?<query>.+)}.match(message) { |m| m[:query] }
29
+ end
30
+
31
+ # [REQUIRED] Define how you parse query from request message
32
+ request.http_method do |message|
33
+ # For example:
34
+ # case message.scan(/quack/).size
35
+ # when 2 then "GET"
36
+ # else "OTHER"
37
+ # end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toycol
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toycol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Misaki Shioi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-23 00:00:00.000000000 Z
11
+ date: 2021-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -32,6 +32,8 @@ executables:
32
32
  extensions: []
33
33
  extra_rdoc_files: []
34
34
  files:
35
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
36
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
35
37
  - ".github/dependabot.yml"
36
38
  - ".github/workflows/main.yml"
37
39
  - ".gitignore"
@@ -44,6 +46,9 @@ files:
44
46
  - Rakefile
45
47
  - bin/console
46
48
  - bin/setup
49
+ - examples/anonymous/Gemfile
50
+ - examples/anonymous/Protocolfile
51
+ - examples/anonymous/config.ru
47
52
  - examples/duck/Gemfile
48
53
  - examples/duck/Protocolfile.duck
49
54
  - examples/duck/config_duck.ru
@@ -71,6 +76,9 @@ files:
71
76
  - lib/toycol/protocol.rb
72
77
  - lib/toycol/proxy.rb
73
78
  - lib/toycol/server.rb
79
+ - lib/toycol/template_generator.rb
80
+ - lib/toycol/templates/application.txt
81
+ - lib/toycol/templates/protocol.txt
74
82
  - lib/toycol/version.rb
75
83
  - toycol.gemspec
76
84
  homepage: https://github.com/shioimm/toycol