File: http.rb

package info (click to toggle)
libnet-ssh-ruby 1.1.2-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 3,472 kB
  • ctags: 2,465
  • sloc: ruby: 10,848; makefile: 17
file content (126 lines) | stat: -rw-r--r-- 4,220 bytes parent folder | download | duplicates (3)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#--
# =============================================================================
# Copyright (c) 2004,2005 Jamis Buck (jamis@37signals.com)
# All rights reserved.
#
# This source file is distributed as part of the Net::SSH Secure Shell Client
# library for Ruby. This file (and the library as a whole) may be used only as
# allowed by either the BSD license, or the Ruby license (or, by association
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SSH
# distribution for the texts of these licenses.
# -----------------------------------------------------------------------------
# net-ssh website : http://net-ssh.rubyforge.org
# project website: http://rubyforge.org/projects/net-ssh
# =============================================================================
#++

require 'socket'
require 'base64'
require 'net/ssh/proxy/errors'

module Net
  module SSH
    module Proxy

      # An implementation of a socket factory that returns a socket which
      # will tunnel the connection through an HTTP proxy. It allows explicit
      # specification of the user and password, but if none are given it
      # will look in the HTTP_PROXY_USER/HTTP_PROXY_PASSWORD and
      # CONNECT_USER/CONNECT_PASSWORD environment variables as well.
      class HTTP

        # Create a new socket factory that tunnels via the given host and
        # port.
        def initialize( proxy_host, proxy_port=80, options={} )
          @proxy_host = proxy_host
          @proxy_port = proxy_port
          @options = options
        end

        # Return a new socket connected to the given host and port via the
        # proxy that was requested when the socket factory was instantiated.
        def open( host, port )
          connect_string = "CONNECT #{host}:#{port} HTTP/1.0"

          socket = TCPSocket.new( @proxy_host, @proxy_port )
          socket.puts connect_string
          socket.puts

          resp = parse_response( socket )

          return socket if resp[:code] == 200

          socket.shutdown
          raise ConnectError, resp.inspect unless resp[:code] == 407

          user = proxy_user
          passwd = proxy_password

          raise UnauthorizedError, "no proxy user given" unless user

          auth = resp[:headers]["Proxy-Authenticate"]
          scheme, parms = auth.split( / /, 2 )

          case scheme
            when "Basic"
              credentials =
                Base64.encode64( "#{user}:#{passwd}" ).gsub( /\n/, "" )
            else
              raise NotImplementedError,
                "authorization scheme #{scheme.inspect} is not supported"
          end

          socket = TCPSocket.new( @proxy_host, @proxy_port )
          socket.puts connect_string
          socket.puts "Proxy-Authorization: #{scheme} #{credentials}"
          socket.puts

          resp = parse_response( socket )

          raise ConnectError, resp.inspect if resp[:code] != 200

          return socket
        end

        def parse_response( socket )
          version, code, reason = socket.gets.chomp.split( / /, 3 )
          headers = {}

          while ( line = socket.gets.chomp ) != ""
            name, value = line.split( /:/, 2 ).map { |v| v.strip }
            headers[ name ] = value
          end

          if headers[ "Content-Length" ]
            body = socket.read( headers[ "Content-Length" ].to_i )
          end

          return { :version => version,
                   :code => code.to_i,
                   :reason => reason,
                   :headers => headers,
                   :body => body }
        end
        private :parse_response

        def proxy_user
          return @options[ :user ] if @options[ :user ]
          return ENV['HTTP_PROXY_USER'] if ENV['HTTP_PROXY_USER']
          return ENV['CONNECT_USER'] if ENV['CONNECT_USER']
          return nil
        end
        private :proxy_user

        def proxy_password
          return @options[ :password ] if @options[ :password ]
          return ENV['HTTP_PROXY_PASSWORD'] if ENV['HTTP_PROXY_PASSWORD']
          return ENV['CONNECT_PASSWORD'] if ENV['CONNECT_PASSWORD']
          return ""
        end
        private :proxy_password

      end

    end
  end
end