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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
# frozen_string_literal: true
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
require 'net/https'
require 'ipaddr'
module Selenium
module WebDriver
module Remote
module Http
# @api private
class Default < Common
attr_writer :proxy
attr_accessor :open_timeout
attr_accessor :read_timeout
# Initializes object.
# Warning: Setting {#open_timeout} to non-nil values will cause a separate thread to spawn.
# Debuggers that freeze the process will not be able to evaluate any operations if that happens.
# @param [Numeric] open_timeout - Open timeout to apply to HTTP client.
# @param [Numeric] read_timeout - Read timeout (seconds) to apply to HTTP client.
def initialize(open_timeout: nil, read_timeout: nil)
@open_timeout = open_timeout
@read_timeout = read_timeout
end
# Maintaining backward compatibility.
# @param [Numeric] value - Timeout in seconds to apply to both open timeout and read timeouts.
# @deprecated Please set the specific desired timeout {#read_timeout} or {#open_timeout} directly.
def timeout=(value)
WebDriver.logger.deprecate ':timeout=', '#read_timeout= and #open_timeout='
self.open_timeout = value
self.read_timeout = value
end
def close
@http&.finish
end
private
def http
@http ||= begin
http = new_http_client
if server_url.scheme == 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
# Defaulting open_timeout to nil to be consistent with Ruby 2.2 and earlier.
http.open_timeout = open_timeout
http.read_timeout = read_timeout if read_timeout
http.start
end
end
MAX_RETRIES = 3
def request(verb, url, headers, payload, redirects = 0)
retries = 0
begin
request = new_request_for(verb, url, headers, payload)
response = response_for(request)
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EADDRINUSE
# a retry is sometimes needed on Windows XP where we may quickly
# run out of ephemeral ports
#
# A more robust solution is bumping the MaxUserPort setting
# as described here:
#
# http://msdn.microsoft.com/en-us/library/aa560610%28v=bts.20%29.aspx
raise if retries >= MAX_RETRIES
retries += 1
sleep 2
retry
rescue Errno::EADDRNOTAVAIL => ex
# a retry is sometimes needed when the port becomes temporarily unavailable
raise if retries >= MAX_RETRIES
retries += 1
sleep 2
retry
rescue Errno::ECONNREFUSED => ex
raise ex.class, "using proxy: #{proxy.http}" if use_proxy?
raise
end
if response.is_a? Net::HTTPRedirection
raise Error::WebDriverError, 'too many redirects' if redirects >= MAX_REDIRECTS
request(:get, URI.parse(response['Location']), DEFAULT_HEADERS.dup, nil, redirects + 1)
else
create_response response.code, response.body, response.content_type
end
end
def new_request_for(verb, url, headers, payload)
req = Net::HTTP.const_get(verb.to_s.capitalize).new(url.path, headers)
req.basic_auth server_url.user, server_url.password if server_url.userinfo
req.body = payload if payload
req
end
def response_for(request)
http.request request
end
def new_http_client
if use_proxy?
url = @proxy.http
raise Error::WebDriverError, "expected HTTP proxy, got #{@proxy.inspect}" unless proxy.respond_to?(:http) && url
proxy = URI.parse(url)
clazz = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password)
clazz.new(server_url.host, server_url.port)
else
Net::HTTP.new server_url.host, server_url.port
end
end
def proxy
@proxy ||= begin
proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
if proxy
proxy = "http://#{proxy}" unless proxy.start_with?('http://')
Proxy.new(http: proxy, no_proxy: no_proxy)
end
end
end
def use_proxy?
return false if proxy.nil?
if proxy.no_proxy
ignored = proxy.no_proxy.split(',').any? do |host|
host == '*' ||
host == server_url.host || (
begin
IPAddr.new(host).include?(server_url.host)
rescue ArgumentError
false
end
)
end
!ignored
else
true
end
end
end # Default
end # Http
end # Remote
end # WebDriver
end # Selenium
|