tipi 0.30
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 +7 -0
- data/.github/workflows/test.yml +27 -0
- data/.gitignore +56 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +50 -0
- data/LICENSE +21 -0
- data/README.md +23 -0
- data/Rakefile +12 -0
- data/TODO.md +66 -0
- data/bin/tipi +12 -0
- data/docs/README.md +62 -0
- data/docs/summary.md +60 -0
- data/examples/cuba.ru +23 -0
- data/examples/hanami-api.ru +23 -0
- data/examples/http_server.js +24 -0
- data/examples/http_server.rb +21 -0
- data/examples/http_server_forked.rb +29 -0
- data/examples/http_server_graceful.rb +27 -0
- data/examples/http_server_simple.rb +11 -0
- data/examples/http_server_throttled.rb +15 -0
- data/examples/http_server_timeout.rb +35 -0
- data/examples/http_ws_server.rb +37 -0
- data/examples/https_server.rb +24 -0
- data/examples/https_server_forked.rb +32 -0
- data/examples/https_wss_server.rb +39 -0
- data/examples/rack_server.rb +12 -0
- data/examples/rack_server_https.rb +19 -0
- data/examples/rack_server_https_forked.rb +27 -0
- data/examples/websocket_secure_server.rb +27 -0
- data/examples/websocket_server.rb +24 -0
- data/examples/ws_page.html +34 -0
- data/examples/wss_page.html +34 -0
- data/lib/tipi.rb +54 -0
- data/lib/tipi/http1_adapter.rb +268 -0
- data/lib/tipi/http2_adapter.rb +74 -0
- data/lib/tipi/http2_stream.rb +134 -0
- data/lib/tipi/rack_adapter.rb +67 -0
- data/lib/tipi/request.rb +118 -0
- data/lib/tipi/version.rb +5 -0
- data/lib/tipi/websocket.rb +61 -0
- data/test/coverage.rb +45 -0
- data/test/eg.rb +27 -0
- data/test/helper.rb +51 -0
- data/test/run.rb +5 -0
- data/test/test_http_server.rb +321 -0
- data/tipi.gemspec +34 -0
- metadata +241 -0
    
        data/examples/cuba.ru
    ADDED
    
    | @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'cuba'
         | 
| 4 | 
            +
            require 'cuba/safe'
         | 
| 5 | 
            +
            require 'delegate' # See https://github.com/rack/rack/pull/1610
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Cuba.use Rack::Session::Cookie, secret: '__a_very_long_string__'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Cuba.plugin Cuba::Safe
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            Cuba.define do
         | 
| 12 | 
            +
              on get do
         | 
| 13 | 
            +
                on 'hello' do
         | 
| 14 | 
            +
                  res.write 'Hello world!'
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                on root do
         | 
| 18 | 
            +
                  res.redirect '/hello'
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            run Cuba
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'hanami/api'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class ExampleApi < Hanami::API
         | 
| 6 | 
            +
              get '/hello' do
         | 
| 7 | 
            +
                'Hello world!'
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              get '/' do
         | 
| 11 | 
            +
                redirect '/hello'
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              get '/404' do
         | 
| 15 | 
            +
                404
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              get '/500' do
         | 
| 19 | 
            +
                500
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            run ExampleApi.new
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            // For the sake of comparing performance, here's a node.js-based HTTP server
         | 
| 2 | 
            +
            // doing roughly the same thing as http_server. Preliminary benchmarking shows
         | 
| 3 | 
            +
            // the ruby version has a throughput (req/s) of about 2/3 of the JS version.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            const http = require('http');
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            const MSG = 'Hello World';
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            const server = http.createServer((req, res) => {
         | 
| 10 | 
            +
              // let requestCopy = {
         | 
| 11 | 
            +
              //   method: req.method,
         | 
| 12 | 
            +
              //   request_url: req.url,
         | 
| 13 | 
            +
              //   headers: req.headers
         | 
| 14 | 
            +
              // };
         | 
| 15 | 
            +
              
         | 
| 16 | 
            +
              // res.writeHead(200, { 'Content-Type': 'application/json' });
         | 
| 17 | 
            +
              // res.end(JSON.stringify(requestCopy));
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              res.writeHead(200);
         | 
| 20 | 
            +
              res.end(MSG)
         | 
| 21 | 
            +
            });
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            server.listen(1235);
         | 
| 24 | 
            +
            console.log('Listening on port 1235');
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            opts = {
         | 
| 7 | 
            +
              reuse_addr:  true,
         | 
| 8 | 
            +
              dont_linger: true
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            spin do
         | 
| 12 | 
            +
              Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 13 | 
            +
                req.respond("Hello world!\n")
         | 
| 14 | 
            +
              rescue Exception => e
         | 
| 15 | 
            +
                p e
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 20 | 
            +
            puts 'Listening on port 1234...'
         | 
| 21 | 
            +
            suspend
         | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ::Exception.__disable_sanitized_backtrace__ = true
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            opts = {
         | 
| 9 | 
            +
              reuse_addr:  true,
         | 
| 10 | 
            +
              dont_linger: true
         | 
| 11 | 
            +
            }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            puts 'Listening on port 1234'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            child_pids = []
         | 
| 18 | 
            +
            8.times do
         | 
| 19 | 
            +
              pid = Polyphony.fork do
         | 
| 20 | 
            +
                puts "forked pid: #{Process.pid}"
         | 
| 21 | 
            +
                server.each do |req|
         | 
| 22 | 
            +
                  req.respond("Hello world! from pid: #{Process.pid}\n")
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              rescue Interrupt
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
              child_pids << pid
         | 
| 27 | 
            +
            end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            child_pids.each { |pid| Thread.current.agent.waitpid(pid) }
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'polyphony'
         | 
| 5 | 
            +
            require 'tipi'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            opts = {
         | 
| 8 | 
            +
              reuse_addr:  true,
         | 
| 9 | 
            +
              dont_linger: true
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            server = spin do
         | 
| 13 | 
            +
              Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 14 | 
            +
                req.respond("Hello world!\n")
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            trap('SIGHUP') do
         | 
| 19 | 
            +
              puts 'got hup'
         | 
| 20 | 
            +
              server.interrupt
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 24 | 
            +
            puts 'Send HUP to stop gracefully'
         | 
| 25 | 
            +
            puts 'Listening on port 1234...'
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            suspend
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            $throttler = throttle(1000)
         | 
| 7 | 
            +
            opts = { reuse_addr: true, dont_linger: true }
         | 
| 8 | 
            +
            spin do
         | 
| 9 | 
            +
              Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 10 | 
            +
                $throttler.call { req.respond("Hello world!\n") }
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 15 | 
            +
            puts 'Listening on port 1234...'
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            opts = {
         | 
| 7 | 
            +
              reuse_addr:  true,
         | 
| 8 | 
            +
              dont_linger: true
         | 
| 9 | 
            +
            }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            def timeout_handler(timeout, &handler)
         | 
| 12 | 
            +
              ->(req) do
         | 
| 13 | 
            +
                cancel_after(timeout) { handler.(req) }
         | 
| 14 | 
            +
              rescue Polyphony::Cancel
         | 
| 15 | 
            +
                req.respond("timeout\n", ':status' => 502)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            sleep 0
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            spin do
         | 
| 22 | 
            +
              Tipi.serve(
         | 
| 23 | 
            +
                '0.0.0.0',
         | 
| 24 | 
            +
                1234,
         | 
| 25 | 
            +
                opts,
         | 
| 26 | 
            +
                &timeout_handler(0.1) do |req|
         | 
| 27 | 
            +
                  sleep rand(0.01..0.2)
         | 
| 28 | 
            +
                  req.respond("Hello timeout world!\n")
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              )
         | 
| 31 | 
            +
            end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 34 | 
            +
            puts 'Listening on port 1234...'
         | 
| 35 | 
            +
            suspend
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'tipi/websocket'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            def ws_handler(conn)
         | 
| 8 | 
            +
              timer = spin do
         | 
| 9 | 
            +
                throttled_loop(1) do
         | 
| 10 | 
            +
                  conn << Time.now.to_s
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
              while (msg = conn.recv)
         | 
| 14 | 
            +
                conn << "you said: #{msg}"
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            ensure
         | 
| 17 | 
            +
              timer.stop
         | 
| 18 | 
            +
            end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            opts = {
         | 
| 21 | 
            +
              reuse_addr:  true,
         | 
| 22 | 
            +
              dont_linger: true,
         | 
| 23 | 
            +
              upgrade:     {
         | 
| 24 | 
            +
                websocket: Polyphony::Websocket.handler(&method(:ws_handler))
         | 
| 25 | 
            +
              }
         | 
| 26 | 
            +
            }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            HTML = IO.read(File.join(__dir__, 'ws_page.html'))
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            spin do
         | 
| 31 | 
            +
              Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 32 | 
            +
                req.respond(HTML, 'Content-Type' => 'text/html')
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 37 | 
            +
            puts 'Listening on port 1234...'
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ::Exception.__disable_sanitized_backtrace__ = true
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 10 | 
            +
            opts = {
         | 
| 11 | 
            +
              reuse_addr:     true,
         | 
| 12 | 
            +
              dont_linger:    true,
         | 
| 13 | 
            +
              secure_context: authority.server_context
         | 
| 14 | 
            +
            }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 17 | 
            +
            puts 'Listening on port 1234...'
         | 
| 18 | 
            +
            Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 19 | 
            +
              req.respond("Hello world!\n")
         | 
| 20 | 
            +
              # req.send_headers
         | 
| 21 | 
            +
              # req.send_chunk("Method: #{req.method}\n")
         | 
| 22 | 
            +
              # req.send_chunk("Path: #{req.path}\n")
         | 
| 23 | 
            +
              # req.send_chunk("Query: #{req.query.inspect}\n", done: true)
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ::Exception.__disable_sanitized_backtrace__ = true
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 10 | 
            +
            opts = {
         | 
| 11 | 
            +
              reuse_addr:     true,
         | 
| 12 | 
            +
              dont_linger:    true,
         | 
| 13 | 
            +
              secure_context: authority.server_context
         | 
| 14 | 
            +
            }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            puts 'Listening on port 1234'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            child_pids = []
         | 
| 21 | 
            +
            4.times do
         | 
| 22 | 
            +
              pid = Polyphony.fork do
         | 
| 23 | 
            +
                puts "forked pid: #{Process.pid}"
         | 
| 24 | 
            +
                server.each do |req|
         | 
| 25 | 
            +
                  req.respond("Hello world!\n")
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              rescue Interrupt
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
              child_pids << pid
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            child_pids.each { |pid| Thread.current.agent.waitpid(pid) }
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            def ws_handler(conn)
         | 
| 8 | 
            +
              timer = spin do
         | 
| 9 | 
            +
                throttled_loop(1) do
         | 
| 10 | 
            +
                  conn << Time.now.to_s
         | 
| 11 | 
            +
                rescue StandardError
         | 
| 12 | 
            +
                  nil
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
              while (msg = conn.recv)
         | 
| 16 | 
            +
                puts "msg: #{msg}"
         | 
| 17 | 
            +
                # conn << "you said: #{msg}"
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            ensure
         | 
| 20 | 
            +
              timer.stop
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 24 | 
            +
            opts = {
         | 
| 25 | 
            +
              reuse_addr:     true,
         | 
| 26 | 
            +
              dont_linger:    true,
         | 
| 27 | 
            +
              secure_context: authority.server_context,
         | 
| 28 | 
            +
              upgrade:        {
         | 
| 29 | 
            +
                websocket: Polyphony::Websocket.handler(&method(:ws_handler))
         | 
| 30 | 
            +
              }
         | 
| 31 | 
            +
            }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            HTML = IO.read(File.join(__dir__, 'wss_page.html'))
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 36 | 
            +
            puts 'Listening on port 1234...'
         | 
| 37 | 
            +
            Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 38 | 
            +
              req.respond(HTML, 'Content-Type' => 'text/html')
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
         | 
| 7 | 
            +
            app = Polyphony::HTTP::Server::RackAdapter.load(app_path)
         | 
| 8 | 
            +
            opts = { reuse_addr: true, dont_linger: true }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            puts 'listening on port 1234'
         | 
| 11 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 12 | 
            +
            Tipi.serve('0.0.0.0', 1234, opts, &app)
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
         | 
| 8 | 
            +
            app = Polyphony::HTTP::Server::RackAdapter.load(app_path)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 11 | 
            +
            opts = {
         | 
| 12 | 
            +
              reuse_addr:     true,
         | 
| 13 | 
            +
              dont_linger:    true,
         | 
| 14 | 
            +
              secure_context: authority.server_context
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            puts 'listening on port 1234'
         | 
| 18 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 19 | 
            +
            Tipi.serve('0.0.0.0', 1234, opts, &app)
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
         | 
| 8 | 
            +
            app = Polyphony::HTTP::Server::RackAdapter.load(app_path)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 11 | 
            +
            opts = {
         | 
| 12 | 
            +
              reuse_addr:     true,
         | 
| 13 | 
            +
              dont_linger:    true,
         | 
| 14 | 
            +
              secure_context: authority.server_context
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
            server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
         | 
| 17 | 
            +
            puts 'Listening on port 1234'
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            child_pids = []
         | 
| 20 | 
            +
            4.times do
         | 
| 21 | 
            +
              child_pids << Polyphony.fork do
         | 
| 22 | 
            +
                puts "forked pid: #{Process.pid}"
         | 
| 23 | 
            +
                server.each(&app)
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            child_pids.each { |pid| EV::Child.new(pid).await }
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'localhost/authority'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            def ws_handler(conn)
         | 
| 8 | 
            +
              while (msg = conn.recv)
         | 
| 9 | 
            +
                conn << "you said: #{msg}"
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            authority = Localhost::Authority.fetch
         | 
| 14 | 
            +
            opts = {
         | 
| 15 | 
            +
              reuse_addr:     true,
         | 
| 16 | 
            +
              dont_linger:    true,
         | 
| 17 | 
            +
              upgrade:        {
         | 
| 18 | 
            +
                websocket: Polyphony::Websocket.handler(&method(:ws_handler))
         | 
| 19 | 
            +
              },
         | 
| 20 | 
            +
              secure_context: authority.server_context
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 24 | 
            +
            puts 'Listening on port 1234...'
         | 
| 25 | 
            +
            Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 26 | 
            +
              req.respond("Hello world!\n")
         | 
| 27 | 
            +
            end
         |