module Qeweney::ResponseMethods
def file_full_path(path, opts)
def file_full_path(path, opts) if (base_path = opts[:base_path]) File.join(opts[:base_path], path) else path end end
def redirect(url, status = Status::FOUND)
def redirect(url, status = Status::FOUND) respond(nil, ':status' => status, 'Location' => url) end
def redirect_to_host(new_host, status = Status::FOUND)
def redirect_to_host(new_host, status = Status::FOUND) secure_uri = "//#{new_host}#{uri}" redirect(secure_uri, status) end
def redirect_to_https(status = Status::MOVED_PERMANENTLY)
def redirect_to_https(status = Status::MOVED_PERMANENTLY) secure_uri = "https://#{host}#{uri}" redirect(secure_uri, status) end
def respond_with_static_file(path, etag, last_modified, opts)
def respond_with_static_file(path, etag, last_modified, opts) cache_headers = { 'etag' => etag, 'last-modified' => last_modified, } File.open(path, 'r') do |f| if opts[:headers] opts[:headers].merge!(cache_headers) else opts[:headers] = cache_headers end # accept_encoding should return encodings in client's order of preference accept_encoding.each do |encoding| case encoding when 'deflate' return serve_io_deflate(f, opts) when 'gzip' return serve_io_gzip(f, opts) end end serve_io(f, opts) end end
def serve_file(path, opts = {})
def serve_file(path, opts = {}) full_path = file_full_path(path, opts) stat = File.stat(full_path) etag = StaticFileCaching.file_stat_to_etag(stat) last_modified = StaticFileCaching.file_stat_to_last_modified(stat) if validate_static_file_cache(etag, last_modified) return respond(nil, { ':status' => Status::NOT_MODIFIED, 'etag' => etag }) end mime_type = Qeweney::MimeTypes[File.extname(path)] opts[:stat] = stat (opts[:headers] ||= {})['Content-Type'] ||= mime_type if mime_type respond_with_static_file(full_path, etag, last_modified, opts) rescue Errno::ENOENT respond(nil, ':status' => Status::NOT_FOUND) end
def serve_io(io, opts)
def serve_io(io, opts) respond(io.read, opts[:headers] || {}) end
def serve_io_deflate(io, opts)
def serve_io_deflate(io, opts) deflate = Zlib::Deflate.new headers = opts[:headers].merge( 'content-encoding' => 'deflate', 'vary' => 'Accept-Encoding' ) respond(deflate.deflate(io.read, Zlib::FINISH), headers) end
def serve_io_gzip(io, opts)
def serve_io_gzip(io, opts) buf = StringIO.new z = Zlib::GzipWriter.new(buf) z << io.read z.flush z.close headers = opts[:headers].merge( 'content-encoding' => 'gzip', 'vary' => 'Accept-Encoding' ) respond(buf.string, headers) end
def serve_rack(app)
def serve_rack(app) response = app.(Qeweney.rack_env_from_request(self)) headers = (response[1] || {}).merge(':status' => response[0]) respond(response[2].join, headers) # TODO: send separate chunks for multi-part body # TODO: add support for streaming body # TODO: add support for websocket end
def upgrade(protocol, custom_headers = nil)
def upgrade(protocol, custom_headers = nil) upgrade_headers = { ':status' => Status::SWITCHING_PROTOCOLS, 'Upgrade' => protocol, 'Connection' => 'upgrade' } upgrade_headers.merge!(custom_headers) if custom_headers respond(nil, upgrade_headers) end
def upgrade_to_websocket(custom_headers = nil)
def upgrade_to_websocket(custom_headers = nil) key = "#{headers['sec-websocket-key']}#{WEBSOCKET_GUID}" upgrade_headers = { 'Sec-WebSocket-Accept' => Digest::SHA1.base64digest(key) } upgrade_headers.merge!(custom_headers) if custom_headers upgrade('websocket', upgrade_headers) adapter.websocket_connection(self) end
def validate_static_file_cache(etag, last_modified)
def validate_static_file_cache(etag, last_modified) if (none_match = headers['if-none-match']) return true if none_match == etag end if (modified_since = headers['if-modified-since']) return true if modified_since == last_modified end false end