Class: Ocular::Inputs::HTTP::Input
- Defined in:
- lib/ocular/inputs/http_input.rb
Defined Under Namespace
Classes: NotFound, Request, Response, WebRunContext
Constant Summary collapse
- URI_INSTANCE =
URI::Parser.new
- DEFAULT_SETTINGS =
{ :host => '0.0.0.0', :port => 8080, :verbose => false, :silent => false }
Instance Attribute Summary collapse
-
#routes ⇒ Object
readonly
Returns the value of attribute routes.
Instance Method Summary collapse
- #add_delete(script_name, path, options, proxy, &block) ⇒ Object
- #add_get(script_name, path, options, proxy, &block) ⇒ Object
- #add_options(script_name, path, options, proxy, &block) ⇒ Object
- #add_post(script_name, path, options, proxy, &block) ⇒ Object
-
#body(context, value = nil, &block) ⇒ Object
Set or retrieve the response body.
- #build_signature(pattern, keys, &block) ⇒ Object
- #call(env) ⇒ Object
- #call!(env) ⇒ Object
- #call_block(context) {|context| ... } ⇒ Object
- #compile(path) ⇒ Object
- #define_check_route ⇒ Object
- #dispatch(context) ⇒ Object
- #encoded(char) ⇒ Object
- #escaped(char, enc = URI_INSTANCE.escape(char)) ⇒ Object
- #generate_uri_from_names(script_name, path) ⇒ Object
- #handle_exception!(context, error) ⇒ Object
- #headers(context, hash = nil) ⇒ Object
-
#indifferent_hash ⇒ Object
Creates a Hash with indifferent access.
- #indifferent_params(object) ⇒ Object
-
#initialize(settings_factory) ⇒ Input
constructor
A new instance of Input.
- #invoke(context) ⇒ Object
- #process_route(context, pattern, keys, values = []) ⇒ Object
- #route(verb, path, options, proxy, &block) ⇒ Object
- #route!(context) ⇒ Object
- #safe_ignore(ignore) ⇒ Object
- #start ⇒ Object
- #status(context, value = nil) ⇒ Object
- #stop ⇒ Object
Constructor Details
#initialize(settings_factory) ⇒ Input
Returns a new instance of Input.
574 575 576 577 578 579 580 581 582 |
# File 'lib/ocular/inputs/http_input.rb', line 574 def initialize(settings_factory) settings = settings_factory.get(:inputs)[:http] @routes = {} @settings = DEFAULT_SETTINGS.merge(settings) @stopsignal = Queue.new() @thread = nil end |
Instance Attribute Details
#routes ⇒ Object (readonly)
Returns the value of attribute routes.
64 65 66 |
# File 'lib/ocular/inputs/http_input.rb', line 64 def routes @routes end |
Instance Method Details
#add_delete(script_name, path, options, proxy, &block) ⇒ Object
288 289 290 291 |
# File 'lib/ocular/inputs/http_input.rb', line 288 def add_delete(script_name, path, , proxy, &block) name = generate_uri_from_names(script_name, path) route('DELETE', name, , proxy, &block) end |
#add_get(script_name, path, options, proxy, &block) ⇒ Object
278 279 280 281 |
# File 'lib/ocular/inputs/http_input.rb', line 278 def add_get(script_name, path, , proxy, &block) name = generate_uri_from_names(script_name, path) route('GET', name, , proxy, &block) end |
#add_options(script_name, path, options, proxy, &block) ⇒ Object
293 294 295 296 |
# File 'lib/ocular/inputs/http_input.rb', line 293 def (script_name, path, , proxy, &block) name = generate_uri_from_names(script_name, path) route('OPTIONS', name, , proxy, &block) end |
#add_post(script_name, path, options, proxy, &block) ⇒ Object
283 284 285 286 |
# File 'lib/ocular/inputs/http_input.rb', line 283 def add_post(script_name, path, , proxy, &block) name = generate_uri_from_names(script_name, path) route('POST', name, , proxy, &block) end |
#body(context, value = nil, &block) ⇒ Object
Set or retrieve the response body. When a block is given, evaluation is deferred until the body is read with #each.
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/ocular/inputs/http_input.rb', line 423 def body(context, value = nil, &block) if block_given? def block.each; yield(call) end context.response.body = block elsif value # Rack 2.0 returns a Rack::File::Iterator here instead of # Rack::File as it was in the previous API. unless context.request.head? headers(context).delete 'Content-Length' end context.response.body = value else context.response.body end end |
#build_signature(pattern, keys, &block) ⇒ Object
298 299 300 |
# File 'lib/ocular/inputs/http_input.rb', line 298 def build_signature(pattern, keys, &block) return [pattern, keys, block] end |
#call(env) ⇒ Object
417 418 419 |
# File 'lib/ocular/inputs/http_input.rb', line 417 def call(env) dup.call!(env) end |
#call!(env) ⇒ Object
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/ocular/inputs/http_input.rb', line 439 def call!(env) context = WebRunContext.new context.request = Request.new(env) context.response = Response.new context.env = env context.params = indifferent_params(context.request.params) context.response['Content-Type'] = nil invoke(context) do |context| dispatch(context) end unless context.response['Content-Type'] context.response['Content-Type'] = "text/html" end context.response.finish end |
#call_block(context) {|context| ... } ⇒ Object
505 506 507 |
# File 'lib/ocular/inputs/http_input.rb', line 505 def call_block(context) yield(context) end |
#compile(path) ⇒ Object
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/ocular/inputs/http_input.rb', line 348 def compile(path) if path.respond_to? :to_str keys = [] # Split the path into pieces in between forward slashes. # A negative number is given as the second argument of path.split # because with this number, the method does not ignore / at the end # and appends an empty string at the end of the return value. # segments = path.split('/', -1).map! do |segment| ignore = [] # Special character handling. # pattern = segment.to_str.gsub(/[^\?\%\\\/\:\*\w]|:(?!\w)/) do |c| ignore << escaped(c).join if c.match(/[\.@]/) patt = encoded(c) patt.gsub(/%[\da-fA-F]{2}/) do |match| match.split(//).map! { |char| char == char.downcase ? char : "[#{char}#{char.downcase}]" }.join end end ignore = ignore.uniq.join # Key handling. # pattern.gsub(/((:\w+)|\*)/) do |match| if match == "*" keys << 'splat' "(.*?)" else keys << $2[1..-1] ignore_pattern = safe_ignore(ignore) ignore_pattern end end end # Special case handling. # if last_segment = segments[-1] and last_segment.match(/\[\^\\\./) parts = last_segment.rpartition(/\[\^\\\./) parts[1] = '[^' segments[-1] = parts.join end [/\A#{segments.join('/')}\z/, keys] elsif path.respond_to?(:keys) && path.respond_to?(:match) [path, path.keys] elsif path.respond_to?(:names) && path.respond_to?(:match) [path, path.names] elsif path.respond_to? :match [path, []] else raise TypeError, path end end |
#define_check_route ⇒ Object
607 608 609 610 611 612 613 |
# File 'lib/ocular/inputs/http_input.rb', line 607 def define_check_route pattern, keys = compile("/check") (@routes["GET"] ||= []) << build_signature(pattern, keys) do |context| [200, "OK\n"] end end |
#dispatch(context) ⇒ Object
459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/ocular/inputs/http_input.rb', line 459 def dispatch(context) invoke(context) do |context| route!(context) end rescue ::Exception => error invoke(context) do |context| handle_exception!(context, error) end ensure end |
#encoded(char) ⇒ Object
406 407 408 409 410 411 |
# File 'lib/ocular/inputs/http_input.rb', line 406 def encoded(char) enc = URI_INSTANCE.escape(char) enc = "(?:#{escaped(char, enc).join('|')})" if enc == char enc = "(?:#{enc}|#{encoded('+')})" if char == " " enc end |
#escaped(char, enc = URI_INSTANCE.escape(char)) ⇒ Object
413 414 415 |
# File 'lib/ocular/inputs/http_input.rb', line 413 def escaped(char, enc = URI_INSTANCE.escape(char)) [Regexp.escape(enc), URI_INSTANCE.escape(char, /./)] end |
#generate_uri_from_names(script_name, path) ⇒ Object
264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/ocular/inputs/http_input.rb', line 264 def generate_uri_from_names(script_name, path) if path[0] == "/" path = path[1..-1] end if script_name && script_name != "" name = script_name + "/" + path else name = path end return "/" + name end |
#handle_exception!(context, error) ⇒ Object
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/ocular/inputs/http_input.rb', line 485 def handle_exception!(context, error) context.env['error'] = error if error.respond_to? :http_status context.response.status = error.http_status if error.respond_to? :to_s str = error.to_s if !str.end_with?("\n") str += "\n" end context.response.body = str end else context.response.status = 500 puts "Internal Server Error: #{error}" puts error.backtrace end end |
#headers(context, hash = nil) ⇒ Object
569 570 571 572 |
# File 'lib/ocular/inputs/http_input.rb', line 569 def headers(context, hash = nil) context.response.headers.merge! hash if hash context.response.headers end |
#indifferent_hash ⇒ Object
Creates a Hash with indifferent access.
546 547 548 |
# File 'lib/ocular/inputs/http_input.rb', line 546 def indifferent_hash Hash.new {|hash,key| hash[key.to_s] if Symbol === key } end |
#indifferent_params(object) ⇒ Object
551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/ocular/inputs/http_input.rb', line 551 def indifferent_params(object) case object when Hash new_hash = indifferent_hash object.each { |key, value| new_hash[key] = indifferent_params(value) } new_hash when Array object.map { |item| indifferent_params(item) } else object end end |
#invoke(context) ⇒ Object
472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/ocular/inputs/http_input.rb', line 472 def invoke(context) res = catch(:halt) { yield(context) } if Array === res and Fixnum === res.first res = res.dup status(context, res.shift) body(context, res.pop) headers(context, *res) elsif res.respond_to? :each body(context, res) end nil # avoid double setting the same response tuple twice end |
#process_route(context, pattern, keys, values = []) ⇒ Object
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
# File 'lib/ocular/inputs/http_input.rb', line 525 def process_route(context, pattern, keys, values = []) route = context.request.path_info route = '/' if route.empty? return unless match = pattern.match(route) values += match.captures.map! { |v| URI_INSTANCE.unescape(v) if v } if values.any? original, @params = context.params, context.params.merge('splat' => [], 'captures' => values) keys.zip(values) { |k,v| Array === context.params[k] ? context.params[k] << v : context.params[k] = v if v } end yield(self, values) rescue context.env['error.params'] = context.params raise ensure @params = original if original end |
#route(verb, path, options, proxy, &block) ⇒ Object
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/ocular/inputs/http_input.rb', line 302 def route(verb, path, , proxy, &block) ::Ocular.logger.debug("Binding #{verb} #{path} to block #{block}") eventbase = Ocular::DSL::EventBase.new(proxy, &block) (proxy.events[verb] ||= {})[path] = eventbase pattern, keys = compile(path) (@routes[verb] ||= []) << build_signature(pattern, keys) do |context| context.event_signature = [verb, path] response = eventbase.exec(context) environment = { :path => path, :options => , :request => context.request, :params => context.params, :env => context.env, :response => response } context.log_cause("on#{verb}", environment) response end end |
#route!(context) ⇒ Object
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/ocular/inputs/http_input.rb', line 509 def route!(context) if routes = @routes[context.request.request_method] routes.each do |pattern, keys, block| process_route(context, pattern, keys) do |*args| #env['route'] = block.instance_variable_get(:@route_name) #throw :halt, context.exec(&block) throw :halt, call_block(context, &block) end end end puts "Route missing: #{context.request.request_method} #{context.request.path_info}" raise NotFound end |
#safe_ignore(ignore) ⇒ Object
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/ocular/inputs/http_input.rb', line 327 def safe_ignore(ignore) unsafe_ignore = [] ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex| unsafe_ignore << hex[1..2] '' end unsafe_patterns = unsafe_ignore.map! do |unsafe| chars = unsafe.split(//).map! do |char| char == char.downcase ? char : char + char.downcase end "|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])" end if unsafe_patterns.length > 0 "((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)" else "([^#{ignore}/?#]+)" end end |
#start ⇒ Object
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
# File 'lib/ocular/inputs/http_input.rb', line 584 def start() if @settings[:verbose] @app = Rack::CommonLogger.new(@app, STDOUT) end ::Ocular.logger.debug "Puma HTTP server started with settings #{@settings}" @thread = Thread.new do events_hander = @settings[:silent] ? ::Puma::Events.strings : ::Puma::Events.stdio server = ::Puma::Server.new(self, events_hander) server.add_tcp_listener @settings[:host], @settings[:port] server.min_threads = 0 server.max_threads = 16 server.run @stopsignal.pop server.stop(true) end define_check_route() end |
#status(context, value = nil) ⇒ Object
564 565 566 567 |
# File 'lib/ocular/inputs/http_input.rb', line 564 def status(context, value = nil) context.response.status = value if value context.response.status end |
#stop ⇒ Object
615 616 617 618 |
# File 'lib/ocular/inputs/http_input.rb', line 615 def stop() @stopsignal << "EXIT" @thread.join end |