Class: Mongrel::DirHandler

Inherits:
HttpHandler show all
Defined in:
lib/mongrel.rb

Overview

Serves the contents of a directory. You give it the path to the root where the files are located, and it tries to find the files based on the PATH_INFO inside the directory. If the requested path is a directory then it returns a simple directory listing.

It does a simple protection against going outside it’s root path by converting all paths to an absolute expanded path, and then making sure that the final expanded path includes the root path. If it doesn’t than it simply gives a 404.

Constant Summary collapse

MIME_TYPES =
{
  ".css"        =>  "text/css",
  ".gif"        =>  "image/gif",
  ".htm"        =>  "text/html",
  ".html"       =>  "text/html",
  ".jpeg"       =>  "image/jpeg",
  ".jpg"        =>  "image/jpeg",
  ".js"         =>  "text/javascript",
  ".png"        =>  "image/png",
  ".swf"        =>  "application/x-shockwave-flash",
  ".txt"        =>  "text/plain"
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, listing_allowed = true, index_html = "index.html") ⇒ DirHandler

You give it the path to the directory root and an (optional)



470
471
472
473
474
# File 'lib/mongrel.rb', line 470

def initialize(path, listing_allowed=true, index_html="index.html")
  @path = File.expand_path(path)
  @listing_allowed=listing_allowed
  @index_html = index_html
end

Instance Attribute Details

#pathObject (readonly)

Returns the value of attribute path.



467
468
469
# File 'lib/mongrel.rb', line 467

def path
  @path
end

Class Method Details

.add_mime_type(extension, type) ⇒ Object

There is a small number of default mime types for extensions, but this lets you add any others you’ll need when serving content.



585
586
587
# File 'lib/mongrel.rb', line 585

def DirHandler::add_mime_type(extension, type)
  MIME_TYPES[extension] = type
end

Instance Method Details

#can_serve(path_info) ⇒ Object

Checks if the given path can be served and returns the full path (or nil if not).



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 'lib/mongrel.rb', line 477

def can_serve(path_info)
  req = File.expand_path(File.join(@path,path_info), @path)

  if req.index(@path) == 0 and File.exist? req
    # it exists and it's in the right location
    if File.directory? req
      # the request is for a directory
      index = File.join(req, @index_html)
      if File.exist? index
        # serve the index
        return index
      elsif @listing_allows
        # serve the directory
        req
      else
        # do not serve anything
        return nil
      end
    else
      # it's a file and it's there
      return req
    end
  else
    # does not exist or isn't in the right spot
    return nil
  end
end

#process(request, response) ⇒ Object

Process the request to either serve a file or a directory listing if allowed (based on the listing_allowed paramter to the constructor).



559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
# File 'lib/mongrel.rb', line 559

def process(request, response)
  req = can_serve request.params['PATH_INFO']
  if not req
    # not found, return a 404
    response.start(404) do |head,out|
      out << "File not found"
    end
  else
    begin
      if File.directory? req
        send_dir_listing(request.params["REQUEST_URI"],req, response)
      else
        send_file(req, response)
      end
    rescue => details
      response.reset
      response.start(403) do |head,out|
        out << "Error accessing file: #{details}"
        out << details.backtrace.join("\n")
      end
    end
  end
end

#send_dir_listing(base, dir, response) ⇒ Object

Returns a simplistic directory listing if they’re enabled, otherwise a 403. Base is the base URI from the REQUEST_URI, dir is the directory to serve on the file system (comes from can_serve()), and response is the HttpResponse object to send the results on.



510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/mongrel.rb', line 510

def send_dir_listing(base, dir, response)
  # take off any trailing / so the links come out right
  base.chop! if base[-1] == "/"[-1]

  if @listing_allowed
    response.start(200) do |head,out|
      head['Content-Type'] = "text/html"
      out << "<html><head><title>Directory Listing</title></head><body>"
      Dir.entries(dir).each do |child|
        next if child == "."

        if child == ".."
          out << "<a href=\"#{base}/#{child}\">Up to parent..</a><br/>"
        else
          out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>"
        end
      end
      out << "</body></html>"
    end
  else
    response.start(403) do |head,out|
      out.write("Directory listings not allowed")
    end
  end
end

#send_file(req, response) ⇒ Object

Sends the contents of a file back to the user. Not terribly efficient since it’s opening and closing the file for each read.



539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# File 'lib/mongrel.rb', line 539

def send_file(req, response)
  response.start(200) do |head,out|
    # set the mime type from our map based on the ending
    dot_at = req.rindex(".")
    if dot_at
      ext = req[dot_at .. -1]
      if MIME_TYPES[ext]
        head['Content-Type'] = MIME_TYPES[ext]
      end
    end

    open(req, "rb") do |f|
      out.write(f.read)
    end
  end
end