toycol 0.0.1 → 0.2.2
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.
- 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
 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "sinatra/base"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "sinatra/reloader"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "toycol"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "post"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Toycol::Protocol.use(:safe_ruby_with_sinatra)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            class App < Sinatra::Base
         
     | 
| 
      
 11 
     | 
    
         
            +
              set :server, :toycol
         
     | 
| 
      
 12 
     | 
    
         
            +
              set :port, 9292
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              get "/posts" do
         
     | 
| 
      
 15 
     | 
    
         
            +
                @posts = params[:user_id] ? Post.where(user_id: params[:user_id]) : Post.all
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                erb :index
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              post "/posts" do
         
     | 
| 
      
 21 
     | 
    
         
            +
                Post.new(user_id: params[:user_id], body: params[:body])
         
     | 
| 
      
 22 
     | 
    
         
            +
                @posts = Post.all
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                erb :index
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              run! if app_file == $PROGRAM_NAME
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Post
         
     | 
| 
      
 4 
     | 
    
         
            +
              @posts ||= []
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                def all
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @posts
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def where(user_id: nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @posts.select do |post|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    post.user_id == Integer(user_id) if user_id
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def insert(record)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @posts << record
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              attr_reader :user_id, :body
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def initialize(user_id:, body:)
         
     | 
| 
      
 25 
     | 
    
         
            +
                @user_id = Integer(user_id)
         
     | 
| 
      
 26 
     | 
    
         
            +
                @body    = body
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                self.class.insert self
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            # Initial records
         
     | 
| 
      
 33 
     | 
    
         
            +
            Post.new(user_id: 1, body: "I love Ruby!")
         
     | 
| 
      
 34 
     | 
    
         
            +
            Post.new(user_id: 2, body: "I love RubyKaigi!")
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <html>
         
     | 
| 
      
 2 
     | 
    
         
            +
              <head>
         
     | 
| 
      
 3 
     | 
    
         
            +
                <meta charset="UTF-8">
         
     | 
| 
      
 4 
     | 
    
         
            +
              </head>
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              <body>
         
     | 
| 
      
 7 
     | 
    
         
            +
                <h1>This app is running on Safe Ruby protocol</h1>
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 10 
     | 
    
         
            +
                  <ul>
         
     | 
| 
      
 11 
     | 
    
         
            +
                    <% @posts.each do |post| %>
         
     | 
| 
      
 12 
     | 
    
         
            +
                      <li><%= "User<#{post.user_id}> #{post.body}" %></li>
         
     | 
| 
      
 13 
     | 
    
         
            +
                    <% end %>
         
     | 
| 
      
 14 
     | 
    
         
            +
                  </ul>
         
     | 
| 
      
 15 
     | 
    
         
            +
                </div>
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                <div>
         
     | 
| 
      
 18 
     | 
    
         
            +
                  <form action='/posts' method='post'>
         
     | 
| 
      
 19 
     | 
    
         
            +
                    <div>
         
     | 
| 
      
 20 
     | 
    
         
            +
                      <label>User ID</label>
         
     | 
| 
      
 21 
     | 
    
         
            +
                      <input type="number" name="user_id">
         
     | 
| 
      
 22 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 23 
     | 
    
         
            +
                    <div>
         
     | 
| 
      
 24 
     | 
    
         
            +
                      <label>Content</label>
         
     | 
| 
      
 25 
     | 
    
         
            +
                      <input type="text" name="body">
         
     | 
| 
      
 26 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 27 
     | 
    
         
            +
                    <div>
         
     | 
| 
      
 28 
     | 
    
         
            +
                      <input type="submit" value="Post">
         
     | 
| 
      
 29 
     | 
    
         
            +
                    </div>
         
     | 
| 
      
 30 
     | 
    
         
            +
                  </form>
         
     | 
| 
      
 31 
     | 
    
         
            +
              </body>
         
     | 
| 
      
 32 
     | 
    
         
            +
            </html>
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Toycol::Protocol.define(:ruby) do |message|
         
     | 
| 
      
 2 
     | 
    
         
            +
              using Module.new {
         
     | 
| 
      
 3 
     | 
    
         
            +
                refine String do
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def get
         
     | 
| 
      
 5 
     | 
    
         
            +
                    Toycol::Protocol.request.path do |message|
         
     | 
| 
      
 6 
     | 
    
         
            +
                      /['"](?<path>.+)['"]/.match(message)[:path]
         
     | 
| 
      
 7 
     | 
    
         
            +
                    end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    Toycol::Protocol.request.http_method { |_| "GET" }
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              }
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              instance_eval message
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "rack"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "toycol"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Toycol::Protocol.use(: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 
     | 
    
         
            +
                    [
         
     | 
| 
      
 15 
     | 
    
         
            +
                      200,
         
     | 
| 
      
 16 
     | 
    
         
            +
                      { "Content-Type" => "text/html" },
         
     | 
| 
      
 17 
     | 
    
         
            +
                      ["I love Ruby!\n", "I've successfully accessed using instance_eval!\n"]
         
     | 
| 
      
 18 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            run App.new
         
     | 
    
        data/exe/toycol
    ADDED
    
    
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "rack"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "rack/handler"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Rack
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Handler
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Toycol
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 10 
     | 
    
         
            +
                    attr_writer :preferred_background_server, :host, :port
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    def run(app, _ = {})
         
     | 
| 
      
 13 
     | 
    
         
            +
                      @app = app
         
     | 
| 
      
 14 
     | 
    
         
            +
                      @host ||= ::Toycol::DEFAULT_HOST
         
     | 
| 
      
 15 
     | 
    
         
            +
                      @port ||= "9292"
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                      if (child_pid = fork)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        ::Toycol::Proxy.new(@host, @port).start
         
     | 
| 
      
 19 
     | 
    
         
            +
                        Process.waitpid(child_pid)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      else
         
     | 
| 
      
 21 
     | 
    
         
            +
                        run_background_server
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    private
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def select_background_server
         
     | 
| 
      
 28 
     | 
    
         
            +
                      case @preferred_background_server
         
     | 
| 
      
 29 
     | 
    
         
            +
                      when "puma"
         
     | 
| 
      
 30 
     | 
    
         
            +
                        return "puma" if puma_requireable?
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                        raise LoadError, "Puma is not installed in your environment."
         
     | 
| 
      
 33 
     | 
    
         
            +
                      when nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                        puma_requireable? ? "puma" : "build_in"
         
     | 
| 
      
 35 
     | 
    
         
            +
                      else
         
     | 
| 
      
 36 
     | 
    
         
            +
                        "build_in"
         
     | 
| 
      
 37 
     | 
    
         
            +
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    rescue LoadError
         
     | 
| 
      
 39 
     | 
    
         
            +
                      Process.kill(:INT, Process.ppid)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      abort
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    def puma_requireable?
         
     | 
| 
      
 44 
     | 
    
         
            +
                      require "rack/handler/puma"
         
     | 
| 
      
 45 
     | 
    
         
            +
                      true
         
     | 
| 
      
 46 
     | 
    
         
            +
                    rescue LoadError
         
     | 
| 
      
 47 
     | 
    
         
            +
                      false
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    def run_background_server
         
     | 
| 
      
 51 
     | 
    
         
            +
                      case select_background_server
         
     | 
| 
      
 52 
     | 
    
         
            +
                      when "puma"
         
     | 
| 
      
 53 
     | 
    
         
            +
                        puts "Toycol starts Puma in single mode, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                        Rack::Handler::Puma.run(@app, **{ Host: ::Toycol::UNIX_SOCKET_PATH, Silent: true })
         
     | 
| 
      
 55 
     | 
    
         
            +
                      else
         
     | 
| 
      
 56 
     | 
    
         
            +
                        puts "Toycol starts build-in server, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
         
     | 
| 
      
 57 
     | 
    
         
            +
                        ::Toycol::Server.run(@app, **{ Path: ::Toycol::UNIX_SOCKET_PATH, Port: @port })
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                register :toycol, Toycol
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/toycol.rb
    CHANGED
    
    | 
         @@ -1,8 +1,27 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require "fileutils"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "toycol/const"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "toycol/helper"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require_relative "toycol/protocol"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative "toycol/proxy"
         
     | 
| 
      
 9 
     | 
    
         
            +
            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/command"
         
     | 
| 
       3 
15 
     | 
    
         
             
            require_relative "toycol/version"
         
     | 
| 
       4 
16 
     | 
    
         | 
| 
       5 
17 
     | 
    
         
             
            module Toycol
         
     | 
| 
       6 
18 
     | 
    
         
             
              class Error < StandardError; end
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              class UnauthorizedMethodError < Error; end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              class UnauthorizedRequestError < Error; end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              class UndefinedRequestMethodError < Error; end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              class UnknownStatusCodeError < Error; end
         
     | 
| 
       8 
27 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "socket"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Toycol
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Client
         
     | 
| 
      
 7 
     | 
    
         
            +
                @port = 9292
         
     | 
| 
      
 8 
     | 
    
         
            +
                CHUNK_SIZE = 1024 * 16
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 11 
     | 
    
         
            +
                  attr_writer :port
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def execute!(request_message, &block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    socket = TCPSocket.new("localhost", @port)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    socket.write(request_message)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    puts "[Toycol] Sent request message: #{request_message}\n---"
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    response_message = []
         
     | 
| 
      
 19 
     | 
    
         
            +
                    response_message << socket.readpartial(CHUNK_SIZE) until socket.eof?
         
     | 
| 
      
 20 
     | 
    
         
            +
                    response_message = response_message.join
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    block ||= default_proc
         
     | 
| 
      
 23 
     | 
    
         
            +
                    block.call(response_message)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 25 
     | 
    
         
            +
                    socket.close
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  private
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def default_proc
         
     | 
| 
      
 31 
     | 
    
         
            +
                    proc do |message|
         
     | 
| 
      
 32 
     | 
    
         
            +
                      puts "[Toycol] Received response message:\n\n"
         
     | 
| 
      
 33 
     | 
    
         
            +
                      puts message
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,132 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "optparse"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative "./client"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Toycol
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Command
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Options
         
     | 
| 
      
 9 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 10 
     | 
    
         
            +
                    def parse!(argv)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      options       = {}
         
     | 
| 
      
 12 
     | 
    
         
            +
                      option_parser = create_option_parser
         
     | 
| 
      
 13 
     | 
    
         
            +
                      sub_command_option_parser = create_sub_command_option_parser
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 16 
     | 
    
         
            +
                        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)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      rescue OptionParser::MissingArgument, OptionParser::InvalidOption, ArgumentError => e
         
     | 
| 
      
 21 
     | 
    
         
            +
                        abort e.message
         
     | 
| 
      
 22 
     | 
    
         
            +
                      end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                      options
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def create_option_parser
         
     | 
| 
      
 28 
     | 
    
         
            +
                      OptionParser.new do |opt|
         
     | 
| 
      
 29 
     | 
    
         
            +
                        opt.banner = "Usage: #{opt.program_name} [-h|--help] [-v|--version] COMMAND [arg...]"
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                        opt.on_head("-v", "--version", "Show Toycol version") do
         
     | 
| 
      
 32 
     | 
    
         
            +
                          opt.version = Toycol::VERSION
         
     | 
| 
      
 33 
     | 
    
         
            +
                          puts opt.ver
         
     | 
| 
      
 34 
     | 
    
         
            +
                          exit
         
     | 
| 
      
 35 
     | 
    
         
            +
                        end
         
     | 
| 
      
 36 
     | 
    
         
            +
                        opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                        opt.separator ""
         
     | 
| 
      
 39 
     | 
    
         
            +
                        opt.separator "Sub commands:"
         
     | 
| 
      
 40 
     | 
    
         
            +
                        sub_command_summaries.each do |command|
         
     | 
| 
      
 41 
     | 
    
         
            +
                          opt.separator [opt.summary_indent, command[:name].ljust(31), command[:summary]].join(" ")
         
     | 
| 
      
 42 
     | 
    
         
            +
                        end
         
     | 
| 
      
 43 
     | 
    
         
            +
                      end
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    def create_sub_command_option_parser
         
     | 
| 
      
 47 
     | 
    
         
            +
                      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
         
     | 
| 
      
 52 
     | 
    
         
            +
                      sub_command_parser
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    private
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    def sub_command_summaries
         
     | 
| 
      
 58 
     | 
    
         
            +
                      [
         
     | 
| 
      
 59 
     | 
    
         
            +
                        { name: "client REQUEST_MESSAGE -p PORT",  summary: "Send request message to server"    },
         
     | 
| 
      
 60 
     | 
    
         
            +
                        { name: "server -u SERVER_NAME",           summary: "Start proxy and background server" }
         
     | 
| 
      
 61 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    def client_option_parser
         
     | 
| 
      
 65 
     | 
    
         
            +
                      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
         
     | 
| 
      
 68 
     | 
    
         
            +
                        end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                        opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    def server_option_parser
         
     | 
| 
      
 75 
     | 
    
         
            +
                      OptionParser.new do |opt|
         
     | 
| 
      
 76 
     | 
    
         
            +
                        opt.on("-o HOST", "--host HOST", "bind to HOST (default: localhost)") do |host|
         
     | 
| 
      
 77 
     | 
    
         
            +
                          ::Rack::Handler::Toycol.host = host
         
     | 
| 
      
 78 
     | 
    
         
            +
                        end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                        opt.on("-p PORT_NUMBER", "--port PORT_NUMBER", "listen on PORT (default: 9292)") do |port|
         
     | 
| 
      
 81 
     | 
    
         
            +
                          ::Rack::Handler::Toycol.port = port
         
     | 
| 
      
 82 
     | 
    
         
            +
                        end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        opt.on("-u SERVER_NAME", "--use SERVER_NAME", "switch using SERVER(puma/build_in)") do |server_name|
         
     | 
| 
      
 85 
     | 
    
         
            +
                          ::Rack::Handler::Toycol.preferred_background_server = server_name
         
     | 
| 
      
 86 
     | 
    
         
            +
                        end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                        opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    def client_command_help_messages
         
     | 
| 
      
 93 
     | 
    
         
            +
                      [
         
     | 
| 
      
 94 
     | 
    
         
            +
                        { name: "client -p=PORT_NUMBER", summary: "Send request to server" }
         
     | 
| 
      
 95 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    def server_command_help_messages
         
     | 
| 
      
 99 
     | 
    
         
            +
                      [
         
     | 
| 
      
 100 
     | 
    
         
            +
                        { name: "server -u=SERVER_NAME", summary: "Start proxy & background server" }
         
     | 
| 
      
 101 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 102 
     | 
    
         
            +
                    end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    def help_command(parser)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      puts parser.help
         
     | 
| 
      
 106 
     | 
    
         
            +
                      exit
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                def self.run(argv)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  new(argv).execute
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def initialize(argv)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @argv = argv
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                def execute
         
     | 
| 
      
 120 
     | 
    
         
            +
                  options = Options.parse!(@argv)
         
     | 
| 
      
 121 
     | 
    
         
            +
                  command = options.delete(:command)
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  case command
         
     | 
| 
      
 124 
     | 
    
         
            +
                  when "client", "c"
         
     | 
| 
      
 125 
     | 
    
         
            +
                    ::Toycol::Client.execute!(options[:request_message])
         
     | 
| 
      
 126 
     | 
    
         
            +
                  when "server", "s"
         
     | 
| 
      
 127 
     | 
    
         
            +
                    ARGV.push("-q", "-s", "toycol")
         
     | 
| 
      
 128 
     | 
    
         
            +
                    Rack::Server.start
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
              end
         
     | 
| 
      
 132 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/toycol/const.rb
    ADDED
    
    | 
         @@ -0,0 +1,102 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Toycol
         
     | 
| 
      
 4 
     | 
    
         
            +
              # For HTTP Protocol
         
     | 
| 
      
 5 
     | 
    
         
            +
              DEFAULT_HTTP_REQUEST_METHODS = %w[
         
     | 
| 
      
 6 
     | 
    
         
            +
                GET
         
     | 
| 
      
 7 
     | 
    
         
            +
                HEAD
         
     | 
| 
      
 8 
     | 
    
         
            +
                POST
         
     | 
| 
      
 9 
     | 
    
         
            +
                OPTIONS
         
     | 
| 
      
 10 
     | 
    
         
            +
                PUT
         
     | 
| 
      
 11 
     | 
    
         
            +
                DELETE
         
     | 
| 
      
 12 
     | 
    
         
            +
                TRACE
         
     | 
| 
      
 13 
     | 
    
         
            +
                PATCH
         
     | 
| 
      
 14 
     | 
    
         
            +
                LINK
         
     | 
| 
      
 15 
     | 
    
         
            +
                UNLINK
         
     | 
| 
      
 16 
     | 
    
         
            +
              ].freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              DEFAULT_HTTP_STATUS_CODES = {
         
     | 
| 
      
 19 
     | 
    
         
            +
                100 => "Continue",
         
     | 
| 
      
 20 
     | 
    
         
            +
                101 => "Switching Protocols",
         
     | 
| 
      
 21 
     | 
    
         
            +
                102 => "Processing",
         
     | 
| 
      
 22 
     | 
    
         
            +
                200 => "OK",
         
     | 
| 
      
 23 
     | 
    
         
            +
                201 => "Created",
         
     | 
| 
      
 24 
     | 
    
         
            +
                202 => "Accepted",
         
     | 
| 
      
 25 
     | 
    
         
            +
                203 => "Non-Authoritative Information",
         
     | 
| 
      
 26 
     | 
    
         
            +
                204 => "No Content",
         
     | 
| 
      
 27 
     | 
    
         
            +
                205 => "Reset Content",
         
     | 
| 
      
 28 
     | 
    
         
            +
                206 => "Partial Content",
         
     | 
| 
      
 29 
     | 
    
         
            +
                207 => "Multi-Status",
         
     | 
| 
      
 30 
     | 
    
         
            +
                208 => "Already Reported",
         
     | 
| 
      
 31 
     | 
    
         
            +
                226 => "IM Used",
         
     | 
| 
      
 32 
     | 
    
         
            +
                300 => "Multiple Choices",
         
     | 
| 
      
 33 
     | 
    
         
            +
                301 => "Moved Permanently",
         
     | 
| 
      
 34 
     | 
    
         
            +
                302 => "Found",
         
     | 
| 
      
 35 
     | 
    
         
            +
                303 => "See Other",
         
     | 
| 
      
 36 
     | 
    
         
            +
                304 => "Not Modified",
         
     | 
| 
      
 37 
     | 
    
         
            +
                305 => "Use Proxy",
         
     | 
| 
      
 38 
     | 
    
         
            +
                307 => "Temporary Redirect",
         
     | 
| 
      
 39 
     | 
    
         
            +
                308 => "Permanent Redirect",
         
     | 
| 
      
 40 
     | 
    
         
            +
                400 => "Bad Request",
         
     | 
| 
      
 41 
     | 
    
         
            +
                401 => "Unauthorized",
         
     | 
| 
      
 42 
     | 
    
         
            +
                402 => "Payment Required",
         
     | 
| 
      
 43 
     | 
    
         
            +
                403 => "Forbidden",
         
     | 
| 
      
 44 
     | 
    
         
            +
                404 => "Not Found",
         
     | 
| 
      
 45 
     | 
    
         
            +
                405 => "Method Not Allowed",
         
     | 
| 
      
 46 
     | 
    
         
            +
                406 => "Not Acceptable",
         
     | 
| 
      
 47 
     | 
    
         
            +
                407 => "Proxy Authentication Required",
         
     | 
| 
      
 48 
     | 
    
         
            +
                408 => "Request Timeout",
         
     | 
| 
      
 49 
     | 
    
         
            +
                409 => "Conflict",
         
     | 
| 
      
 50 
     | 
    
         
            +
                410 => "Gone",
         
     | 
| 
      
 51 
     | 
    
         
            +
                411 => "Length Required",
         
     | 
| 
      
 52 
     | 
    
         
            +
                412 => "Precondition Failed",
         
     | 
| 
      
 53 
     | 
    
         
            +
                413 => "Payload Too Large",
         
     | 
| 
      
 54 
     | 
    
         
            +
                414 => "URI Too Long",
         
     | 
| 
      
 55 
     | 
    
         
            +
                415 => "Unsupported Media Type",
         
     | 
| 
      
 56 
     | 
    
         
            +
                416 => "Range Not Satisfiable",
         
     | 
| 
      
 57 
     | 
    
         
            +
                417 => "Expectation Failed",
         
     | 
| 
      
 58 
     | 
    
         
            +
                418 => "I'm A Teapot",
         
     | 
| 
      
 59 
     | 
    
         
            +
                421 => "Misdirected Request",
         
     | 
| 
      
 60 
     | 
    
         
            +
                422 => "Unprocessable Entity",
         
     | 
| 
      
 61 
     | 
    
         
            +
                423 => "Locked",
         
     | 
| 
      
 62 
     | 
    
         
            +
                424 => "Failed Dependency",
         
     | 
| 
      
 63 
     | 
    
         
            +
                426 => "Upgrade Required",
         
     | 
| 
      
 64 
     | 
    
         
            +
                428 => "Precondition Required",
         
     | 
| 
      
 65 
     | 
    
         
            +
                429 => "Too Many Requests",
         
     | 
| 
      
 66 
     | 
    
         
            +
                431 => "Request Header Fields Too Large",
         
     | 
| 
      
 67 
     | 
    
         
            +
                451 => "Unavailable For Legal Reasons",
         
     | 
| 
      
 68 
     | 
    
         
            +
                500 => "Internal Server Error",
         
     | 
| 
      
 69 
     | 
    
         
            +
                501 => "Not Implemented",
         
     | 
| 
      
 70 
     | 
    
         
            +
                502 => "Bad Gateway",
         
     | 
| 
      
 71 
     | 
    
         
            +
                503 => "Service Unavailable",
         
     | 
| 
      
 72 
     | 
    
         
            +
                504 => "Gateway Timeout",
         
     | 
| 
      
 73 
     | 
    
         
            +
                505 => "HTTP Version Not Supported",
         
     | 
| 
      
 74 
     | 
    
         
            +
                506 => "Variant Also Negotiates",
         
     | 
| 
      
 75 
     | 
    
         
            +
                507 => "Insufficient Storage",
         
     | 
| 
      
 76 
     | 
    
         
            +
                508 => "Loop Detected",
         
     | 
| 
      
 77 
     | 
    
         
            +
                510 => "Not Extended",
         
     | 
| 
      
 78 
     | 
    
         
            +
                511 => "Network Authentication Required"
         
     | 
| 
      
 79 
     | 
    
         
            +
              }.freeze
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              # For environment
         
     | 
| 
      
 82 
     | 
    
         
            +
              ENVIRONMENT  = ENV["RACK_ENV"] || "development"
         
     | 
| 
      
 83 
     | 
    
         
            +
              DEFAULT_HOST = ENVIRONMENT == "development" ? "localhost" : "0.0.0.0"
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
              # For connection from proxy server to app server
         
     | 
| 
      
 86 
     | 
    
         
            +
              UNIX_SOCKET_PATH = ENV["TOYCOL_SOCKET_PATH"] || "/tmp/toycol.socket"
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              # Rack compartible environment
         
     | 
| 
      
 89 
     | 
    
         
            +
              PATH_INFO         = "PATH_INFO"
         
     | 
| 
      
 90 
     | 
    
         
            +
              QUERY_STRING      = "QUERY_STRING"
         
     | 
| 
      
 91 
     | 
    
         
            +
              REQUEST_METHOD    = "REQUEST_METHOD"
         
     | 
| 
      
 92 
     | 
    
         
            +
              SERVER_NAME       = "SERVER_NAME"
         
     | 
| 
      
 93 
     | 
    
         
            +
              SERVER_PORT       = "SERVER_PORT"
         
     | 
| 
      
 94 
     | 
    
         
            +
              CONTENT_LENGTH    = "CONTENT_LENGTH"
         
     | 
| 
      
 95 
     | 
    
         
            +
              RACK_VERSION      = "rack.version"
         
     | 
| 
      
 96 
     | 
    
         
            +
              RACK_INPUT        = "rack.input"
         
     | 
| 
      
 97 
     | 
    
         
            +
              RACK_ERRORS       = "rack.errors"
         
     | 
| 
      
 98 
     | 
    
         
            +
              RACK_MULTITHREAD  = "rack.multithread"
         
     | 
| 
      
 99 
     | 
    
         
            +
              RACK_MULTIPROCESS = "rack.multiprocess"
         
     | 
| 
      
 100 
     | 
    
         
            +
              RACK_RUN_ONCE     = "rack.run_once"
         
     | 
| 
      
 101 
     | 
    
         
            +
              RACK_URL_SCHEME   = "rack.url_scheme"
         
     | 
| 
      
 102 
     | 
    
         
            +
            end
         
     |