File: ssh.rb

package info (click to toggle)
ruby-httpx 1.7.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,816 kB
  • sloc: ruby: 12,209; makefile: 4
file content (94 lines) | stat: -rw-r--r-- 2,896 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# frozen_string_literal: true

require "httpx/plugins/proxy"

module HTTPX
  module Plugins
    module Proxy
      module SSH
        class << self
          def load_dependencies(*)
            require "net/ssh/gateway"
          end
        end

        module OptionsMethods
          private

          def option_proxy(value)
            Hash[value]
          end
        end

        module InstanceMethods
          def request(*args, **options)
            raise ArgumentError, "must perform at least one request" if args.empty?

            requests = args.first.is_a?(Request) ? args : build_requests(*args, options)

            request = requests.first or return super

            request_options = request.options

            return super unless request_options.proxy

            ssh_options = request_options.proxy
            ssh_uris = ssh_options.delete(:uri)
            ssh_uri = URI.parse(ssh_uris.shift)

            return super unless ssh_uri.scheme == "ssh"

            ssh_username = ssh_options.delete(:username)
            ssh_options[:port] ||= ssh_uri.port || 22
            if request_options.debug
              ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
            end

            request_uri = URI(requests.first.uri)
            @_gateway = Net::SSH::Gateway.new(ssh_uri.host, ssh_username, ssh_options)
            begin
              @_gateway.open(request_uri.host, request_uri.port) do |local_port|
                io = build_gateway_socket(local_port, request_uri, request_options)
                super(*args, **options.merge(io: io))
              end
            ensure
              @_gateway.shutdown!
            end
          end

          private

          def build_gateway_socket(port, request_uri, options)
            case request_uri.scheme
            when "https"
              ctx = OpenSSL::SSL::SSLContext.new
              ctx_options = SSL::TLS_OPTIONS.merge(options.ssl)
              ctx.set_params(ctx_options) unless ctx_options.empty?
              sock = TCPSocket.open("localhost", port)
              io = OpenSSL::SSL::SSLSocket.new(sock, ctx)
              io.hostname = request_uri.host
              io.sync_close = true
              io.connect
              io.post_connection_check(request_uri.host) if ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
              io
            when "http"
              TCPSocket.open("localhost", port)
            else
              raise TypeError, "unexpected scheme: #{request_uri.scheme}"
            end
          end
        end

        module ConnectionMethods
          # should not coalesce connections here, as the IP is the IP of the proxy
          def coalescable?(*)
            return super unless @options.proxy

            false
          end
        end
      end
    end
    register_plugin :"proxy/ssh", Proxy::SSH
  end
end