diff --git a/lib/nexpose/api_request.rb b/lib/nexpose/api_request.rb index 73a25704..775dc66a 100644 --- a/lib/nexpose/api_request.rb +++ b/lib/nexpose/api_request.rb @@ -5,9 +5,6 @@ class APIRequest attr_reader :http attr_reader :uri attr_reader :headers - attr_reader :retry_count - attr_reader :time_out - attr_reader :pause attr_reader :req attr_reader :res @@ -20,7 +17,7 @@ class APIRequest attr_reader :raw_response attr_reader :raw_response_data - def initialize(req, url, api_version='1.1') + def initialize(req, url, api_version = '1.1') @url = url @req = req @api_version = api_version @@ -29,10 +26,6 @@ def initialize(req, url, api_version='1.1') end def prepare_http_client - @retry_count = 0 - @retry_count_max = 10 - @time_out = 30 - @pause = 2 @uri = URI.parse(@url) @http = Net::HTTP.new(@uri.host, @uri.port) @http.use_ssl = true @@ -46,11 +39,12 @@ def prepare_http_client @success = false end - def execute + def execute(options = {}) @conn_tries = 0 begin prepare_http_client + @http.read_timeout = options[:timeout] if options.key? :timeout @raw_response = @http.post(@uri.path, @req, @headers) @raw_response_data = @raw_response.read_body @res = parse_xml(@raw_response_data) @@ -93,7 +87,7 @@ def execute @conn_tries += 1 retry end - rescue ::ArgumentError, ::NoMethodError + rescue ::ArgumentError, ::NoMethodError => e if @conn_tries < 5 @conn_tries += 1 retry @@ -101,9 +95,10 @@ def execute rescue ::Timeout::Error if @conn_tries < 5 @conn_tries += 1 - retry + # If an explicit timeout is set, don't retry. + retry unless options.key? :timeout end - @error = 'Nexpose host did not respond.' + @error = "Nexpose did not respond within #{@http.read_timeout} seconds." rescue ::Errno::EHOSTUNREACH, ::Errno::ENETDOWN, ::Errno::ENETUNREACH, ::Errno::ENETRESET, ::Errno::EHOSTDOWN, ::Errno::EACCES, ::Errno::EINVAL, ::Errno::EADDRNOTAVAIL @error = 'Nexpose host is unreachable.' # Handle console-level interrupts @@ -127,12 +122,11 @@ def attributes(*args) @res.root.attributes(*args) end - def self.execute(url, req, api_version='1.1') + def self.execute(url, req, api_version='1.1', options = {}) obj = self.new(req, url, api_version) - obj.execute + obj.execute(options) raise APIError.new(obj, "Action failed: #{obj.error}") unless obj.success obj end - end end diff --git a/lib/nexpose/connection.rb b/lib/nexpose/connection.rb index a9146b41..072de8c0 100644 --- a/lib/nexpose/connection.rb +++ b/lib/nexpose/connection.rb @@ -84,10 +84,10 @@ def logout end # Execute an API request - def execute(xml, version = '1.1') + def execute(xml, version = '1.1', options = {}) @request_xml = xml.to_s @api_version = version - response = APIRequest.execute(@url, @request_xml, @api_version) + response = APIRequest.execute(@url, @request_xml, @api_version, options) @response_xml = response.raw_response_data response end diff --git a/lib/nexpose/report.rb b/lib/nexpose/report.rb index 99d87926..f0554d60 100644 --- a/lib/nexpose/report.rb +++ b/lib/nexpose/report.rb @@ -226,13 +226,15 @@ def to_xml # For XML-based reports, only the raw report is returned and not any images. # # @param [Connection] connection Nexpose connection. + # @param [Fixnum] timeout How long, in seconds, to wait for the report to + # generate. Larger reports can take a significant amount of time. # @return Report in text format except for PDF, which returns binary data. # - def generate(connection) + def generate(connection, timeout = 300) xml = %() xml << to_xml xml << '' - response = connection.execute(xml) + response = connection.execute(xml, '1.1', timeout: timeout) if response.success content_type_response = response.raw_response.header['Content-Type'] if content_type_response =~ /multipart\/mixed;\s*boundary=([^\s]+)/