Module: EventParsers::Http11Parser

Included in:
Http11Server
Defined in:
bin/httphere

Overview

Implement this by including it in a class and call receive_data on every read event. Callbacks available:

upon_new_request(request)       # after first HTTP line
receive_header(request, header) # after each header is received
upon_headers_finished(request)  # after all headers are received
process_request(request)        # after the full request is received

Defined Under Namespace

Modules: BasicAuth, ClassMethods Classes: HeaderAndEntityStateStore, Request

Constant Summary collapse

HttpResponseRE =
/\AHTTP\/(1.[01]) ([\d]{3})/i
HttpRequestRE =
/^(GET|POST|PUT|DELETE) (\/.*) HTTP\/([\d\.]+)[\r\n]?$/i
BlankLineRE =
/^[\n\r]+$/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#socketObject (readonly)

Returns the value of attribute socket.



407
408
409
# File 'bin/httphere', line 407

def socket
  @socket
end

Class Method Details

.included(base) ⇒ Object



398
399
400
# File 'bin/httphere', line 398

def self.included(base)
  base.extend ClassMethods
end

Instance Method Details

#current_requestObject



413
414
415
# File 'bin/httphere', line 413

def current_request
  request_backlog.first
end

#parsing_requestObject



416
417
418
# File 'bin/httphere', line 416

def parsing_request
  request_backlog.last
end

#process_request(request) ⇒ Object



505
506
507
# File 'bin/httphere', line 505

def process_request(request)
  warn "STUB - overwrite process_request in a subclass of Http11Parser to process this #{request.inspect}"
end

#receive_data(data) ⇒ Object



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
# File 'bin/httphere', line 424

def receive_data(data)
			return unless (data and data.size > 0)

  @last_activity = Time.now

  case parser.state
  when :init
	if ix = data.index("\n")
		parser.linebuffer << data[0...ix+1]
		ln = parser.linebuffer.join
		     parser.linebuffer.clear
      log "[#{parser.state}]: #{ln}" if $DEBUG
      if ln =~ HttpRequestRE
        request_backlog << self.class.request_klass.new
        parsing_request.connection = self
        method, resource_uri, http_version = parse_init_line(ln)
        parsing_request.method = method
        parsing_request.resource_uri = resource_uri
        parsing_request.query_params = parse_query_string(resource_uri.index('?') ? resource_uri[(resource_uri.index('?')+1)..-1] : '')
        parsing_request.http_version = http_version
        upon_new_request(parsing_request) if respond_to?(:upon_new_request)
        parser.state = :headers
      else
			  parser.bogus_line!(ln)
      end
		receive_data(data[(ix+1)..-1])
    else
      parser.linebuffer << data
    end
  when :headers
	if ix = data.index("\n")
		parser.linebuffer << data[0...ix+1]
		ln = parser.linebuffer.join
		     parser.linebuffer.clear
      # If it's a blank line, move to content state
      if ln =~ BlankLineRE
        upon_headers_finished(parsing_request) if respond_to?(:upon_headers_finished)
        if parser.entity?
          # evaluate_headers(parsing_request.headers)
          parser.state = :entity
        else
          receive_full_request
        end
      else
        header = parse_header_line(ln)
        log "\t[#{parser.state}]: #{header.inspect}\n" if $DEBUG
        receive_header(parsing_request, header.to_a[0]) if respond_to?(:receive_header)
        parsing_request.headers.merge!(header)
      end
		receive_data(data[(ix+1)..-1])
	else
		parser.linebuffer << data
	end
			when :entity
if parser.entity_size
	chars_yet_needed = parser.entity_size - parser.entity_pos
	taking_this_many = [chars_yet_needed, data.size].sort.first
	parser.textbuffer << data[0...taking_this_many]
		leftover_data = data[taking_this_many..-1]
	parser.entity_pos += taking_this_many
	if parser.entity_pos >= parser.entity_size
		entity_data = parser.textbuffer.join
		              parser.textbuffer.clear
		parsing_request.entity << entity_data
        log "[#{parser.state}]: #{entity_data}\n" if $DEBUG
        receive_full_request
	end
	receive_data(leftover_data)
else
  raise "TODO!"
      # receive_binary_data data
end
    
else
    # Probably shouldn't ever be here?
    raise "Shouldn't be here!"
			end

  # TODO: Exception if number of parser.bogus_lines is higher than threshold
end

#request_backlogObject



409
410
411
# File 'bin/httphere', line 409

def request_backlog
  @request_backlog ||= []
end

#send_response!(http_response) ⇒ Object



509
510
511
512
513
514
515
# File 'bin/httphere', line 509

def send_response!(http_response)
  log "Sending Response: #{http_response.inspect}\n" if $DEBUG
  socket.write(http_response)
  request_backlog.shift
  # Process the next request IF it is already waiting.
  process_request(current_request) if current_request && current_request.ready?
end