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_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
- #dispatch(context) ⇒ 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.
519 520 521 522 523 524 525 526 527 |
# File 'lib/ocular/inputs/http_input.rb', line 519 def initialize(settings_factory) settings = settings_factory[: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.
40 41 42 |
# File 'lib/ocular/inputs/http_input.rb', line 40 def routes @routes end |
Instance Method Details
#add_delete(script_name, path, options, proxy, &block) ⇒ Object
261 262 263 264 |
# File 'lib/ocular/inputs/http_input.rb', line 261 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
251 252 253 254 |
# File 'lib/ocular/inputs/http_input.rb', line 251 def add_get(script_name, path, , proxy, &block) name = generate_uri_from_names(script_name, path) route('GET', name, , proxy, &block) end |
#add_post(script_name, path, options, proxy, &block) ⇒ Object
256 257 258 259 |
# File 'lib/ocular/inputs/http_input.rb', line 256 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.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/ocular/inputs/http_input.rb', line 378 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
266 267 268 |
# File 'lib/ocular/inputs/http_input.rb', line 266 def build_signature(pattern, keys, &block) return [pattern, keys, block] end |
#call(env) ⇒ Object
372 373 374 |
# File 'lib/ocular/inputs/http_input.rb', line 372 def call(env) dup.call!(env) end |
#call!(env) ⇒ Object
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
# File 'lib/ocular/inputs/http_input.rb', line 394 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) { |context| dispatch(context) } unless context.response['Content-Type'] context.response['Content-Type'] = "text/html" end context.response.finish end |
#call_block(context) {|context| ... } ⇒ Object
450 451 452 |
# File 'lib/ocular/inputs/http_input.rb', line 450 def call_block(context) yield(context) end |
#compile(path) ⇒ Object
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/ocular/inputs/http_input.rb', line 314 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 |
#dispatch(context) ⇒ Object
412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/ocular/inputs/http_input.rb', line 412 def dispatch(context) invoke(context) do |context| route!(context) end rescue ::Exception => error invoke(context) do |context| handle_exception!(context, error) end ensure end |
#generate_uri_from_names(script_name, path) ⇒ Object
237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/ocular/inputs/http_input.rb', line 237 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
438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/ocular/inputs/http_input.rb', line 438 def handle_exception!(context, error) context.env['error'] = error if error.respond_to? :http_status context.response.status = error.http_status else context.response.status = 500 puts "Internal Server Error: #{error}" puts error.backtrace end end |
#headers(context, hash = nil) ⇒ Object
514 515 516 517 |
# File 'lib/ocular/inputs/http_input.rb', line 514 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.
491 492 493 |
# File 'lib/ocular/inputs/http_input.rb', line 491 def indifferent_hash Hash.new {|hash,key| hash[key.to_s] if Symbol === key } end |
#indifferent_params(object) ⇒ Object
496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/ocular/inputs/http_input.rb', line 496 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
424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/ocular/inputs/http_input.rb', line 424 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
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/ocular/inputs/http_input.rb', line 470 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
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/ocular/inputs/http_input.rb', line 270 def route(verb, path, , proxy, &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
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/ocular/inputs/http_input.rb', line 454 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" raise NotFound end |
#safe_ignore(ignore) ⇒ Object
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/ocular/inputs/http_input.rb', line 293 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
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/ocular/inputs/http_input.rb', line 529 def start() if @settings[:verbose] @app = Rack::CommonLogger.new(@app, STDOUT) end @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 end |
#status(context, value = nil) ⇒ Object
509 510 511 512 |
# File 'lib/ocular/inputs/http_input.rb', line 509 def status(context, value = nil) context.response.status = value if value context.response.status end |
#stop ⇒ Object
549 550 551 552 |
# File 'lib/ocular/inputs/http_input.rb', line 549 def stop() @stopsignal << "EXIT" @thread.join end |