Class: Ramaze::Dispatcher::File

Inherits:
Object
  • Object
show all
Extended by:
Trinity
Defined in:
lib/ramaze/dispatcher/file.rb

Overview

First of the dispatchers, looks up the public path and serves the file if found.

Constant Summary collapse

INDICES =

These names are checked for serving from public directory. They take priority over Actions which comes later in the FILTER

%w[index.htm index.xhtml index.html index]

Class Method Summary collapse

Class Method Details

.call(path) ⇒ Object

Entry point from Dispatcher::filter. searches for the file and builds a response with status 200 if found.



24
25
26
27
28
29
30
31
# File 'lib/ramaze/dispatcher/file.rb', line 24

def call(path)
  return unless file = open_file(CGI.unescape(path))
  Session.current.drop! if Session.current
  if file == :NotModified
    return response.build([], STATUS_CODE['Not Modified'])
  end
  response.build(file, STATUS_CODE['OK'])
end

.in_public?(path) ⇒ Boolean

Determine whether a path is in the public directory for better security.

Returns:

  • (Boolean)


75
76
77
# File 'lib/ramaze/dispatcher/file.rb', line 75

def in_public?(path)
  expand(path).start_with?(expand(Global.public_root))
end

.open_file(path) ⇒ Object

returns file-handle with the open file on success, setting the Content-Type as found in Tool::MIME



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ramaze/dispatcher/file.rb', line 36

def open_file(path)
  file = resolve_path(path)
  
  if ::File.file?(file)
    return unless in_public?(file)
    response['Content-Type'] = Tool::MIME.type_for(file) unless ::File.extname(file).empty?
    mtime = ::File.mtime(file)
    response['Last-Modified'] = mtime.httpdate
    response['ETag']= Digest::MD5.hexdigest(file+mtime.to_s).inspect
    if modified_since = request.env['HTTP_IF_MODIFIED_SINCE']
      return :NotModified unless Time.parse(modified_since) < mtime
    elsif match = request.env['HTTP_IF_NONE_MATCH']
      # Should be a unique string enclosed in ""
      # To avoiding more file reading we use mtime and filepath
      # we could throw in inode and size for more uniqueness
      return :NotModified if response['ETag']==match
    end
    log(file)
    ::File.open(file, 'rb')
  end
end

.resolve_path(path) ⇒ Object

If path matches a directory in public_root, return that directory path, unless there is an index file in that directory, in which case return path for that. If path is not a directory, simply return given path in public_root. Either way, the returned path always starts with public_root.



63
64
65
66
67
68
69
70
71
# File 'lib/ramaze/dispatcher/file.rb', line 63

def resolve_path(path)
  joined = ::File.join(Global.public_root, path)

  if ::File.directory?(joined)
    Dir[joined/"{#{INDICES.join(',')}}"].first || joined
  else
    joined
  end
end