Class: Yarn::AbstractHandler

Inherits:
Object
  • Object
show all
Includes:
ErrorPage, Logging
Defined in:
lib/yarn/abstract_handler.rb

Overview

Base class for the handler classes. Built using the Template Method design pattern.

Direct Known Subclasses

RackHandler, RequestHandler

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ErrorPage

#serve_404_page, #serve_500_page

Methods included from Logging

#debug, #log, #output, #timestamp

Constructor Details

#initializeAbstractHandler

Returns a new instance of AbstractHandler.



21
22
23
24
# File 'lib/yarn/abstract_handler.rb', line 21

def initialize
  @parser = Parser.new
  @response = Response.new
end

Instance Attribute Details

#parserObject

Returns the value of attribute parser.



19
20
21
# File 'lib/yarn/abstract_handler.rb', line 19

def parser
  @parser
end

#requestObject

Returns the value of attribute request.



19
20
21
# File 'lib/yarn/abstract_handler.rb', line 19

def request
  @request
end

#responseObject

Returns the value of attribute response.



19
20
21
# File 'lib/yarn/abstract_handler.rb', line 19

def response
  @response
end

#sessionObject

Returns the value of attribute session.



19
20
21
# File 'lib/yarn/abstract_handler.rb', line 19

def session
  @session
end

Instance Method Details

#client_addressObject

Proxy for the clients address.



131
132
133
134
135
136
137
# File 'lib/yarn/abstract_handler.rb', line 131

def client_address
  begin
    @session.peeraddr(:numeric)[2] if @session
  rescue Errno::ENOTCONN
    return ""
  end
end

#extract_pathObject

Extracts the path from the parsed request



112
113
114
115
116
117
118
# File 'lib/yarn/abstract_handler.rb', line 112

def extract_path
  path = @request[:uri][:path].to_s
  if path[0] == "/" && path != "/"
    path = path[1..-1] 
  end
  path.gsub(/%20/, " ").strip
end

#parse_requestObject

Invokes the parser upon the request

Raises:



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/yarn/abstract_handler.rb', line 49

def parse_request
  raw_request = read_request
  raise EmptyRequestError if raw_request.empty?

  begin
    @request = @parser.run raw_request
  rescue Parslet::ParseFailed => e
    @response.status = 400
    debug "Parse failed: #{@request}"
  end
end

#post_bodyObject

Proxy to getting the request body.



121
122
123
# File 'lib/yarn/abstract_handler.rb', line 121

def post_body
  @request ? @request[:body].to_s : ""
end

#prepare_responseObject

Only implemented in the actual handler classes.



62
63
# File 'lib/yarn/abstract_handler.rb', line 62

def prepare_response
end

#read_requestObject

Reads the request from the socket. If a Content-Length header is given, that means there is an accompanying request body. The body is read according to the set Content-Length.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/yarn/abstract_handler.rb', line 83

def read_request
  input = []
  while (line = @session.gets) do
    length = line.gsub(/\D/,"") if line =~ /Content-Length/
      if line == "\r\n"
        input << line
        input << @session.read(length.to_i) if length
        break
      else
        input << line
      end
  end

  debug "Done reading request"
  input.join
end

#request_pathObject

Proxy for getting the request path.



126
127
128
# File 'lib/yarn/abstract_handler.rb', line 126

def request_path
  @request[:uri][:path] if @request
end

#return_responseObject

returns the reqponse by writing it to the socket.



66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/yarn/abstract_handler.rb', line 66

def return_response
  begin
    @session.puts "HTTP/1.1 #{@response.status} #{STATUS_CODES[@response.status]}"
    @session.puts @response.headers.map { |k,v| "#{k}: #{v}" }
    @session.puts ""

    @response.body.each do |line|
      @session.puts line
    end
  rescue Exception => exception
    log "An error occured returning the response to the client"
  end
end

#run(session) ⇒ Object

The template method which drives the handlers. Starts by setting common headers and parses the request, prepares the response, returns it to the client, and closes the connection.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/yarn/abstract_handler.rb', line 30

def run(session)
  set_common_headers
  @session = session
  begin
    parse_request
    debug "Request parsed, path: #{request_path}"
    prepare_response
    debug "Response prepared: #{@response.status}"
    return_response
    log "#{STATUS_CODES[@response.status]} #{client_address} #{request_path}"
  rescue EmptyRequestError
    log "Empty request from #{client_address}"
  ensure
    @session.close
    debug "Connection closed"
  end
end

#set_common_headersObject

Sets common headers like server name and date.



101
102
103
104
105
106
107
108
109
# File 'lib/yarn/abstract_handler.rb', line 101

def set_common_headers
  @response.headers[:Server] = "Yarn webserver v#{VERSION}"

  # HTTP date format: Fri, 31 Dec 1999 23:59:59 GMT
  time ||= DateTime.now.new_offset(0)
  @response.headers[:Date] = time.strftime("%a, %d %b %Y %H:%M:%S GMT")
  # Close connection header ( until support for persistent connections )
  @response.headers[:Connection] = "Close"
end