Class: Rack::Archive::Zip::Extract
- Inherits:
-
Object
- Object
- Rack::Archive::Zip::Extract
- Extended by:
- Utils
- Includes:
- Utils
- Defined in:
- lib/rack/archive/zip/extract.rb
Overview
Note:
Rack::Archive::Zip::Extract does not serve a zip file itself. Use Rack::File or so to do so.
Rack::Archive::Zip::Extract is a Rack application which serves files in zip archives.
Defined Under Namespace
Classes: ExtractedFile
Constant Summary collapse
- DOT =
'.'.freeze
- DOUBLE_DOT =
'..'.freeze
- CONTENT_TYPE =
'Content-Type'.freeze
- CONTENT_LENGTH =
'Content-Length'.freeze
- IF_MODIFIED_SINCE =
'HTTP_IF_MODIFIED_SINCE'.freeze
- LAST_MODIFIED =
'Last-Modified'.freeze
- IF_NONE_MATCH =
'HTTP_IF_NONE_MATCH'.freeze
- ETAG =
'ETag'.freeze
- REQUEST_METHOD =
'REQUEST_METHOD'.freeze
- PATH_INFO =
'PATH_INFO'.freeze
- EMPTY_BODY =
[].freeze
- EMPTY_HEADERS =
{}.freeze
- METHOD_NOT_ALLOWED =
[status_code(:method_not_allowd), {'Allow'.freeze => Rack::File::ALLOWED_VERBS.join(', ').freeze}.freeze, EMPTY_BODY]
- NOT_FOUND =
[status_code(:not_found), EMPTY_HEADERS, EMPTY_BODY].freeze
- NOT_MODIFIED =
[status_code(:not_modified), EMPTY_HEADERS, EMPTY_BODY].freeze
- OCTET_STREAM =
'application/octet-stream'.freeze
Instance Method Summary collapse
- #call(env) ⇒ Object
- #extract_file(zip_file_path, inner_path) ⇒ ExtractedFile?
-
#find_zip_file_and_inner_path(path_info, extension) ⇒ Array
A pair of Pathname(zip file) and String(file path in zip archive).
-
#initialize(root, extensions: %w[.zip],, mime_types: {}, buffer_size: ExtractedFile::BUFFER_SIZE) ⇒ Extract
constructor
A new instance of Extract.
-
#path_info_to_clean_segments(path_info) ⇒ Array<String>
Segments of clean path.
Constructor Details
#initialize(root, extensions: %w[.zip],, mime_types: {}, buffer_size: ExtractedFile::BUFFER_SIZE) ⇒ Extract
Returns a new instance of Extract.
43 44 45 46 47 48 49 50 |
# File 'lib/rack/archive/zip/extract.rb', line 43 def initialize(root, extensions: %w[.zip], mime_types: {}, buffer_size: ExtractedFile::BUFFER_SIZE) @root = root.kind_of?(Pathname) ? root : Pathname(root) @root = @root. @extensions = extensions.map {|extention| extention.dup.freeze}.lazy @mime_types = Rack::Mime::MIME_TYPES.merge(mime_types) @buffer_size = buffer_size raise ArgumentError, "Not a directory: #{@root}" unless @root.directory? end |
Instance Method Details
#call(env) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rack/archive/zip/extract.rb', line 52 def call(env) return METHOD_NOT_ALLOWED unless Rack::File::ALLOWED_VERBS.include? env[REQUEST_METHOD] path_info = unescape(env[PATH_INFO]) file = @extensions.map {|ext| zip_file, inner_path = find_zip_file_and_inner_path(path_info, ext) extract_file(zip_file, inner_path) }.select {|file| file}.first return NOT_FOUND if file.nil? if_modified_since = Time.parse(env[IF_MODIFIED_SINCE]) rescue Time.new(0) if_none_match = env[IF_NONE_MATCH] if file.mtime <= if_modified_since or env[IF_NONE_MATCH] == file.etag file.close NOT_MODIFIED else [ status_code(:ok), { CONTENT_TYPE => @mime_types.fetch(::File.extname(path_info), OCTET_STREAM), CONTENT_LENGTH => file.size.to_s, LAST_MODIFIED => file.mtime.httpdate, ETAG => file.etag }, file ] end end |
#extract_file(zip_file_path, inner_path) ⇒ ExtractedFile?
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/rack/archive/zip/extract.rb', line 101 def extract_file(zip_file_path, inner_path) return if zip_file_path.nil? or inner_path.empty? archive = ::Zip::Archive.open(zip_file_path.to_path) if archive.locate_name(inner_path) < 0 archive.close nil else ExtractedFile.new(archive, inner_path, @buffer_size) end end |
#find_zip_file_and_inner_path(path_info, extension) ⇒ Array
Returns a pair of Pathname(zip file) and String(file path in zip archive).
85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rack/archive/zip/extract.rb', line 85 def find_zip_file_and_inner_path(path_info, extension) segments = path_info_to_clean_segments(path_info) current = @root zip_file = nil while segment = segments.shift zip_file = current + "#{segment}#{extension}" return zip_file, ::File.join(segments) if zip_file.file? current += segment end end |
#path_info_to_clean_segments(path_info) ⇒ Array<String>
Returns segments of clean path.
115 116 117 118 119 120 121 122 123 |
# File 'lib/rack/archive/zip/extract.rb', line 115 def path_info_to_clean_segments(path_info) segments = path_info.split PATH_SEPS clean = [] segments.each do |segment| next if segment.empty? || segment == DOT segment == DOUBLE_DOT ? clean.pop : clean << segment end clean end |