182: def send_file(req_path, request, response, header_only=false)
183:
184: stat = File.stat(req_path)
185:
186:
187: mtime = stat.mtime
188:
189: etag = Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
190:
191: modified_since = request.params[Const::HTTP_IF_MODIFIED_SINCE]
192: none_match = request.params[Const::HTTP_IF_NONE_MATCH]
193:
194:
195:
196: same_response = case
197: when modified_since && !last_response_time = Time.httpdate(modified_since) rescue nil : false
198: when modified_since && last_response_time > Time.now : false
199: when modified_since && mtime > last_response_time : false
200: when none_match && none_match == '*' : false
201: when none_match && !none_match.strip.split(/\s*,\s*/).include?(etag) : false
202: else modified_since || none_match
203: end
204:
205: header = response.header
206: header[Const::ETAG] = etag
207:
208: if same_response
209: response.start(304) {}
210: else
211:
212: response.status = 200
213: header[Const::LAST_MODIFIED] = mtime.httpdate
214:
215:
216: dot_at = req_path.rindex('.')
217: if dot_at
218: header[Const::CONTENT_TYPE] = MIME_TYPES[req_path[dot_at .. -1]] || @default_content_type
219: else
220: header[Const::CONTENT_TYPE] = @default_content_type
221: end
222:
223:
224: response.send_status(stat.size)
225: response.send_header
226:
227: if not header_only
228: response.send_file(req_path, stat.size < Const::CHUNK_SIZE * 2)
229: end
230: end
231: end