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