polyphony 0.19 → 0.20
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/.gitignore +1 -1
 - data/.rubocop.yml +87 -1
 - data/CHANGELOG.md +35 -0
 - data/Gemfile.lock +17 -6
 - data/README.md +200 -139
 - data/Rakefile +4 -4
 - data/TODO.md +35 -7
 - data/bin/poly +11 -0
 - data/docs/getting-started/getting-started.md +1 -1
 - data/docs/summary.md +3 -0
 - data/docs/technical-overview/exception-handling.md +94 -0
 - data/docs/technical-overview/fiber-scheduling.md +99 -0
 - data/examples/core/cancel.rb +8 -4
 - data/examples/core/channel_echo.rb +18 -17
 - data/examples/core/defer.rb +12 -0
 - data/examples/core/enumerator.rb +4 -4
 - data/examples/core/fiber_error.rb +9 -0
 - data/examples/core/fiber_error_with_backtrace.rb +73 -0
 - data/examples/core/fork.rb +6 -6
 - data/examples/core/genserver.rb +16 -8
 - data/examples/core/lock.rb +3 -3
 - data/examples/core/move_on.rb +4 -3
 - data/examples/core/move_on_twice.rb +5 -5
 - data/examples/core/move_on_with_ensure.rb +8 -11
 - data/examples/core/move_on_with_value.rb +14 -0
 - data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
 - data/examples/core/nested_cancel.rb +5 -5
 - data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
 - data/examples/core/nested_spin.rb +17 -0
 - data/examples/core/pingpong.rb +21 -0
 - data/examples/core/pulse.rb +4 -5
 - data/examples/core/resource.rb +6 -4
 - data/examples/core/resource_cancel.rb +6 -9
 - data/examples/core/resource_delegate.rb +3 -3
 - data/examples/core/sleep.rb +3 -3
 - data/examples/core/sleep_spin.rb +19 -0
 - data/examples/core/snooze.rb +32 -0
 - data/examples/core/spin.rb +14 -0
 - data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
 - data/examples/core/spin_error.rb +17 -0
 - data/examples/core/spin_error_backtrace.rb +30 -0
 - data/examples/core/spin_uncaught_error.rb +15 -0
 - data/examples/core/supervisor.rb +8 -8
 - data/examples/core/supervisor_with_cancel_scope.rb +7 -7
 - data/examples/core/supervisor_with_error.rb +8 -8
 - data/examples/core/supervisor_with_manual_move_on.rb +6 -7
 - data/examples/core/suspend.rb +13 -0
 - data/examples/core/thread.rb +1 -1
 - data/examples/core/thread_cancel.rb +9 -11
 - data/examples/core/thread_pool.rb +18 -14
 - data/examples/core/throttle.rb +7 -7
 - data/examples/core/timeout.rb +3 -3
 - data/examples/fs/read.rb +7 -9
 - data/examples/http/config.ru +7 -3
 - data/examples/http/cuba.ru +22 -0
 - data/examples/http/happy_eyeballs.rb +6 -4
 - data/examples/http/http_client.rb +1 -1
 - data/examples/http/http_get.rb +1 -1
 - data/examples/http/http_parse_experiment.rb +21 -16
 - data/examples/http/http_proxy.rb +28 -26
 - data/examples/http/http_server.rb +10 -10
 - data/examples/http/http_server_forked.rb +6 -5
 - data/examples/http/http_server_throttled.rb +3 -3
 - data/examples/http/http_ws_server.rb +11 -11
 - data/examples/http/https_raw_client.rb +1 -1
 - data/examples/http/https_server.rb +8 -8
 - data/examples/http/https_wss_server.rb +13 -11
 - data/examples/http/rack_server.rb +2 -2
 - data/examples/http/rack_server_https.rb +4 -4
 - data/examples/http/rack_server_https_forked.rb +5 -5
 - data/examples/http/websocket_secure_server.rb +6 -6
 - data/examples/http/websocket_server.rb +5 -5
 - data/examples/interfaces/pg_client.rb +4 -4
 - data/examples/interfaces/pg_pool.rb +13 -6
 - data/examples/interfaces/pg_transaction.rb +5 -4
 - data/examples/interfaces/redis_channels.rb +15 -11
 - data/examples/interfaces/redis_client.rb +2 -2
 - data/examples/interfaces/redis_pubsub.rb +2 -1
 - data/examples/interfaces/redis_pubsub_perf.rb +13 -9
 - data/examples/io/backticks.rb +11 -0
 - data/examples/io/cat.rb +4 -5
 - data/examples/io/echo_client.rb +9 -4
 - data/examples/io/echo_client_from_stdin.rb +20 -0
 - data/examples/io/echo_pipe.rb +7 -8
 - data/examples/io/echo_server.rb +8 -6
 - data/examples/io/echo_server_with_timeout.rb +13 -10
 - data/examples/io/echo_stdin.rb +3 -3
 - data/examples/io/httparty.rb +2 -2
 - data/examples/io/httparty_multi.rb +8 -4
 - data/examples/io/httparty_threaded.rb +6 -2
 - data/examples/io/io_read.rb +2 -2
 - data/examples/io/irb.rb +16 -4
 - data/examples/io/net-http.rb +3 -3
 - data/examples/io/open.rb +17 -0
 - data/examples/io/system.rb +3 -3
 - data/examples/io/tcpserver.rb +15 -0
 - data/examples/io/tcpsocket.rb +6 -5
 - data/examples/performance/multi_snooze.rb +29 -0
 - data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
 - data/examples/performance/snooze_raw.rb +39 -0
 - data/ext/gyro/async.c +165 -0
 - data/ext/gyro/child.c +167 -0
 - data/ext/{ev → gyro}/extconf.rb +4 -3
 - data/ext/gyro/gyro.c +316 -0
 - data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
 - data/ext/gyro/gyro_ext.c +23 -0
 - data/ext/{ev → gyro}/io.c +65 -57
 - data/ext/{ev → gyro}/libev.h +0 -0
 - data/ext/gyro/signal.c +117 -0
 - data/ext/{ev → gyro}/socket.c +61 -6
 - data/ext/gyro/timer.c +199 -0
 - data/ext/libev/Changes +35 -0
 - data/ext/libev/README +2 -1
 - data/ext/libev/ev.c +213 -151
 - data/ext/libev/ev.h +95 -88
 - data/ext/libev/ev_epoll.c +26 -15
 - data/ext/libev/ev_kqueue.c +11 -5
 - data/ext/libev/ev_linuxaio.c +642 -0
 - data/ext/libev/ev_poll.c +13 -8
 - data/ext/libev/ev_port.c +5 -2
 - data/ext/libev/ev_vars.h +14 -3
 - data/ext/libev/ev_wrap.h +16 -0
 - data/lib/ev_ext.bundle +0 -0
 - data/lib/polyphony.rb +46 -50
 - data/lib/polyphony/auto_run.rb +12 -0
 - data/lib/polyphony/core/cancel_scope.rb +11 -7
 - data/lib/polyphony/core/channel.rb +16 -9
 - data/lib/polyphony/core/coprocess.rb +101 -51
 - data/lib/polyphony/core/exceptions.rb +14 -12
 - data/lib/polyphony/core/resource_pool.rb +21 -8
 - data/lib/polyphony/core/supervisor.rb +10 -5
 - data/lib/polyphony/core/sync.rb +7 -6
 - data/lib/polyphony/core/thread.rb +4 -4
 - data/lib/polyphony/core/thread_pool.rb +4 -4
 - data/lib/polyphony/core/throttler.rb +6 -4
 - data/lib/polyphony/extensions/core.rb +253 -0
 - data/lib/polyphony/extensions/io.rb +28 -16
 - data/lib/polyphony/extensions/openssl.rb +2 -1
 - data/lib/polyphony/extensions/socket.rb +47 -52
 - data/lib/polyphony/http.rb +4 -3
 - data/lib/polyphony/http/agent.rb +68 -57
 - data/lib/polyphony/http/server.rb +5 -5
 - data/lib/polyphony/http/server/http1.rb +268 -0
 - data/lib/polyphony/http/server/http2.rb +62 -0
 - data/lib/polyphony/http/server/http2_stream.rb +104 -0
 - data/lib/polyphony/http/server/rack.rb +64 -0
 - data/lib/polyphony/http/server/request.rb +119 -0
 - data/lib/polyphony/net.rb +26 -15
 - data/lib/polyphony/postgres.rb +17 -13
 - data/lib/polyphony/redis.rb +16 -15
 - data/lib/polyphony/version.rb +1 -1
 - data/lib/polyphony/websocket.rb +11 -4
 - data/polyphony.gemspec +13 -9
 - data/test/eg.rb +27 -0
 - data/test/helper.rb +25 -0
 - data/test/run.rb +5 -0
 - data/test/test_async.rb +33 -0
 - data/test/test_coprocess.rb +239 -77
 - data/test/test_core.rb +95 -61
 - data/test/test_gyro.rb +148 -0
 - data/test/test_http_server.rb +313 -0
 - data/test/test_io.rb +79 -27
 - data/test/test_kernel.rb +22 -12
 - data/test/test_signal.rb +36 -0
 - data/test/test_timer.rb +24 -0
 - metadata +89 -33
 - data/examples/core/nested_async.rb +0 -17
 - data/examples/core/next_tick.rb +0 -12
 - data/examples/core/sleep_spawn.rb +0 -19
 - data/examples/core/spawn.rb +0 -14
 - data/examples/core/spawn_error.rb +0 -28
 - data/examples/performance/perf_multi_snooze.rb +0 -21
 - data/ext/ev/async.c +0 -168
 - data/ext/ev/child.c +0 -169
 - data/ext/ev/ev_ext.c +0 -23
 - data/ext/ev/ev_module.c +0 -242
 - data/ext/ev/signal.c +0 -119
 - data/ext/ev/timer.c +0 -197
 - data/lib/polyphony/core/fiber_pool.rb +0 -98
 - data/lib/polyphony/extensions/kernel.rb +0 -169
 - data/lib/polyphony/http/http1_adapter.rb +0 -254
 - data/lib/polyphony/http/http2_adapter.rb +0 -157
 - data/lib/polyphony/http/rack.rb +0 -25
 - data/lib/polyphony/http/request.rb +0 -66
 - data/test/test_ev.rb +0 -110
 
| 
         @@ -1,5 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # IO overrides
         
     | 
| 
       3 
6 
     | 
    
         
             
            class ::IO
         
     | 
| 
       4 
7 
     | 
    
         
             
              class << self
         
     | 
| 
       5 
8 
     | 
    
         
             
                alias_method :orig_binread, :binread
         
     | 
| 
         @@ -18,11 +21,14 @@ class ::IO 
     | 
|
| 
       18 
21 
     | 
    
         
             
                  end
         
     | 
| 
       19 
22 
     | 
    
         
             
                end
         
     | 
| 
       20 
23 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                EMPTY_HASH = {}
         
     | 
| 
      
 24 
     | 
    
         
            +
                EMPTY_HASH = {}.freeze
         
     | 
| 
       22 
25 
     | 
    
         | 
| 
       23 
26 
     | 
    
         
             
                alias_method :orig_foreach, :foreach
         
     | 
| 
       24 
27 
     | 
    
         
             
                def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block)
         
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 28 
     | 
    
         
            +
                  if sep.is_a?(Integer)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    sep = $/
         
     | 
| 
      
 30 
     | 
    
         
            +
                    limit = sep
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
       26 
32 
     | 
    
         
             
                  File.open(name, 'r') do |f|
         
     | 
| 
       27 
33 
     | 
    
         
             
                    f.each_line(sep, limit, getline_args, &block)
         
     | 
| 
       28 
34 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -30,7 +36,10 @@ class ::IO 
     | 
|
| 
       30 
36 
     | 
    
         | 
| 
       31 
37 
     | 
    
         
             
                alias_method :orig_read, :read
         
     | 
| 
       32 
38 
     | 
    
         
             
                def read(name, length = nil, offset = nil, opt = EMPTY_HASH)
         
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
      
 39 
     | 
    
         
            +
                  if length.is_a?(Hash)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    opt = length
         
     | 
| 
      
 41 
     | 
    
         
            +
                    length = nil
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
       34 
43 
     | 
    
         
             
                  File.open(name, opt[:mode] || 'r') do |f|
         
     | 
| 
       35 
44 
     | 
    
         
             
                    f.seek(offset) if offset
         
     | 
| 
       36 
45 
     | 
    
         
             
                    length ? f.read(length) : f.read
         
     | 
| 
         @@ -40,7 +49,6 @@ class ::IO 
     | 
|
| 
       40 
49 
     | 
    
         
             
                # alias_method :orig_readlines, :readlines
         
     | 
| 
       41 
50 
     | 
    
         
             
                # def readlines(name, sep = $/, limit = nil, getline_args = EMPTY_HASH)
         
     | 
| 
       42 
51 
     | 
    
         
             
                #   File.open(name, 'r') do |f|
         
     | 
| 
       43 
     | 
    
         
            -
                #     puts "readlines(#{sep.inspect}, #{limit.inspect}, #{getline_args.inspect}"
         
     | 
| 
       44 
52 
     | 
    
         
             
                #     f.readlines(sep, limit, getline_args)
         
     | 
| 
       45 
53 
     | 
    
         
             
                #   end
         
     | 
| 
       46 
54 
     | 
    
         
             
                # end
         
     | 
| 
         @@ -54,10 +62,10 @@ class ::IO 
     | 
|
| 
       54 
62 
     | 
    
         
             
                end
         
     | 
| 
       55 
63 
     | 
    
         | 
| 
       56 
64 
     | 
    
         
             
                alias_method :orig_popen, :popen
         
     | 
| 
       57 
     | 
    
         
            -
                def popen( 
     | 
| 
       58 
     | 
    
         
            -
                   
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                   
     | 
| 
      
 65 
     | 
    
         
            +
                def popen(cmd, mode = 'r')
         
     | 
| 
      
 66 
     | 
    
         
            +
                  return orig_popen(cmd, mode) unless block_given?
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  Open3.popen2(cmd) { |_i, o, _t| yield o }
         
     | 
| 
       61 
69 
     | 
    
         
             
                end
         
     | 
| 
       62 
70 
     | 
    
         
             
              end
         
     | 
| 
       63 
71 
     | 
    
         | 
| 
         @@ -82,8 +90,11 @@ class ::IO 
     | 
|
| 
       82 
90 
     | 
    
         
             
              # end
         
     | 
| 
       83 
91 
     | 
    
         | 
| 
       84 
92 
     | 
    
         
             
              alias_method :orig_gets, :gets
         
     | 
| 
       85 
     | 
    
         
            -
              def gets(sep = $/,  
     | 
| 
       86 
     | 
    
         
            -
                 
     | 
| 
      
 93 
     | 
    
         
            +
              def gets(sep = $/, _limit = nil, _chomp: nil)
         
     | 
| 
      
 94 
     | 
    
         
            +
                if sep.is_a?(Integer)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  sep = $/
         
     | 
| 
      
 96 
     | 
    
         
            +
                  _limit = sep
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
       87 
98 
     | 
    
         
             
                sep_size = sep.bytesize
         
     | 
| 
       88 
99 
     | 
    
         | 
| 
       89 
100 
     | 
    
         
             
                @gets_buffer ||= +''
         
     | 
| 
         @@ -92,12 +103,13 @@ class ::IO 
     | 
|
| 
       92 
103 
     | 
    
         
             
                  idx = @gets_buffer.index(sep)
         
     | 
| 
       93 
104 
     | 
    
         
             
                  return @gets_buffer.slice!(0, idx + sep_size) if idx
         
     | 
| 
       94 
105 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
                  data = readpartial(8192)
         
     | 
| 
       96 
     | 
    
         
            -
                  if data
         
     | 
| 
      
 106 
     | 
    
         
            +
                  if (data = readpartial(8192))
         
     | 
| 
       97 
107 
     | 
    
         
             
                    @gets_buffer << data
         
     | 
| 
       98 
108 
     | 
    
         
             
                  else
         
     | 
| 
       99 
109 
     | 
    
         
             
                    return nil if @gets_buffer.empty?
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    line = @gets_buffer.freeze
         
     | 
| 
      
 112 
     | 
    
         
            +
                    @gets_buffer = +''
         
     | 
| 
       101 
113 
     | 
    
         
             
                    return line
         
     | 
| 
       102 
114 
     | 
    
         
             
                  end
         
     | 
| 
       103 
115 
     | 
    
         
             
                end
         
     | 
| 
         @@ -130,6 +142,7 @@ class ::IO 
     | 
|
| 
       130 
142 
     | 
    
         
             
                  end
         
     | 
| 
       131 
143 
     | 
    
         
             
                end
         
     | 
| 
       132 
144 
     | 
    
         
             
                write s
         
     | 
| 
      
 145 
     | 
    
         
            +
                nil
         
     | 
| 
       133 
146 
     | 
    
         
             
              end
         
     | 
| 
       134 
147 
     | 
    
         | 
| 
       135 
148 
     | 
    
         
             
              # def readbyte
         
     | 
| 
         @@ -144,14 +157,13 @@ class ::IO 
     | 
|
| 
       144 
157 
     | 
    
         
             
              # def readlines(sep = $/, limit = nil, chomp: nil)
         
     | 
| 
       145 
158 
     | 
    
         
             
              # end
         
     | 
| 
       146 
159 
     | 
    
         | 
| 
       147 
     | 
    
         
            -
              def write_nonblock(string,  
     | 
| 
      
 160 
     | 
    
         
            +
              def write_nonblock(string, _options = {})
         
     | 
| 
       148 
161 
     | 
    
         
             
                # STDOUT << '>'
         
     | 
| 
       149 
162 
     | 
    
         
             
                write(string, 0)
         
     | 
| 
       150 
163 
     | 
    
         
             
              end
         
     | 
| 
       151 
164 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
              def read_nonblock(maxlen, buf = nil,  
     | 
| 
      
 165 
     | 
    
         
            +
              def read_nonblock(maxlen, buf = nil, _options = nil)
         
     | 
| 
       153 
166 
     | 
    
         
             
                # STDOUT << '<'
         
     | 
| 
       154 
167 
     | 
    
         
             
                buf ? readpartial(maxlen, buf) : readpartial(maxlen)
         
     | 
| 
       155 
168 
     | 
    
         
             
              end
         
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
169 
     | 
    
         
             
            end
         
     | 
| 
         @@ -4,6 +4,7 @@ require 'openssl' 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            import('./socket')
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            # Open ssl socket helper methods (to make it compatible with Socket API)
         
     | 
| 
       7 
8 
     | 
    
         
             
            class ::OpenSSL::SSL::SSLSocket
         
     | 
| 
       8 
9 
     | 
    
         
             
              def dont_linger
         
     | 
| 
       9 
10 
     | 
    
         
             
                io.dont_linger
         
     | 
| 
         @@ -16,4 +17,4 @@ class ::OpenSSL::SSL::SSLSocket 
     | 
|
| 
       16 
17 
     | 
    
         
             
              def reuse_addr
         
     | 
| 
       17 
18 
     | 
    
         
             
                io.reuse_addr
         
     | 
| 
       18 
19 
     | 
    
         
             
              end
         
     | 
| 
       19 
     | 
    
         
            -
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -4,31 +4,16 @@ require 'socket' 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            import('./io')
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            # Socket overrides (eventually rewritten in C)
         
     | 
| 
       7 
8 
     | 
    
         
             
            class ::Socket
         
     | 
| 
       8 
9 
     | 
    
         
             
              NO_EXCEPTION = { exception: false }.freeze
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
              def accept
         
     | 
| 
       11 
     | 
    
         
            -
                loop do
         
     | 
| 
       12 
     | 
    
         
            -
                  result, client_addr = accept_nonblock(NO_EXCEPTION)
         
     | 
| 
       13 
     | 
    
         
            -
                  case result
         
     | 
| 
       14 
     | 
    
         
            -
                  when Socket         then return result
         
     | 
| 
       15 
     | 
    
         
            -
                  when :wait_readable then read_watcher.await
         
     | 
| 
       16 
     | 
    
         
            -
                  else
         
     | 
| 
       17 
     | 
    
         
            -
                    raise "failed to accept (#{result.inspect})"
         
     | 
| 
       18 
     | 
    
         
            -
                  end
         
     | 
| 
       19 
     | 
    
         
            -
                end
         
     | 
| 
       20 
     | 
    
         
            -
              ensure
         
     | 
| 
       21 
     | 
    
         
            -
                @read_watcher&.stop
         
     | 
| 
       22 
     | 
    
         
            -
              end
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
11 
     | 
    
         
             
              def connect(remotesockaddr)
         
     | 
| 
       25 
12 
     | 
    
         
             
                loop do
         
     | 
| 
       26 
13 
     | 
    
         
             
                  result = connect_nonblock(remotesockaddr, NO_EXCEPTION)
         
     | 
| 
       27 
     | 
    
         
            -
                   
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                   
     | 
| 
       30 
     | 
    
         
            -
                  else                raise IOError
         
     | 
| 
       31 
     | 
    
         
            -
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return if result == 0
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  result == :wait_writable ? write_watcher.await : (raise IOError)
         
     | 
| 
       32 
17 
     | 
    
         
             
                end
         
     | 
| 
       33 
18 
     | 
    
         
             
              ensure
         
     | 
| 
       34 
19 
     | 
    
         
             
                @write_watcher&.stop
         
     | 
| 
         @@ -38,12 +23,9 @@ class ::Socket 
     | 
|
| 
       38 
23 
     | 
    
         
             
                outbuf ||= +''
         
     | 
| 
       39 
24 
     | 
    
         
             
                loop do
         
     | 
| 
       40 
25 
     | 
    
         
             
                  result = recv_nonblock(maxlen, flags, outbuf, NO_EXCEPTION)
         
     | 
| 
       41 
     | 
    
         
            -
                   
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                  else
         
     | 
| 
       45 
     | 
    
         
            -
                    return result
         
     | 
| 
       46 
     | 
    
         
            -
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  raise IOError unless result
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  result == :wait_readable ? read_watcher.await : (return result)
         
     | 
| 
       47 
29 
     | 
    
         
             
                end
         
     | 
| 
       48 
30 
     | 
    
         
             
              end
         
     | 
| 
       49 
31 
     | 
    
         | 
| 
         @@ -51,17 +33,15 @@ class ::Socket 
     | 
|
| 
       51 
33 
     | 
    
         
             
                @read_buffer ||= +''
         
     | 
| 
       52 
34 
     | 
    
         
             
                loop do
         
     | 
| 
       53 
35 
     | 
    
         
             
                  result = recvfrom_nonblock(maxlen, flags, @read_buffer, NO_EXCEPTION)
         
     | 
| 
       54 
     | 
    
         
            -
                   
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
                   
     | 
| 
       57 
     | 
    
         
            -
                  else                return result
         
     | 
| 
       58 
     | 
    
         
            -
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  raise IOError unless result
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  result == :wait_readable ? read_watcher.await : (return result)
         
     | 
| 
       59 
39 
     | 
    
         
             
                end
         
     | 
| 
       60 
40 
     | 
    
         
             
              ensure
         
     | 
| 
       61 
41 
     | 
    
         
             
                @read_watcher&.stop
         
     | 
| 
       62 
42 
     | 
    
         
             
              end
         
     | 
| 
       63 
43 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
              ZERO_LINGER = [0, 0].pack( 
     | 
| 
      
 44 
     | 
    
         
            +
              ZERO_LINGER = [0, 0].pack('ii').freeze
         
     | 
| 
       65 
45 
     | 
    
         | 
| 
       66 
46 
     | 
    
         
             
              def dont_linger
         
     | 
| 
       67 
47 
     | 
    
         
             
                setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, ZERO_LINGER)
         
     | 
| 
         @@ -83,46 +63,61 @@ class ::Socket 
     | 
|
| 
       83 
63 
     | 
    
         
             
              end
         
     | 
| 
       84 
64 
     | 
    
         
             
            end
         
     | 
| 
       85 
65 
     | 
    
         | 
| 
      
 66 
     | 
    
         
            +
            # Overide stock TCPSocket code by encapsulating a Socket instance
         
     | 
| 
       86 
67 
     | 
    
         
             
            class ::TCPSocket
         
     | 
| 
       87 
68 
     | 
    
         
             
              NO_EXCEPTION = { exception: false }.freeze
         
     | 
| 
       88 
69 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
              def  
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
              def initialize(remote_host, remote_port, local_host=nil, local_port=nil)
         
     | 
| 
      
 70 
     | 
    
         
            +
              def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
         
     | 
| 
       92 
71 
     | 
    
         
             
                @io = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
         
     | 
| 
       93 
72 
     | 
    
         
             
                if local_host && local_port
         
     | 
| 
       94 
     | 
    
         
            -
                   
     | 
| 
      
 73 
     | 
    
         
            +
                  addr = Addrinfo.tcp(local_host, local_port)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @io.bind(addr)
         
     | 
| 
       95 
75 
     | 
    
         
             
                end
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                return unless remote_host && remote_port
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                addr = Addrinfo.tcp(remote_host, remote_port)
         
     | 
| 
      
 80 
     | 
    
         
            +
                @io.connect(addr)
         
     | 
| 
       97 
81 
     | 
    
         
             
              end
         
     | 
| 
       98 
82 
     | 
    
         | 
| 
      
 83 
     | 
    
         
            +
              alias_method :orig_close, :close
         
     | 
| 
       99 
84 
     | 
    
         
             
              def close
         
     | 
| 
       100 
     | 
    
         
            -
                @io.close
         
     | 
| 
      
 85 
     | 
    
         
            +
                @io ? @io.close : orig_close
         
     | 
| 
       101 
86 
     | 
    
         
             
              end
         
     | 
| 
       102 
87 
     | 
    
         | 
| 
      
 88 
     | 
    
         
            +
              alias_method :orig_setsockopt, :setsockopt
         
     | 
| 
       103 
89 
     | 
    
         
             
              def setsockopt(*args)
         
     | 
| 
       104 
     | 
    
         
            -
                @io.setsockopt(*args)
         
     | 
| 
      
 90 
     | 
    
         
            +
                @io ? @io.setsockopt(*args) : orig_setsockopt(*args)
         
     | 
| 
       105 
91 
     | 
    
         
             
              end
         
     | 
| 
       106 
92 
     | 
    
         | 
| 
      
 93 
     | 
    
         
            +
              alias_method :orig_closed?, :closed?
         
     | 
| 
       107 
94 
     | 
    
         
             
              def closed?
         
     | 
| 
       108 
     | 
    
         
            -
                @io.closed?
         
     | 
| 
      
 95 
     | 
    
         
            +
                @io ? @io.closed? : orig_closed?
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
              def dont_linger
         
     | 
| 
      
 99 
     | 
    
         
            +
                setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_LINGER, ::Socket::ZERO_LINGER)
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              def no_delay
         
     | 
| 
      
 103 
     | 
    
         
            +
                setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              def reuse_addr
         
     | 
| 
      
 107 
     | 
    
         
            +
                setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
         
     | 
| 
       109 
108 
     | 
    
         
             
              end
         
     | 
| 
       110 
109 
     | 
    
         
             
            end
         
     | 
| 
       111 
110 
     | 
    
         | 
| 
      
 111 
     | 
    
         
            +
            # Override stock TCPServer code by encapsulating a Socket instance.
         
     | 
| 
       112 
112 
     | 
    
         
             
            class ::TCPServer
         
     | 
| 
       113 
     | 
    
         
            -
               
     | 
| 
      
 113 
     | 
    
         
            +
              def initialize(hostname = nil, port = 0)
         
     | 
| 
      
 114 
     | 
    
         
            +
                @io = Socket.new Socket::AF_INET, Socket::SOCK_STREAM
         
     | 
| 
      
 115 
     | 
    
         
            +
                @io.bind(Addrinfo.tcp(hostname, port))
         
     | 
| 
      
 116 
     | 
    
         
            +
                @io.listen(0)
         
     | 
| 
      
 117 
     | 
    
         
            +
              end
         
     | 
| 
       114 
118 
     | 
    
         | 
| 
      
 119 
     | 
    
         
            +
              alias_method :orig_accept, :accept
         
     | 
| 
       115 
120 
     | 
    
         
             
              def accept
         
     | 
| 
       116 
     | 
    
         
            -
                 
     | 
| 
       117 
     | 
    
         
            -
                  result, client_addr = accept_nonblock(NO_EXCEPTION)
         
     | 
| 
       118 
     | 
    
         
            -
                  case result
         
     | 
| 
       119 
     | 
    
         
            -
                  when TCPSocket         then return result
         
     | 
| 
       120 
     | 
    
         
            -
                  when :wait_readable then read_watcher.await
         
     | 
| 
       121 
     | 
    
         
            -
                  else
         
     | 
| 
       122 
     | 
    
         
            -
                    raise "failed to accept (#{result.inspect})"
         
     | 
| 
       123 
     | 
    
         
            -
                  end
         
     | 
| 
       124 
     | 
    
         
            -
                end
         
     | 
| 
       125 
     | 
    
         
            -
              ensure
         
     | 
| 
       126 
     | 
    
         
            -
                @read_watcher&.stop
         
     | 
| 
      
 121 
     | 
    
         
            +
                @io ? @io.accept : orig_accept
         
     | 
| 
       127 
122 
     | 
    
         
             
              end
         
     | 
| 
       128 
     | 
    
         
            -
            end
         
     | 
| 
      
 123 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/polyphony/http.rb
    CHANGED
    
    | 
         @@ -3,11 +3,12 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require_relative '../polyphony'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module Polyphony
         
     | 
| 
      
 6 
     | 
    
         
            +
              # HTTP imports (loaded dynamically)
         
     | 
| 
       6 
7 
     | 
    
         
             
              module HTTP
         
     | 
| 
       7 
8 
     | 
    
         
             
                auto_import(
         
     | 
| 
       8 
     | 
    
         
            -
                  Agent: 
     | 
| 
       9 
     | 
    
         
            -
                  Rack: 
     | 
| 
       10 
     | 
    
         
            -
                  Server: 
     | 
| 
      
 9 
     | 
    
         
            +
                  Agent:  './http/agent',
         
     | 
| 
      
 10 
     | 
    
         
            +
                  Rack:   './http/server/rack',
         
     | 
| 
      
 11 
     | 
    
         
            +
                  Server: './http/server'
         
     | 
| 
       11 
12 
     | 
    
         
             
                )
         
     | 
| 
       12 
13 
     | 
    
         
             
              end
         
     | 
| 
       13 
14 
     | 
    
         
             
            end
         
     | 
    
        data/lib/polyphony/http/agent.rb
    CHANGED
    
    | 
         @@ -9,6 +9,7 @@ require 'json' 
     | 
|
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
            ResourcePool = import('../core/resource_pool')
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
            # Response mixin
         
     | 
| 
       12 
13 
     | 
    
         
             
            module ResponseMixin
         
     | 
| 
       13 
14 
     | 
    
         
             
              def body
         
     | 
| 
       14 
15 
     | 
    
         
             
                self[:body]
         
     | 
| 
         @@ -49,7 +50,6 @@ class Agent 
     | 
|
| 
       49 
50 
     | 
    
         
             
                request(url, opts.merge(method: :POST))
         
     | 
| 
       50 
51 
     | 
    
         
             
              end
         
     | 
| 
       51 
52 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
53 
     | 
    
         
             
              def request(url, opts = OPTS_DEFAULT)
         
     | 
| 
       54 
54 
     | 
    
         
             
                ctx = request_ctx(url, opts)
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
         @@ -66,19 +66,20 @@ class Agent 
     | 
|
| 
       66 
66 
     | 
    
         | 
| 
       67 
67 
     | 
    
         
             
              def redirect(url, ctx, opts)
         
     | 
| 
       68 
68 
     | 
    
         
             
                url = case url
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
      
 69 
     | 
    
         
            +
                      when /^http(?:s)?\:\/\//
         
     | 
| 
      
 70 
     | 
    
         
            +
                        url
         
     | 
| 
      
 71 
     | 
    
         
            +
                      when /^\/\/(.+)$/
         
     | 
| 
      
 72 
     | 
    
         
            +
                        ctx[:uri].scheme + url
         
     | 
| 
      
 73 
     | 
    
         
            +
                      when /^\//
         
     | 
| 
      
 74 
     | 
    
         
            +
                        format(
         
     | 
| 
      
 75 
     | 
    
         
            +
                          '%<scheme>s://%<host>s%<url>s',
         
     | 
| 
      
 76 
     | 
    
         
            +
                          scheme: ctx[:uri].scheme,
         
     | 
| 
      
 77 
     | 
    
         
            +
                          host:   ctx[:uri].host,
         
     | 
| 
      
 78 
     | 
    
         
            +
                          url:    url
         
     | 
| 
      
 79 
     | 
    
         
            +
                        )
         
     | 
| 
      
 80 
     | 
    
         
            +
                      else
         
     | 
| 
      
 81 
     | 
    
         
            +
                        ctx[:uri] + url
         
     | 
| 
      
 82 
     | 
    
         
            +
                      end
         
     | 
| 
       82 
83 
     | 
    
         | 
| 
       83 
84 
     | 
    
         
             
                request(url, opts)
         
     | 
| 
       84 
85 
     | 
    
         
             
              end
         
     | 
| 
         @@ -88,14 +89,14 @@ class Agent 
     | 
|
| 
       88 
89 
     | 
    
         
             
                  method: opts[:method] || :GET,
         
     | 
| 
       89 
90 
     | 
    
         
             
                  uri:    url_to_uri(url, opts),
         
     | 
| 
       90 
91 
     | 
    
         
             
                  opts:   opts,
         
     | 
| 
       91 
     | 
    
         
            -
                  retry:  0 
     | 
| 
      
 92 
     | 
    
         
            +
                  retry:  0
         
     | 
| 
       92 
93 
     | 
    
         
             
                }
         
     | 
| 
       93 
94 
     | 
    
         
             
              end
         
     | 
| 
       94 
95 
     | 
    
         | 
| 
       95 
96 
     | 
    
         
             
              def url_to_uri(url, opts)
         
     | 
| 
       96 
97 
     | 
    
         
             
                uri = URI(url)
         
     | 
| 
       97 
98 
     | 
    
         
             
                if opts[:query]
         
     | 
| 
       98 
     | 
    
         
            -
                  query = opts[:query].map { |k, v| "#{k}=#{v}" }.join( 
     | 
| 
      
 99 
     | 
    
         
            +
                  query = opts[:query].map { |k, v| "#{k}=#{v}" }.join('&')
         
     | 
| 
       99 
100 
     | 
    
         
             
                  if uri.query
         
     | 
| 
       100 
101 
     | 
    
         
             
                    v.query = "#{uri.query}&#{query}"
         
     | 
| 
       101 
102 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -109,23 +110,22 @@ class Agent 
     | 
|
| 
       109 
110 
     | 
    
         
             
                key = uri_key(ctx[:uri])
         
     | 
| 
       110 
111 
     | 
    
         
             
                @pools[key].acquire do |state|
         
     | 
| 
       111 
112 
     | 
    
         
             
                  cancel_after(10) do
         
     | 
| 
       112 
     | 
    
         
            -
                    state[:socket] ||= 
     | 
| 
      
 113 
     | 
    
         
            +
                    state[:socket] ||= connect(key)
         
     | 
| 
       113 
114 
     | 
    
         
             
                    state[:protocol_method] ||= protocol_method(state[:socket], ctx)
         
     | 
| 
       114 
115 
     | 
    
         
             
                    send(state[:protocol_method], state, ctx)
         
     | 
| 
       115 
     | 
    
         
            -
                  rescue => e
         
     | 
| 
       116 
     | 
    
         
            -
                    state[:socket]&.close 
     | 
| 
      
 116 
     | 
    
         
            +
                  rescue Exception => e
         
     | 
| 
      
 117 
     | 
    
         
            +
                    state[:socket]&.close
         
     | 
| 
       117 
118 
     | 
    
         
             
                    state.clear
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                     
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
                    end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    raise e unless ctx[:retry] < 3
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    ctx[:retry] += 1
         
     | 
| 
      
 123 
     | 
    
         
            +
                    do_request(ctx)
         
     | 
| 
       124 
124 
     | 
    
         
             
                  end
         
     | 
| 
       125 
125 
     | 
    
         
             
                end
         
     | 
| 
       126 
126 
     | 
    
         
             
              end
         
     | 
| 
       127 
127 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
              def protocol_method(socket,  
     | 
| 
      
 128 
     | 
    
         
            +
              def protocol_method(socket, _ctx)
         
     | 
| 
       129 
129 
     | 
    
         
             
                if socket.is_a?(::OpenSSL::SSL::SSLSocket) && (socket.alpn_protocol == 'h2')
         
     | 
| 
       130 
130 
     | 
    
         
             
                  :do_http2
         
     | 
| 
       131 
131 
     | 
    
         
             
                else
         
     | 
| 
         @@ -142,32 +142,31 @@ class Agent 
     | 
|
| 
       142 
142 
     | 
    
         
             
                request = format_http1_request(ctx)
         
     | 
| 
       143 
143 
     | 
    
         | 
| 
       144 
144 
     | 
    
         
             
                state[:socket] << request
         
     | 
| 
       145 
     | 
    
         
            -
                 
     | 
| 
       146 
     | 
    
         
            -
                  parser << state[:socket].readpartial(8192)
         
     | 
| 
       147 
     | 
    
         
            -
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
                parser << state[:socket].readpartial(8192) until done
         
     | 
| 
       148 
146 
     | 
    
         | 
| 
       149 
147 
     | 
    
         
             
                {
         
     | 
| 
       150 
     | 
    
         
            -
                  protocol: 
     | 
| 
       151 
     | 
    
         
            -
                  status_code: 
     | 
| 
       152 
     | 
    
         
            -
                  headers: 
     | 
| 
       153 
     | 
    
         
            -
                  body: 
     | 
| 
      
 148 
     | 
    
         
            +
                  protocol:    'http1.1',
         
     | 
| 
      
 149 
     | 
    
         
            +
                  status_code: parser.status_code,
         
     | 
| 
      
 150 
     | 
    
         
            +
                  headers:     parser.headers,
         
     | 
| 
      
 151 
     | 
    
         
            +
                  body:        body
         
     | 
| 
       154 
152 
     | 
    
         
             
                }
         
     | 
| 
       155 
153 
     | 
    
         
             
              end
         
     | 
| 
       156 
154 
     | 
    
         | 
| 
       157 
155 
     | 
    
         
             
              def do_http2(state, ctx)
         
     | 
| 
       158 
156 
     | 
    
         
             
                unless state[:http2_client]
         
     | 
| 
       159 
     | 
    
         
            -
                  socket 
     | 
| 
       160 
     | 
    
         
            -
                  client 
     | 
| 
      
 157 
     | 
    
         
            +
                  socket = state[:socket]
         
     | 
| 
      
 158 
     | 
    
         
            +
                  client = HTTP2::Client.new
         
     | 
| 
      
 159 
     | 
    
         
            +
                  client.on(:frame) { |bytes| socket << bytes }
         
     | 
| 
       161 
160 
     | 
    
         
             
                  state[:http2_client] = client
         
     | 
| 
       162 
161 
     | 
    
         
             
                end
         
     | 
| 
       163 
162 
     | 
    
         | 
| 
       164 
163 
     | 
    
         
             
                stream = state[:http2_client].new_stream # allocate new stream
         
     | 
| 
       165 
164 
     | 
    
         | 
| 
       166 
165 
     | 
    
         
             
                headers = {
         
     | 
| 
       167 
     | 
    
         
            -
                  ':method' 
     | 
| 
       168 
     | 
    
         
            -
                  ':scheme' 
     | 
| 
       169 
     | 
    
         
            -
                  ':authority' 
     | 
| 
       170 
     | 
    
         
            -
                  ':path' 
     | 
| 
      
 166 
     | 
    
         
            +
                  ':method'    => ctx[:method].to_s,
         
     | 
| 
      
 167 
     | 
    
         
            +
                  ':scheme'    => ctx[:uri].scheme,
         
     | 
| 
      
 168 
     | 
    
         
            +
                  ':authority' => [ctx[:uri].host, ctx[:uri].port].join(':'),
         
     | 
| 
      
 169 
     | 
    
         
            +
                  ':path'      => ctx[:uri].request_uri
         
     | 
| 
       171 
170 
     | 
    
         
             
                }
         
     | 
| 
       172 
171 
     | 
    
         
             
                headers.merge!(ctx[:opts][:headers]) if ctx[:opts][:headers]
         
     | 
| 
       173 
172 
     | 
    
         
             
                puts "* proxy request headers: #{headers.inspect}"
         
     | 
| 
         @@ -185,35 +184,47 @@ class Agent 
     | 
|
| 
       185 
184 
     | 
    
         | 
| 
       186 
185 
     | 
    
         
             
                stream.on(:headers) { |h| headers = h.to_h }
         
     | 
| 
       187 
186 
     | 
    
         
             
                stream.on(:data) { |c| body << c }
         
     | 
| 
       188 
     | 
    
         
            -
                stream.on(:close)  
     | 
| 
      
 187 
     | 
    
         
            +
                stream.on(:close) do
         
     | 
| 
       189 
188 
     | 
    
         
             
                  done = true
         
     | 
| 
       190 
189 
     | 
    
         
             
                  return {
         
     | 
| 
       191 
     | 
    
         
            -
                    protocol: 
     | 
| 
       192 
     | 
    
         
            -
                    status_code: 
     | 
| 
       193 
     | 
    
         
            -
                    headers: 
     | 
| 
       194 
     | 
    
         
            -
                    body: 
     | 
| 
      
 190 
     | 
    
         
            +
                    protocol:    'http2',
         
     | 
| 
      
 191 
     | 
    
         
            +
                    status_code: headers && headers[':status'].to_i,
         
     | 
| 
      
 192 
     | 
    
         
            +
                    headers:     headers || {},
         
     | 
| 
      
 193 
     | 
    
         
            +
                    body:        body
         
     | 
| 
       195 
194 
     | 
    
         
             
                  }
         
     | 
| 
       196 
     | 
    
         
            -
                 
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
       197 
196 
     | 
    
         | 
| 
       198 
     | 
    
         
            -
                while data = state[:socket].readpartial(8192)
         
     | 
| 
      
 197 
     | 
    
         
            +
                while (data = state[:socket].readpartial(8192))
         
     | 
| 
       199 
198 
     | 
    
         
             
                  state[:http2_client] << data
         
     | 
| 
       200 
199 
     | 
    
         
             
                end
         
     | 
| 
       201 
200 
     | 
    
         
             
              ensure
         
     | 
| 
       202 
     | 
    
         
            -
                 
     | 
| 
      
 201 
     | 
    
         
            +
                stream.close unless done
         
     | 
| 
       203 
202 
     | 
    
         
             
              end
         
     | 
| 
       204 
203 
     | 
    
         | 
| 
       205 
     | 
    
         
            -
              HTTP1_REQUEST =  
     | 
| 
      
 204 
     | 
    
         
            +
              HTTP1_REQUEST = <<~HTTP.gsub("\n", "\r\n")
         
     | 
| 
      
 205 
     | 
    
         
            +
                %<method>s %<request>s HTTP/1.1
         
     | 
| 
      
 206 
     | 
    
         
            +
                Host: %<host>s
         
     | 
| 
      
 207 
     | 
    
         
            +
                %<headers>s
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
              HTTP
         
     | 
| 
       206 
210 
     | 
    
         | 
| 
       207 
211 
     | 
    
         
             
              def format_http1_request(ctx)
         
     | 
| 
       208 
     | 
    
         
            -
                headers = ctx 
     | 
| 
      
 212 
     | 
    
         
            +
                headers = format_headers(ctx)
         
     | 
| 
       209 
213 
     | 
    
         
             
                puts "* proxy request headers: #{headers.inspect}"
         
     | 
| 
       210 
214 
     | 
    
         | 
| 
       211 
     | 
    
         
            -
                 
     | 
| 
       212 
     | 
    
         
            -
                   
     | 
| 
       213 
     | 
    
         
            -
                   
     | 
| 
       214 
     | 
    
         
            -
                   
     | 
| 
       215 
     | 
    
         
            -
                   
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
      
 215 
     | 
    
         
            +
                format(
         
     | 
| 
      
 216 
     | 
    
         
            +
                  HTTP1_REQUEST,
         
     | 
| 
      
 217 
     | 
    
         
            +
                  method:  ctx[:method],
         
     | 
| 
      
 218 
     | 
    
         
            +
                  request: ctx[:uri].request_uri,
         
     | 
| 
      
 219 
     | 
    
         
            +
                  host:    ctx[:uri].host,
         
     | 
| 
      
 220 
     | 
    
         
            +
                  headers: headers
         
     | 
| 
      
 221 
     | 
    
         
            +
                )
         
     | 
| 
      
 222 
     | 
    
         
            +
              end
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
              def format_headers(headers)
         
     | 
| 
      
 225 
     | 
    
         
            +
                return nil unless ctx[:opts][:headers]
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                headers.map { |k, v| "#{k}: #{v}\r\n" }.join
         
     | 
| 
       217 
228 
     | 
    
         
             
              end
         
     | 
| 
       218 
229 
     | 
    
         | 
| 
       219 
230 
     | 
    
         
             
              def uri_key(uri)
         
     | 
| 
         @@ -224,7 +235,7 @@ class Agent 
     | 
|
| 
       224 
235 
     | 
    
         
             
                }
         
     | 
| 
       225 
236 
     | 
    
         
             
              end
         
     | 
| 
       226 
237 
     | 
    
         | 
| 
       227 
     | 
    
         
            -
              SECURE_OPTS = { secure: true, alpn_protocols: ['h2', 'http/1.1'] }
         
     | 
| 
      
 238 
     | 
    
         
            +
              SECURE_OPTS = { secure: true, alpn_protocols: ['h2', 'http/1.1'] }.freeze
         
     | 
| 
       228 
239 
     | 
    
         | 
| 
       229 
240 
     | 
    
         
             
              def connect(key)
         
     | 
| 
       230 
241 
     | 
    
         
             
                case key[:scheme]
         
     | 
| 
         @@ -236,4 +247,4 @@ class Agent 
     | 
|
| 
       236 
247 
     | 
    
         
             
                  raise "Invalid scheme #{key[:scheme].inspect}"
         
     | 
| 
       237 
248 
     | 
    
         
             
                end
         
     | 
| 
       238 
249 
     | 
    
         
             
              end
         
     | 
| 
       239 
     | 
    
         
            -
            end
         
     | 
| 
      
 250 
     | 
    
         
            +
            end
         
     |