Class: Maze::Proxy

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/maze/proxy.rb

Overview

Provides the ability to run a proxy server, using the WEBrick proxy server. Note that for an HTTPS proxy a self-signed certificate will be used. If using curl, for example, this means having to employ the –proxy-insecure option.

Constant Summary collapse

PORT =

There are some constraints on the port from driving remote browsers on BrowserStack. E.g. the ports/ranges that Safari will access on “localhost” urls are restricted to the following:

80, 3000, 4000, 5000, 8000, 8080 or 9000-9999 [ from https://stackoverflow.com/a/28678652 ]
9000

Instance Method Summary collapse

Constructor Details

#initializeProxy

Returns a new instance of Proxy.



22
23
24
# File 'lib/maze/proxy.rb', line 22

def initialize
  @hosts = []
end

Instance Method Details

#handled_host?(host) ⇒ Boolean

Whether the proxy handled a request for the given host

Parameters:

  • host (String)

    The destination host to test for

Returns:

  • (Boolean)


29
30
31
# File 'lib/maze/proxy.rb', line 29

def handled_host?(host)
  @hosts.include? host
end

#running?Boolean

Whether the proxy server thread is running

Returns:

  • (Boolean)


35
36
37
# File 'lib/maze/proxy.rb', line 35

def running?
  @thread&.alive?
end

#start(protocol, authenticated = false) ⇒ Object

Starts the WEBrick proxy in a separate thread If authentication if requested, then the credentials used are simply ‘user’ with ‘password’.

Parameters:

  • protocol (Symbol)

    :Http or Https

  • authenticated (Boolean) (defaults to: false)

    Whether basic authentication should be applied.



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
# File 'lib/maze/proxy.rb', line 44

def start(protocol, authenticated = false)
  @hosts.clear

  attempts = 0
  $logger.info 'Starting proxy server'
  loop do
    @thread = Thread.new do

      handler = proc do |req, res|
        req.header['host'].each { |host| @hosts.append(host) }
      end
      config = {
        Logger: $logger,
        Port: PORT,
        ProxyContentHandler: handler
      }

      # Setup protocol
      if protocol == :Http
        $logger.info 'Starting HTTP proxy'
      elsif protocol == :Https
        $logger.info 'Starting HTTPS proxy'
        cert_name = [
          %w[CN localhost]
        ]
        config[:SSLCertName] = cert_name
        config[:SSLEnable] = true
      else
        raise "Unsupported protocol #{protocol}: :Http and :Https are supported"
      end

      # Authentication required?
      if authenticated
        # Apache compatible Password manager
        htpasswd = WEBrick::HTTPAuth::Htpasswd.new File.expand_path('htpasswd', __dir__)
        htpasswd.set_passwd 'Proxy Realm', 'user', 'password'
        htpasswd.flush
        authenticator = WEBrick::HTTPAuth::ProxyBasicAuth.new Realm: 'Proxy Realm',
                                                              UserDB: htpasswd
        config[:ProxyAuthProc] = authenticator.method(:authenticate).to_proc
      end

      # Create and start the proxy
      proxy = WEBrick::HTTPProxyServer.new config
      proxy.start
    rescue StandardError => e
      Bugsnag.notify e
      $logger.warn "Failed to start proxy server: #{e.message}"
    ensure
      proxy&.shutdown
    end

    # Need a short sleep here as a dying thread is still alive momentarily
    sleep 1
    break if running?

    # Bail out after 3 attempts
    attempts += 1
    raise 'Too many failed attempts to start proxy server' if attempts == 3

    # Failed to start - sleep before retrying
    $logger.info 'Retrying in 5 seconds'
    sleep 5
  end
end

#stopObject

Stops the WEBrick proxy thread if it’s running



111
112
113
114
# File 'lib/maze/proxy.rb', line 111

def stop
  @thread&.kill if @thread&.alive?
  @thread = nil
end