Class: Yarn::RequestHandler

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

Direct Known Subclasses

RackHandler

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

#initialize(options = {}) ⇒ RequestHandler

Returns a new instance of RequestHandler.



17
18
19
20
# File 'lib/yarn/request_handler.rb', line 17

def initialize(options={})
  @parser = ParsletParser.new
  @response = Response.new
end

Instance Attribute Details

#parserObject

Returns the value of attribute parser.



15
16
17
# File 'lib/yarn/request_handler.rb', line 15

def parser
  @parser
end

#requestObject

Returns the value of attribute request.



15
16
17
# File 'lib/yarn/request_handler.rb', line 15

def request
  @request
end

#responseObject

Returns the value of attribute response.



15
16
17
# File 'lib/yarn/request_handler.rb', line 15

def response
  @response
end

#sessionObject

Returns the value of attribute session.



15
16
17
# File 'lib/yarn/request_handler.rb', line 15

def session
  @session
end

Instance Method Details

#client_addressObject



198
199
200
# File 'lib/yarn/request_handler.rb', line 198

def client_address
  @session.peeraddr(:numeric)[2] if @session
end

#close_connectionObject



93
94
95
96
97
98
99
# File 'lib/yarn/request_handler.rb', line 93

def close_connection
  if @session #&& !persistent?
    @session.close
  else
    # TODO: start some kind of timeout
  end
end

#execute_script(path) ⇒ Object



74
75
76
77
78
79
80
81
# File 'lib/yarn/request_handler.rb', line 74

def execute_script(path)
  response = `ruby #{path}`
  if !! ($?.to_s =~ /1$/)
    raise ProcessingError
  else
    response
  end
end

#extract_pathObject



154
155
156
157
158
159
160
# File 'lib/yarn/request_handler.rb', line 154

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

#get_mime_type(path) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/yarn/request_handler.rb', line 173

def get_mime_type(path)
    return false unless path.include? '.'
    filetype = path.split('.').last

    return case
  when ["html", "htm"].include?(filetype)
    "text/html"
  when "txt" == filetype 
    "text/plain"
  when "css" == filetype
    "text/css"
  when "js" == filetype
    "text/javascript"
  when ["png", "jpg", "jpeg", "gif", "tiff"].include?(filetype)
    "image/#{filetype}"
  when ["zip","pdf","postscript","x-tar","x-dvi"].include?(filetype)
    "application/#{filetype}"
  else false
  end
end

#parse_requestObject

Raises:



38
39
40
41
42
43
44
45
46
47
48
# File 'lib/yarn/request_handler.rb', line 38

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

#persistent?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/yarn/request_handler.rb', line 110

def persistent?
  return @request[:headers]["Connection"] == "keep-alive"
end

#prepare_responseObject



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/yarn/request_handler.rb', line 50

def prepare_response
  path = extract_path

  @response.headers["Content-Type"] = "text/html"

  begin
    if File.directory? path
      serve_directory path
    elsif File.exists?(path)
      if path =~ /.*\.rb$/
        @response.body << execute_script(path)
        @response.status = 200
      else
        serve_file(path)
      end
    else
      serve_404_page
    end
  rescue ProcessingError
    log "An error occured processing #{path}"
    serve_500_page
  end
end

#read_file(path) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
# File 'lib/yarn/request_handler.rb', line 142

def read_file(path)
  file_contents = []

  File.open(path, "r") do |file|
    while (line = file.gets) do
      file_contents << line
    end
  end

  file_contents
end

#read_requestObject



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

def read_request
  input = []
  while (line = @session.gets) do
    break if line.length <= 2
    input << line
  end
  input.join
end

#request_pathObject



194
195
196
# File 'lib/yarn/request_handler.rb', line 194

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

#return_responseObject



83
84
85
86
87
88
89
90
91
# File 'lib/yarn/request_handler.rb', line 83

def return_response
  @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
end

#run(session) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/yarn/request_handler.rb', line 22

def run(session)
  @response = Response.new
  set_common_headers
  @session = session
  begin
    parse_request
    prepare_response
    return_response
    log "Served (#{STATUS_CODES[@response.status]}) #{client_address} #{request_path}"
  rescue EmptyRequestError
    log "Empty request from #{client_address}"
  ensure
    close_connection
  end
end

#serve_directory(path) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/yarn/request_handler.rb', line 130

def serve_directory(path)
  @response.status = 200
  if File.exists?("index.html") || File.exists?("/index.html")
    @response.body = read_file "index.html"
    @response.headers["Content-Type"] = "text/html"
  else
    @response.headers["Content-Type"] = "text/html"
    directory_lister = DirectoryLister.new
    @response.body << directory_lister.list(path)
  end
end

#serve_file(path) ⇒ Object



124
125
126
127
128
# File 'lib/yarn/request_handler.rb', line 124

def serve_file(path)
  @response.status = 200
  @response.body << read_file(path)
  @response.headers["Content-Type"] = get_mime_type path
end

#set_common_headersObject



114
115
116
117
118
119
120
121
122
# File 'lib/yarn/request_handler.rb', line 114

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