Class: Sensu::API::HTTPHandler
- Inherits:
-
EM::HttpServer::Server
- Object
- EM::HttpServer::Server
- Sensu::API::HTTPHandler
- Includes:
- Routes
- Defined in:
- lib/sensu/api/http_handler.rb
Constant Summary
Constants included from Routes
Routes::DELETE_METHOD, Routes::GET_METHOD, Routes::GET_ROUTES, Routes::HEAD_METHOD, Routes::OPTIONS_METHOD, Routes::POST_METHOD, Routes::ROUTES
Constants included from Routes::Silenced
Routes::Silenced::SILENCED_CHECK_URI, Routes::Silenced::SILENCED_CLEAR_URI, Routes::Silenced::SILENCED_ID_URI, Routes::Silenced::SILENCED_SUBSCRIPTION_URI, Routes::Silenced::SILENCED_URI
Constants included from Routes::Results
Routes::Results::RESULTS_CLIENT_URI, Routes::Results::RESULTS_URI, Routes::Results::RESULT_URI
Constants included from Routes::Stashes
Routes::Stashes::STASHES_URI, Routes::Stashes::STASH_URI
Constants included from Routes::Aggregates
Routes::Aggregates::AGGREGATES_URI, Routes::Aggregates::AGGREGATE_CHECKS_URI, Routes::Aggregates::AGGREGATE_CLIENTS_URI, Routes::Aggregates::AGGREGATE_RESULTS_SEVERITY_URI, Routes::Aggregates::AGGREGATE_URI
Constants included from Routes::Resolve
Constants included from Routes::Events
Routes::Events::EVENTS_CLIENT_URI, Routes::Events::EVENTS_URI, Routes::Events::EVENT_URI
Constants included from Routes::Request
Constants included from Routes::Checks
Routes::Checks::CHECKS_URI, Routes::Checks::CHECK_URI
Constants included from Routes::Clients
Routes::Clients::CLIENTS_URI, Routes::Clients::CLIENT_HISTORY_URI, Routes::Clients::CLIENT_URI
Constants included from Routes::Health
Constants included from Routes::Info
Instance Attribute Summary collapse
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#redis ⇒ Object
Returns the value of attribute redis.
-
#settings ⇒ Object
Returns the value of attribute settings.
-
#transport ⇒ Object
Returns the value of attribute transport.
Instance Method Summary collapse
-
#accepted! ⇒ Object
Respond to the HTTP request with a ‘202` (Accepted) response.
-
#authorized? ⇒ TrueClass, FalseClass
Determine if an HTTP request is authorized.
-
#bad_request! ⇒ Object
Respond to the HTTP request with a ‘400` (Bad Request) response.
-
#connected? ⇒ Boolean
Determine if the API is connected to Redis and the Transport.
-
#create_response ⇒ Object
Create an EM HTTP Server HTTP response object, ‘@response`.
-
#created! ⇒ Object
Respond to the HTTP request with a ‘201` (Created) response.
-
#error! ⇒ Object
Respond to the HTTP request with a ‘500` (Internal Server Error) response.
-
#http_request_errback(error) ⇒ Object
Catch uncaught/unexpected errors, log them, and attempt to respond with a ‘500` (Internal Server Error) HTTP response.
-
#integer_parameter(value) ⇒ Integer?
Determine if a parameter has an integer value and if so return it as one.
-
#log_request ⇒ Object
Log the HTTP request.
-
#log_response ⇒ Object
Log the HTTP response.
-
#no_content! ⇒ Object
Respond to the HTTP request with a ‘204` (No Content) response.
-
#not_found! ⇒ Object
Respond to the HTTP request with a ‘404` (Not Found) response.
-
#pagination(items) ⇒ Array
Paginate the provided items.
-
#parse_parameters ⇒ Object
Parse the HTTP request query string for parameters.
-
#parse_uri(regex) ⇒ Array
Parse the HTTP request URI using a regular expression, returning the URI unescaped match data values.
-
#precondition_failed! ⇒ Object
Respond to the HTTP request with a ‘412` (Precondition Failed) response.
-
#process_http_request ⇒ Object
Process a HTTP request.
-
#read_data(rules = {}) {|Object| ... } ⇒ Object
Read JSON data from the HTTP request content and validate it with the provided rules.
-
#request_details ⇒ Object
Create a hash containing the HTTP request details.
-
#respond ⇒ Object
Respond to an HTTP request.
-
#route_request ⇒ Object
Route the HTTP request.
-
#set_cors_headers ⇒ Object
Set the cors (Cross-origin resource sharing) HTTP headers.
-
#unauthorized! ⇒ Object
Respond to the HTTP request with a ‘401` (Unauthroized) response.
Methods included from Routes::Silenced
#fetch_silenced, #get_silenced, #get_silenced_check, #get_silenced_id, #get_silenced_subscription, #post_silenced, #post_silenced_clear
Methods included from Routes::Results
#delete_result, #get_result, #get_results, #get_results_client, #post_results
Methods included from Routes::Stashes
#delete_stash, #get_stash, #get_stashes, #post_stash, #post_stashes
Methods included from Routes::Aggregates
#delete_aggregate, #get_aggregate, #get_aggregate_checks, #get_aggregate_clients, #get_aggregate_results_severity, #get_aggregates
Methods included from Routes::Resolve
Methods included from Routes::Events
#delete_event, #get_event, #get_events, #get_events_client
Methods included from Routes::Request
Methods included from Routes::Checks
Methods included from Routes::Clients
#delete_client, #get_client, #get_client_history, #get_clients, #post_clients
Methods included from Routes::Health
Methods included from Routes::Info
Instance Attribute Details
#logger ⇒ Object
Returns the value of attribute logger.
13 14 15 |
# File 'lib/sensu/api/http_handler.rb', line 13 def logger @logger end |
#redis ⇒ Object
Returns the value of attribute redis.
13 14 15 |
# File 'lib/sensu/api/http_handler.rb', line 13 def redis @redis end |
#settings ⇒ Object
Returns the value of attribute settings.
13 14 15 |
# File 'lib/sensu/api/http_handler.rb', line 13 def settings @settings end |
#transport ⇒ Object
Returns the value of attribute transport.
13 14 15 |
# File 'lib/sensu/api/http_handler.rb', line 13 def transport @transport end |
Instance Method Details
#accepted! ⇒ Object
Respond to the HTTP request with a ‘202` (Accepted) response.
245 246 247 248 249 |
# File 'lib/sensu/api/http_handler.rb', line 245 def accepted! @response_status = 202 @response_status_string = "Accepted" respond end |
#authorized? ⇒ TrueClass, FalseClass
Determine if an HTTP request is authorized. This method compares the configured API user and password (if any) with the HTTP request basic authentication credentials. No authentication is done if the API user and password are not configured. OPTIONS HTTP requests bypass authentication.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/sensu/api/http_handler.rb', line 191 def api = @settings[:api] if api && api[:user] && api[:password] if @http_request_method == OPTIONS_METHOD true elsif @http[:authorization] scheme, base64 = @http[:authorization].split("\s") if scheme == "Basic" user, password = Base64.decode64(base64).split(":") user == api[:user] && password == api[:password] else false end else false end else true end end |
#bad_request! ⇒ Object
Respond to the HTTP request with a ‘400` (Bad Request) response.
261 262 263 264 265 |
# File 'lib/sensu/api/http_handler.rb', line 261 def bad_request! @response_status = 400 @response_status_string = "Bad Request" respond end |
#connected? ⇒ Boolean
Determine if the API is connected to Redis and the Transport. This method sets the ‘@response_content` if the API is not connected or it has not yet initialized the connection objects. The `/info` and `/health` routes are excluded from the connectivity checks.
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/sensu/api/http_handler.rb', line 217 def connected? connected = true if @redis && @transport unless @http_request_uri =~ INFO_URI || @http_request_uri =~ HEALTH_URI unless @redis.connected? @response_content = {:error => "not connected to redis"} connected = false end unless @transport.connected? @response_content = {:error => "not connected to transport"} connected = false end end else @response_content = {:error => "redis and transport connections not initialized"} connected = false end connected end |
#create_response ⇒ Object
Create an EM HTTP Server HTTP response object, ‘@response`. The response object is use to build up the response status, status string, content type, and content. The response object is responsible for sending the HTTP response to the HTTP client and closing the connection afterwards.
119 120 121 |
# File 'lib/sensu/api/http_handler.rb', line 119 def create_response @response = EM::DelegatedHttpResponse.new(self) end |
#created! ⇒ Object
Respond to the HTTP request with a ‘201` (Created) response.
238 239 240 241 242 |
# File 'lib/sensu/api/http_handler.rb', line 238 def created! @response_status = 201 @response_status_string = "Created" respond end |
#error! ⇒ Object
Respond to the HTTP request with a ‘500` (Internal Server Error) response.
294 295 296 297 298 |
# File 'lib/sensu/api/http_handler.rb', line 294 def error! @response_status = 500 @response_status_string = "Internal Server Error" respond end |
#http_request_errback(error) ⇒ Object
Catch uncaught/unexpected errors, log them, and attempt to respond with a ‘500` (Internal Server Error) HTTP response. This method is called by EM HTTP Server.
350 351 352 353 354 355 356 |
# File 'lib/sensu/api/http_handler.rb', line 350 def http_request_errback(error) @logger.error("unexpected api error", { :error => error.to_s, :backtrace => error.backtrace.join("\n") }) error! rescue nil end |
#integer_parameter(value) ⇒ Integer?
Determine if a parameter has an integer value and if so return it as one. This method will return ‘nil` if the parameter value is not an integer.
81 82 83 |
# File 'lib/sensu/api/http_handler.rb', line 81 def integer_parameter(value) value =~ /\A[0-9]+\z/ ? value.to_i : nil end |
#log_request ⇒ Object
Log the HTTP request. The debug log level is used for requests as response logging includes the same information.
39 40 41 |
# File 'lib/sensu/api/http_handler.rb', line 39 def log_request @logger.debug("api request", request_details) end |
#log_response ⇒ Object
Log the HTTP response.
44 45 46 47 48 49 50 |
# File 'lib/sensu/api/http_handler.rb', line 44 def log_response @logger.info("api response", { :request => request_details, :status => @response.status, :content_length => @response.content.to_s.bytesize }) end |
#no_content! ⇒ Object
Respond to the HTTP request with a ‘204` (No Content) response.
253 254 255 256 257 |
# File 'lib/sensu/api/http_handler.rb', line 253 def no_content! @response_status = 204 @response_status_string = "No Content" respond end |
#not_found! ⇒ Object
Respond to the HTTP request with a ‘404` (Not Found) response.
278 279 280 281 282 |
# File 'lib/sensu/api/http_handler.rb', line 278 def not_found! @response_status = 404 @response_status_string = "Not Found" respond end |
#pagination(items) ⇒ Array
Paginate the provided items. This method uses two HTTP query parameters to determine how to paginate the items, ‘limit` and `offset`. The parameter `limit` specifies how many items are to be returned in the response. The parameter `offset` specifies the items array index, skipping a number of items. This method sets the “X-Pagination” HTTP response header to a JSON object containing the `limit`, `offset` and `total` number of items that are being paginated.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/sensu/api/http_handler.rb', line 150 def pagination(items) limit = integer_parameter(@params[:limit]) offset = integer_parameter(@params[:offset]) || 0 unless limit.nil? @response.headers["X-Pagination"] = Sensu::JSON.dump( :limit => limit, :offset => offset, :total => items.length ) paginated = items.slice(offset, limit) Array(paginated) else items end end |
#parse_parameters ⇒ Object
Parse the HTTP request query string for parameters. This method creates ‘@params`, a hash of parsed query parameters, used by the API routes.
65 66 67 68 69 70 71 72 73 |
# File 'lib/sensu/api/http_handler.rb', line 65 def parse_parameters @params = {} if @http_query_string @http_query_string.split("&").each do |pair| key, value = pair.split("=") @params[key.to_sym] = value end end end |
#parse_uri(regex) ⇒ Array
Parse the HTTP request URI using a regular expression, returning the URI unescaped match data values.
57 58 59 60 |
# File 'lib/sensu/api/http_handler.rb', line 57 def parse_uri(regex) uri_match = regex.match(@http_request_uri)[1..-1] uri_match.map { |s| URI.unescape(s) } end |
#precondition_failed! ⇒ Object
Respond to the HTTP request with a ‘412` (Precondition Failed) response.
286 287 288 289 290 |
# File 'lib/sensu/api/http_handler.rb', line 286 def precondition_failed! @response_status = 412 @response_status_string = "Precondition Failed" respond end |
#process_http_request ⇒ Object
Process a HTTP request. Log the request, parse the HTTP query parameters, create the HTTP response object, set the cors HTTP response headers, determine if the request is authorized, determine if the API is connected to Redis and the Transport, and then route the HTTP request (responding to the request). This method is called by EM HTTP Server when handling a new connection.
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/sensu/api/http_handler.rb', line 329 def process_http_request log_request parse_parameters create_response set_cors_headers if if connected? route_request else error! end else end end |
#read_data(rules = {}) {|Object| ... } ⇒ Object
Read JSON data from the HTTP request content and validate it with the provided rules. If the HTTP request content does not contain valid JSON or it does not pass validation, this method returns a ‘400` (Bad Request) HTTP response.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/sensu/api/http_handler.rb', line 93 def read_data(rules={}) begin data = Sensu::JSON.load(@http_content) valid = data.is_a?(Hash) && rules.all? do |key, rule| value = data[key] (Array(rule[:type]).any? {|type| value.is_a?(type)} || (rule[:nil_ok] && value.nil?)) && (value.nil? || rule[:regex].nil?) || (rule[:regex] && (value =~ rule[:regex]) == 0) end if valid yield(data) else bad_request! end rescue Sensu::JSON::ParseError bad_request! end end |
#request_details ⇒ Object
Create a hash containing the HTTP request details. This method determines the remote address for the HTTP client (using EventMachine Connection ‘get_peername()`).
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/sensu/api/http_handler.rb', line 20 def request_details return @request_details if @request_details _, remote_address = Socket.unpack_sockaddr_in(get_peername) @request_details = { :remote_address => remote_address, :user_agent => @http[:user_agent], :method => @http_request_method, :uri => @http_request_uri, :query_string => @http_query_string, :body => @http_content } if @http[:x_forwarded_for] @request_details[:x_forwarded_for] = @http[:x_forwarded_for] end @request_details end |
#respond ⇒ Object
Respond to an HTTP request. The routes set ‘@response_status`, `@response_status_string`, and `@response_content` appropriately. The HTTP response status defaults to `200` with the status string `OK`. The Sensu API only returns JSON response content, `@response_content` is assumed to be a Ruby object that can be serialized as JSON.
172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/sensu/api/http_handler.rb', line 172 def respond @response.status = @response_status || 200 @response.status_string = @response_status_string || "OK" if @response_content && @http_request_method != HEAD_METHOD @response.content_type "application/json" @response.content = Sensu::JSON.dump(@response_content) end @response.headers["Connection"] = "close" log_response @response.send_response end |
#route_request ⇒ Object
Route the HTTP request. OPTIONS HTTP requests will always return a ‘200` with no response content. The route regular expressions and associated route method calls are provided by `ROUTES`. If a route match is not found, this method responds with a `404` (Not Found) HTTP response.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/sensu/api/http_handler.rb', line 305 def route_request if @http_request_method == OPTIONS_METHOD respond elsif ROUTES.has_key?(@http_request_method) route = ROUTES[@http_request_method].detect do |route| @http_request_uri =~ route[0] end unless route.nil? send(route[1]) else not_found! end else not_found! end end |
#set_cors_headers ⇒ Object
Set the cors (Cross-origin resource sharing) HTTP headers.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/sensu/api/http_handler.rb', line 124 def set_cors_headers api = @settings[:api] || {} api[:cors] ||= { "Origin" => "*", "Methods" => "GET, POST, PUT, DELETE, OPTIONS", "Credentials" => "true", "Headers" => "Origin, X-Requested-With, Content-Type, Accept, Authorization" } if api[:cors].is_a?(Hash) api[:cors].each do |header, value| @response.headers["Access-Control-Allow-#{header}"] = value end end end |
#unauthorized! ⇒ Object
Respond to the HTTP request with a ‘401` (Unauthroized) response. This method sets the “WWW-Autenticate” HTTP response header.
270 271 272 273 274 275 |
# File 'lib/sensu/api/http_handler.rb', line 270 def @response.headers["WWW-Authenticate"] = 'Basic realm="Restricted Area"' @response_status = 401 @response_status_string = "Unauthorized" respond end |