tipi 0.32 → 0.37
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/CHANGELOG.md +27 -0
- data/Gemfile.lock +10 -4
- data/LICENSE +1 -1
- data/TODO.md +13 -47
- data/bin/tipi +13 -0
- data/df/agent.rb +63 -0
- data/df/etc_benchmark.rb +15 -0
- data/df/multi_agent_supervisor.rb +87 -0
- data/df/multi_client.rb +84 -0
- data/df/routing_benchmark.rb +60 -0
- data/df/sample_agent.rb +89 -0
- data/df/server.rb +54 -0
- data/df/sse_page.html +29 -0
- data/df/stress.rb +24 -0
- data/df/ws_page.html +38 -0
- data/e +0 -0
- data/examples/http_request_ws_server.rb +35 -0
- data/examples/http_server.rb +6 -6
- data/examples/http_server_forked.rb +4 -5
- data/examples/http_server_form.rb +23 -0
- data/examples/http_server_throttled_accept.rb +23 -0
- data/examples/http_unix_socket_server.rb +17 -0
- data/examples/http_ws_server.rb +10 -12
- data/examples/routing_server.rb +34 -0
- data/examples/ws_page.html +1 -2
- data/lib/tipi.rb +7 -5
- data/lib/tipi/configuration.rb +1 -1
- data/lib/tipi/digital_fabric.rb +7 -0
- data/lib/tipi/digital_fabric/agent.rb +225 -0
- data/lib/tipi/digital_fabric/agent_proxy.rb +265 -0
- data/lib/tipi/digital_fabric/executive.rb +100 -0
- data/lib/tipi/digital_fabric/executive/index.html +69 -0
- data/lib/tipi/digital_fabric/protocol.rb +90 -0
- data/lib/tipi/digital_fabric/request_adapter.rb +48 -0
- data/lib/tipi/digital_fabric/service.rb +230 -0
- data/lib/tipi/http1_adapter.rb +50 -16
- data/lib/tipi/http2_adapter.rb +5 -3
- data/lib/tipi/http2_stream.rb +19 -7
- data/lib/tipi/rack_adapter.rb +11 -3
- data/lib/tipi/version.rb +1 -1
- data/lib/tipi/websocket.rb +33 -29
- data/test/helper.rb +1 -2
- data/test/test_http_server.rb +3 -2
- data/test/test_request.rb +108 -0
- data/tipi.gemspec +7 -3
- metadata +59 -7
- data/lib/tipi/request.rb +0 -118
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'polyphony'
         | 
| 5 | 
            +
            require 'tipi/digital_fabric'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class FakeAgent
         | 
| 8 | 
            +
              def initialize(idx)
         | 
| 9 | 
            +
                @idx = idx
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
            end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            def setup_df_service_with_agents(agent_count)
         | 
| 14 | 
            +
              server = DigitalFabric::Service.new
         | 
| 15 | 
            +
              agent_count.times do |i|
         | 
| 16 | 
            +
                server.mount({path: "/#{i}"}, FakeAgent.new(i))
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              server
         | 
| 19 | 
            +
            end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            def benchmark_route_compilation(agent_count, iterations)
         | 
| 22 | 
            +
              service = setup_df_service_with_agents(agent_count)
         | 
| 23 | 
            +
              t0 = Time.now
         | 
| 24 | 
            +
              iterations.times { service.compile_agent_routes }
         | 
| 25 | 
            +
              elapsed = Time.now - t0
         | 
| 26 | 
            +
              puts "route_compilation: #{agent_count} => #{elapsed / iterations}s (#{1/(elapsed / iterations)} ops/sec)"
         | 
| 27 | 
            +
            end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            class FauxRequest
         | 
| 30 | 
            +
              def initialize(agent_count)
         | 
| 31 | 
            +
                @agent_count = agent_count
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def headers
         | 
| 35 | 
            +
                { ':path' => "/#{rand(@agent_count)}"}
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            def benchmark_find_agent(agent_count, iterations)
         | 
| 40 | 
            +
              service = setup_df_service_with_agents(agent_count)
         | 
| 41 | 
            +
              t0 = Time.now
         | 
| 42 | 
            +
              request = FauxRequest.new(agent_count)
         | 
| 43 | 
            +
              iterations.times do
         | 
| 44 | 
            +
                agent = service.find_agent(request)
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
              elapsed = Time.now - t0
         | 
| 47 | 
            +
              puts "routing: #{agent_count} => #{elapsed / iterations}s (#{1/(elapsed / iterations)} ops/sec)"
         | 
| 48 | 
            +
            end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            def benchmark
         | 
| 51 | 
            +
              benchmark_route_compilation(100, 1000)
         | 
| 52 | 
            +
              benchmark_route_compilation(500,  200)
         | 
| 53 | 
            +
              benchmark_route_compilation(1000, 100)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              benchmark_find_agent(100, 1000)
         | 
| 56 | 
            +
              benchmark_find_agent(500,  200)
         | 
| 57 | 
            +
              benchmark_find_agent(1000, 100)
         | 
| 58 | 
            +
            end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            benchmark
         | 
    
        data/df/sample_agent.rb
    ADDED
    
    | @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'polyphony'
         | 
| 5 | 
            +
            require 'json'
         | 
| 6 | 
            +
            require 'tipi/digital_fabric/protocol'
         | 
| 7 | 
            +
            require 'tipi/digital_fabric/agent'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Protocol = DigitalFabric::Protocol
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            class SampleAgent < DigitalFabric::Agent
         | 
| 12 | 
            +
              HTML_WS = IO.read(File.join(__dir__, 'ws_page.html'))
         | 
| 13 | 
            +
              HTML_SSE = IO.read(File.join(__dir__, 'sse_page.html'))
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def http_request(req)
         | 
| 16 | 
            +
                path = req['headers'][':path']
         | 
| 17 | 
            +
                case path
         | 
| 18 | 
            +
                when '/agent'
         | 
| 19 | 
            +
                  send_df_message(Protocol.http_response(
         | 
| 20 | 
            +
                    req['id'],
         | 
| 21 | 
            +
                    'Hello, world!',
         | 
| 22 | 
            +
                    {},
         | 
| 23 | 
            +
                    true
         | 
| 24 | 
            +
                  ))
         | 
| 25 | 
            +
                when '/agent/ws'
         | 
| 26 | 
            +
                  send_df_message(Protocol.http_response(
         | 
| 27 | 
            +
                    req['id'],
         | 
| 28 | 
            +
                    HTML_WS,
         | 
| 29 | 
            +
                    { 'Content-Type' => 'text/html' },
         | 
| 30 | 
            +
                    true
         | 
| 31 | 
            +
                  ))
         | 
| 32 | 
            +
                when '/agent/sse'
         | 
| 33 | 
            +
                  send_df_message(Protocol.http_response(
         | 
| 34 | 
            +
                    req['id'],
         | 
| 35 | 
            +
                    HTML_SSE,
         | 
| 36 | 
            +
                    { 'Content-Type' => 'text/html' },
         | 
| 37 | 
            +
                    true
         | 
| 38 | 
            +
                  ))
         | 
| 39 | 
            +
                when '/agent/sse/events'
         | 
| 40 | 
            +
                  stream_sse_response(req)
         | 
| 41 | 
            +
                else
         | 
| 42 | 
            +
                  send_df_message(Protocol.http_response(
         | 
| 43 | 
            +
                    req['id'],
         | 
| 44 | 
            +
                    nil,
         | 
| 45 | 
            +
                    { ':status' => 400 },
         | 
| 46 | 
            +
                    true
         | 
| 47 | 
            +
                  ))
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def ws_request(req)
         | 
| 53 | 
            +
                send_df_message(Protocol.ws_response(req['id'], {}))
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                10.times do
         | 
| 56 | 
            +
                  sleep 1
         | 
| 57 | 
            +
                  send_df_message(Protocol.ws_data(req['id'], Time.now.to_s))
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
                send_df_message(Protocol.ws_close(req['id']))
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              def stream_sse_response(req)
         | 
| 63 | 
            +
                send_df_message(Protocol.http_response(
         | 
| 64 | 
            +
                  req['id'],
         | 
| 65 | 
            +
                  nil,
         | 
| 66 | 
            +
                  { 'Content-Type' => 'text/event-stream' },
         | 
| 67 | 
            +
                  false
         | 
| 68 | 
            +
                ))
         | 
| 69 | 
            +
                10.times do
         | 
| 70 | 
            +
                  sleep 1
         | 
| 71 | 
            +
                  send_df_message(Protocol.http_response(
         | 
| 72 | 
            +
                    req['id'],
         | 
| 73 | 
            +
                    "data: #{Time.now}\n\n",
         | 
| 74 | 
            +
                    nil,
         | 
| 75 | 
            +
                    false
         | 
| 76 | 
            +
                  ))
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
                send_df_message(Protocol.http_response(
         | 
| 79 | 
            +
                  req['id'],
         | 
| 80 | 
            +
                  "retry: 0\n\n",
         | 
| 81 | 
            +
                  nil,
         | 
| 82 | 
            +
                  true
         | 
| 83 | 
            +
                ))
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
              
         | 
| 86 | 
            +
            end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            agent = SampleAgent.new('127.0.0.1', 4411, { path: '/agent' })
         | 
| 89 | 
            +
            agent.run
         | 
    
        data/df/server.rb
    ADDED
    
    | @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
            require 'tipi/digital_fabric'
         | 
| 6 | 
            +
            require 'tipi/digital_fabric/executive'
         | 
| 7 | 
            +
            require 'json'
         | 
| 8 | 
            +
            require 'fileutils'
         | 
| 9 | 
            +
            FileUtils.cd(__dir__)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            service = DigitalFabric::Service.new(token: 'foobar')
         | 
| 12 | 
            +
            executive = DigitalFabric::Executive.new(service, { host: 'executive.realiteq.net' })
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            spin_loop(interval: 60) { GC.start }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            class Polyphony::BaseException
         | 
| 17 | 
            +
              attr_reader :caller_backtrace
         | 
| 18 | 
            +
            end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            tcp_listener = spin do
         | 
| 23 | 
            +
              opts = {
         | 
| 24 | 
            +
                reuse_addr:  true,
         | 
| 25 | 
            +
                dont_linger: true,
         | 
| 26 | 
            +
              }
         | 
| 27 | 
            +
              puts 'Listening on localhost:4411'
         | 
| 28 | 
            +
              server = Polyphony::Net.tcp_listen('0.0.0.0', 4411, opts)
         | 
| 29 | 
            +
              server.accept_loop do |client|
         | 
| 30 | 
            +
                spin do
         | 
| 31 | 
            +
                  service.incr_connection_count
         | 
| 32 | 
            +
                  Tipi.client_loop(client, opts) { |req| service.http_request(req) }
         | 
| 33 | 
            +
                ensure
         | 
| 34 | 
            +
                  service.decr_connection_count
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            UNIX_SOCKET_PATH = '/tmp/df.sock'
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            unix_listener = spin do
         | 
| 42 | 
            +
              puts "Listening on #{UNIX_SOCKET_PATH}"
         | 
| 43 | 
            +
              FileUtils.rm(UNIX_SOCKET_PATH) if File.exists?(UNIX_SOCKET_PATH)
         | 
| 44 | 
            +
              socket = UNIXServer.new(UNIX_SOCKET_PATH)
         | 
| 45 | 
            +
              Tipi.accept_loop(socket, {}) { |req| service.http_request(req) }
         | 
| 46 | 
            +
            end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            begin
         | 
| 49 | 
            +
              Fiber.await(tcp_listener, unix_listener)
         | 
| 50 | 
            +
            rescue Interrupt
         | 
| 51 | 
            +
              puts "Got SIGINT, shutting down gracefully"
         | 
| 52 | 
            +
              service.graceful_shutdown
         | 
| 53 | 
            +
              puts "post graceful shutdown"
         | 
| 54 | 
            +
            end
         | 
    
        data/df/sse_page.html
    ADDED
    
    | @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            <!doctype html>
         | 
| 2 | 
            +
            <html lang="en">
         | 
| 3 | 
            +
            <head>
         | 
| 4 | 
            +
              <title>SSE Client</title>
         | 
| 5 | 
            +
            </head>
         | 
| 6 | 
            +
            <body>
         | 
| 7 | 
            +
              <h1>SSE Client</h1>
         | 
| 8 | 
            +
              <script>
         | 
| 9 | 
            +
                var connect = function () {
         | 
| 10 | 
            +
                  console.log("connecting...");
         | 
| 11 | 
            +
                  var eventSource = new EventSource("/agent/sse/events");
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  eventSource.addEventListener('open', function(e) {
         | 
| 14 | 
            +
                    console.log("connected");
         | 
| 15 | 
            +
                    document.querySelector('#status').innerText = 'connected';
         | 
| 16 | 
            +
                    return false;
         | 
| 17 | 
            +
                  }, false);
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  eventSource.addEventListener('message', function(e) {
         | 
| 20 | 
            +
                    document.querySelector('#msg').innerText = e.data;
         | 
| 21 | 
            +
                  }, false);
         | 
| 22 | 
            +
                };
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                window.onload = connect;
         | 
| 25 | 
            +
              </script>
         | 
| 26 | 
            +
              <h1 id="status">disconnected</h1>
         | 
| 27 | 
            +
              <h1 id="msg"></h1>
         | 
| 28 | 
            +
            </body>
         | 
| 29 | 
            +
            </html>
         | 
    
        data/df/stress.rb
    ADDED
    
    | @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'polyphony'
         | 
| 5 | 
            +
            require 'fileutils'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            FileUtils.cd(__dir__)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            def monitor_process(cmd)
         | 
| 10 | 
            +
              while true
         | 
| 11 | 
            +
                puts "Starting #{cmd}"
         | 
| 12 | 
            +
                Polyphony::Process.watch(cmd)
         | 
| 13 | 
            +
                sleep 5
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 18 | 
            +
            puts 'Starting stress test'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            spin { monitor_process('ruby server.rb') }
         | 
| 21 | 
            +
            spin { monitor_process('ruby multi_agent_supervisor.rb') }
         | 
| 22 | 
            +
            spin { monitor_process('ruby multi_client.rb') }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            sleep
         | 
    
        data/df/ws_page.html
    ADDED
    
    | @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            <!doctype html>
         | 
| 2 | 
            +
            <html lang="en">
         | 
| 3 | 
            +
            <head>
         | 
| 4 | 
            +
              <title>Websocket Client</title>
         | 
| 5 | 
            +
            </head>
         | 
| 6 | 
            +
            <body>
         | 
| 7 | 
            +
              <h1>WebSocket Client</h1>
         | 
| 8 | 
            +
              <script>
         | 
| 9 | 
            +
                var connect = function () {
         | 
| 10 | 
            +
                  console.log("connecting...")
         | 
| 11 | 
            +
                  var exampleSocket = new WebSocket("wss://dev.realiteq.net/agent");
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  exampleSocket.onopen = function (event) {
         | 
| 14 | 
            +
                    console.log("connected");
         | 
| 15 | 
            +
                    document.querySelector('#status').innerText = 'connected';
         | 
| 16 | 
            +
                    exampleSocket.send("Can you hear me?");
         | 
| 17 | 
            +
                  };
         | 
| 18 | 
            +
                  exampleSocket.onclose = function (event) {
         | 
| 19 | 
            +
                    console.log("disconnected");
         | 
| 20 | 
            +
                    document.querySelector('#status').innerText = 'disconnected';
         | 
| 21 | 
            +
                    setTimeout(function () {
         | 
| 22 | 
            +
                      // exampleSocket.removeAllListeners();
         | 
| 23 | 
            +
                      connect();
         | 
| 24 | 
            +
                    }, 1000);
         | 
| 25 | 
            +
                  }
         | 
| 26 | 
            +
                  exampleSocket.onmessage = function (event) {
         | 
| 27 | 
            +
                    console.log("got message", event.data);
         | 
| 28 | 
            +
                    document.querySelector('#msg').innerText = event.data;
         | 
| 29 | 
            +
                    console.log(event.data);
         | 
| 30 | 
            +
                  }
         | 
| 31 | 
            +
                };
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                window.onload = connect;
         | 
| 34 | 
            +
              </script>
         | 
| 35 | 
            +
              <h1 id="status">disconnected</h1>
         | 
| 36 | 
            +
              <h1 id="msg"></h1>
         | 
| 37 | 
            +
            </body>
         | 
| 38 | 
            +
            </html>
         | 
    
        data/e
    ADDED
    
    | 
            File without changes
         | 
| @@ -0,0 +1,35 @@ | |
| 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_loop(interval: 1) do
         | 
| 9 | 
            +
                conn << Time.now.to_s
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
              while (msg = conn.recv)
         | 
| 12 | 
            +
                conn << "you said: #{msg}"
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            ensure
         | 
| 15 | 
            +
              timer.stop
         | 
| 16 | 
            +
            end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            opts = {
         | 
| 19 | 
            +
              reuse_addr:  true,
         | 
| 20 | 
            +
              dont_linger: true,
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            HTML = IO.read(File.join(__dir__, 'ws_page.html'))
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 26 | 
            +
            puts 'Listening on port 4411...'
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            Tipi.serve('0.0.0.0', 4411, opts) do |req|
         | 
| 29 | 
            +
              if req.upgrade_protocol == 'websocket'
         | 
| 30 | 
            +
                conn = req.upgrade_to_websocket
         | 
| 31 | 
            +
                ws_handler(conn)
         | 
| 32 | 
            +
              else
         | 
| 33 | 
            +
                req.respond(HTML, 'Content-Type' => 'text/html')
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
    
        data/examples/http_server.rb
    CHANGED
    
    | @@ -8,14 +8,14 @@ opts = { | |
| 8 8 | 
             
              dont_linger: true
         | 
| 9 9 | 
             
            }
         | 
| 10 10 |  | 
| 11 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 12 | 
            +
            puts 'Listening on port 4411...'
         | 
| 13 | 
            +
             | 
| 11 14 | 
             
            spin do
         | 
| 12 | 
            -
              Tipi.serve('0.0.0.0',  | 
| 15 | 
            +
              Tipi.serve('0.0.0.0', 4411, opts) do |req|
         | 
| 13 16 | 
             
                req.respond("Hello world!\n")
         | 
| 14 17 | 
             
              rescue Exception => e
         | 
| 15 18 | 
             
                p e
         | 
| 16 19 | 
             
              end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
            puts "pid: #{Process.pid}"
         | 
| 20 | 
            -
            puts 'Listening on port 1234...'
         | 
| 21 | 
            -
            suspend
         | 
| 20 | 
            +
              p 'done...'
         | 
| 21 | 
            +
            end.await
         | 
| @@ -7,18 +7,15 @@ require 'tipi' | |
| 7 7 |  | 
| 8 8 | 
             
            opts = {
         | 
| 9 9 | 
             
              reuse_addr:  true,
         | 
| 10 | 
            +
              reuse_port: true,
         | 
| 10 11 | 
             
              dont_linger: true
         | 
| 11 12 | 
             
            }
         | 
| 12 13 |  | 
| 13 | 
            -
            server = Polyphony::HTTP::Server.listen('0.0.0.0', 1234, opts)
         | 
| 14 | 
            -
             | 
| 15 | 
            -
            puts 'Listening on port 1234'
         | 
| 16 | 
            -
             | 
| 17 14 | 
             
            child_pids = []
         | 
| 18 15 | 
             
            8.times do
         | 
| 19 16 | 
             
              pid = Polyphony.fork do
         | 
| 20 17 | 
             
                puts "forked pid: #{Process.pid}"
         | 
| 21 | 
            -
                 | 
| 18 | 
            +
                Tipi.serve('0.0.0.0', 1234, opts) do |req|
         | 
| 22 19 | 
             
                  req.respond("Hello world! from pid: #{Process.pid}\n")
         | 
| 23 20 | 
             
                end
         | 
| 24 21 | 
             
              rescue Interrupt
         | 
| @@ -26,4 +23,6 @@ child_pids = [] | |
| 26 23 | 
             
              child_pids << pid
         | 
| 27 24 | 
             
            end
         | 
| 28 25 |  | 
| 26 | 
            +
            puts 'Listening on port 1234'
         | 
| 27 | 
            +
             | 
| 29 28 | 
             
            child_pids.each { |pid| Thread.current.backend.waitpid(pid) }
         | 
| @@ -0,0 +1,23 @@ | |
| 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 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 12 | 
            +
            puts 'Listening on port 4411...'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            spin do
         | 
| 15 | 
            +
              Tipi.serve('0.0.0.0', 4411, opts) do |req|
         | 
| 16 | 
            +
                body = req.read
         | 
| 17 | 
            +
                body2 = req.read
         | 
| 18 | 
            +
                req.respond("body: #{body} (body2: #{body2.inspect})\n")
         | 
| 19 | 
            +
              rescue Exception => e
         | 
| 20 | 
            +
                p e
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
              p 'done...'
         | 
| 23 | 
            +
            end.await
         | 
| @@ -0,0 +1,23 @@ | |
| 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 | 
            +
              reuse_port:     true,
         | 
| 11 | 
            +
              dont_linger:    true
         | 
| 12 | 
            +
            }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            server = Tipi.listen('0.0.0.0', 1234, opts)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            puts 'Listening on port 1234'
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            throttler = Polyphony::Throttler.new(interval: 5)
         | 
| 19 | 
            +
            server.accept_loop do |socket|
         | 
| 20 | 
            +
              throttler.call do
         | 
| 21 | 
            +
                spin { Tipi.client_loop(socket, opts) { |req| req.respond("Hello world!\n") } }
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/setup'
         | 
| 4 | 
            +
            require 'tipi'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            path = '/tmp/tipi.sock'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            puts "pid: #{Process.pid}"
         | 
| 9 | 
            +
            puts "Listening on #{path}"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            FileUtils.rm(path) rescue nil
         | 
| 12 | 
            +
            socket = UNIXServer.new(path)
         | 
| 13 | 
            +
            Tipi.accept_loop(socket, {}) do |req|
         | 
| 14 | 
            +
              req.respond("Hello world!\n")
         | 
| 15 | 
            +
            rescue Exception => e
         | 
| 16 | 
            +
              p e
         | 
| 17 | 
            +
            end
         |