6 # Condition Symbol :http_response_code
9 # Trigger based on the response from an HTTP request.
13 # +host+ is the hostname to connect [required]
14 # --one of code_is or code_is_not--
15 # +code_is+ trigger if the response code IS one of these
16 # e.g. 500 or '500' or [404, 500] or %w{404 500}
17 # +code_is_not+ trigger if the response code IS NOT one of these
18 # e.g. 200 or '200' or [200, 302] or %w{200 302}
20 # +port+ is the port to connect (default 80)
21 # +path+ is the path to connect (default '/')
22 # +times+ is the number of times after which to trigger (default 1)
23 # e.g. 3 (times in a row) or [3, 5] (three out of fives times)
24 # +timeout+ is the time to wait for a connection (default 60.seconds)
28 # Trigger if the response code from www.example.com/foo/bar
29 # is not a 200 (or if the connection is refused or times out:
31 # on.condition(:http_response_code) do |c|
32 # c.host = 'www.example.com'
37 # Trigger if the response code is a 404 or a 500 (will not
38 # be triggered by a connection refusal or timeout):
40 # on.condition(:http_response_code) do |c|
41 # c.host = 'www.example.com'
43 # c.code_is = [404, 500]
46 # Trigger if the response code is not a 200 five times in a row:
48 # on.condition(:http_response_code) do |c|
49 # c.host = 'www.example.com'
55 # Trigger if the response code is not a 200 or does not respond
58 # on.condition(:http_response_code) do |c|
59 # c.host = 'www.example.com'
64 class HttpResponseCode < PollCondition
65 attr_accessor :code_is, # e.g. 500 or '500' or [404, 500] or %w{404 500}
66 :code_is_not, # e.g. 200 or '200' or [200, 302] or %w{200 302}
67 :times, # e.g. 3 or [3, 5]
68 :host, # e.g. www.example.com
70 :timeout, # e.g. 60.seconds
78 self.timeout = 60.seconds
82 self.code_is = Array(self.code_is).map { |x| x.to_i } if self.code_is
83 self.code_is_not = Array(self.code_is_not).map { |x| x.to_i } if self.code_is_not
85 if self.times.kind_of?(Integer)
86 self.times = [self.times, self.times]
89 @timeline = Timeline.new(self.times[1])
90 @history = Timeline.new(self.times[1])
100 valid &= complain("Attribute 'host' must be specified", self) if self.host.nil?
101 valid &= complain("One (and only one) of attributes 'code_is' and 'code_is_not' must be specified", self) if
102 (self.code_is.nil? && self.code_is_not.nil?) || (self.code_is && self.code_is_not)
109 Net::HTTP.start(self.host, self.port) do |http|
110 http.read_timeout = self.timeout
111 response = http.get(self.path)
114 actual_response_code = response.code.to_i
115 if self.code_is && self.code_is.include?(actual_response_code)
116 pass(actual_response_code)
117 elsif self.code_is_not && !self.code_is_not.include?(actual_response_code)
118 pass(actual_response_code)
120 fail(actual_response_code)
122 rescue Errno::ECONNREFUSED
123 self.code_is ? fail('Refused') : pass('Refused')
125 self.code_is ? fail('EOF') : pass('EOF')
126 rescue Timeout::Error
127 self.code_is ? fail('Timeout') : pass('Timeout')
134 if @timeline.select { |x| x }.size >= self.times.first
135 self.info = "http response abnormal #{history(code, true)}"
138 self.info = "http response nominal #{history(code, true)}"
145 self.info = "http response nominal #{history(code, false)}"
149 def history(code, passed)
150 entry = code.to_s.dup
151 entry = '*' + entry if passed
153 '[' + @history.join(", ") + ']'