Class: WEBrick::HTTPServlet::DefaultFileHandler

Inherits:
AbstractServlet show all
Defined in:
lib/webrick/httpservlet/filehandler.rb

Overview

Servlet for serving a single file. You probably want to use the FileHandler servlet instead as it handles directories and fancy indexes.

Example:

server.mount('/my_page.txt', WEBrick::HTTPServlet::DefaultFileHandler,
             '/path/to/my_page.txt')

This servlet handles If-Modified-Since and Range requests.

Instance Method Summary collapse

Methods inherited from AbstractServlet

#do_HEAD, #do_OPTIONS, get_instance, #service

Constructor Details

#initialize(server, local_path) ⇒ DefaultFileHandler

Creates a DefaultFileHandler instance for the file at local_path.


37
38
39
40
# File 'lib/webrick/httpservlet/filehandler.rb', line 37

def initialize(server, local_path)
  super(server, local_path)
  @local_path = local_path
end

Instance Method Details

#do_GET(req, res) ⇒ Object

:stopdoc:


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/webrick/httpservlet/filehandler.rb', line 44

def do_GET(req, res)
  st = File::stat(@local_path)
  mtime = st.mtime
  res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i)

  if not_modified?(req, res, mtime, res['etag'])
    res.body = ''
    raise HTTPStatus::NotModified
  elsif req['range']
    make_partial_content(req, res, @local_path, st.size)
    raise HTTPStatus::PartialContent
  else
    mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
    res['content-type'] = mtype
    res['content-length'] = st.size
    res['last-modified'] = mtime.httpdate
    res.body = open(@local_path, "rb")
  end
end

#make_partial_content(req, res, filename, filesize) ⇒ Object


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/webrick/httpservlet/filehandler.rb', line 89

def make_partial_content(req, res, filename, filesize)
  mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
  unless ranges = HTTPUtils::parse_range_header(req['range'])
    raise HTTPStatus::BadRequest,
      "Unrecognized range-spec: \"#{req['range']}\""
  end
  open(filename, "rb"){|io|
    if ranges.size > 1
      time = Time.now
      boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
      body = ''
      ranges.each{|range|
        first, last = prepare_range(range, filesize)
        next if first < 0
        io.pos = first
        content = io.read(last-first+1)
        body << "--" << boundary << CRLF
        body << "Content-Type: #{mtype}" << CRLF
        body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
        body << CRLF
        body << content
        body << CRLF
      }
      raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
      body << "--" << boundary << "--" << CRLF
      res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
      res.body = body
    elsif range = ranges[0]
      first, last = prepare_range(range, filesize)
      raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
      if last == filesize - 1
        content = io.dup
        content.pos = first
      else
        io.pos = first
        content = io.read(last-first+1)
      end
      res['content-type'] = mtype
      res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
      res['content-length'] = last - first + 1
      res.body = content
    else
      raise HTTPStatus::BadRequest
    end
  }
end

#not_modified?(req, res, mtime, etag) ⇒ Boolean

Returns:

  • (Boolean)

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/webrick/httpservlet/filehandler.rb', line 64

def not_modified?(req, res, mtime, etag)
  if ir = req['if-range']
    begin
      if Time.httpdate(ir) >= mtime
        return true
      end
    rescue
      if HTTPUtils::split_header_value(ir).member?(res['etag'])
        return true
      end
    end
  end

  if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime
    return true
  end

  if (inm = req['if-none-match']) &&
     HTTPUtils::split_header_value(inm).member?(res['etag'])
    return true
  end

  return false
end

#prepare_range(range, filesize) ⇒ Object


136
137
138
139
140
141
142
# File 'lib/webrick/httpservlet/filehandler.rb', line 136

def prepare_range(range, filesize)
  first = range.first < 0 ? filesize + range.first : range.first
  return -1, -1 if first < 0 || first >= filesize
  last = range.last < 0 ? filesize + range.last : range.last
  last = filesize - 1 if last >= filesize
  return first, last
end