rack-archive-zip-extract 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rack/archive/zip/extract.rb +59 -34
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d1d1fdef39fc5a5b020441ab5e3016d4ceb278e
4
- data.tar.gz: 9c122a1f1cd9c251625211a264e49ec4779fddc7
3
+ metadata.gz: 367946e3ae493b7beb2c8a894a737861d8819a44
4
+ data.tar.gz: 1b2509141f86cde9c02afb4ce3174136db6d57fd
5
5
  SHA512:
6
- metadata.gz: 281a9e3aabf364311f5600e8857e579fcd2ddcccbdbb622b9bf2ccb16564bf12ec01c057660e11e5d3bb83bbd126eeda9d1892ee0218db272ddf9598dde5b5e0
7
- data.tar.gz: 57c7a6d86e25a58b13b23d3d6e16c1241f2094a23de109357443a7014d140934ba2e3d967bebd2abaa06e7313f443976900acd1ea9b32c5a0ff899a08513f75b
6
+ metadata.gz: 0200cd1fa15c0182d6597d21dd25796c5c928753d252060a44d1255a0c10d1aa5a91ad910387321b8073e2b12773066322b06bf43dc49f705cf27e4e3ec1364d
7
+ data.tar.gz: 36807535e98a5b0947fef9066be63b5d47ad09a951854b211ea894960ddc92c61f92a1d57448a4141412e73170122a04750aa38ee4a4ce065201f874ee9e1d42
@@ -4,18 +4,30 @@ require 'rack/file'
4
4
  require 'rack/mime'
5
5
  require 'zipruby'
6
6
 
7
- # {Rack::Archive::Zip::Extract Rack::Archive::Zip::Extract} is a Rack application which serves files in zip archives.
8
- # @example
9
- # run Rack::Archive::Zip::Extract.new('path/to/docroot')
10
- # @example
11
- # run Rack::Archive::Zip::Extract.new('path/to/docroot', extensions: %w[.epub .zip .jar .odt .docx])
12
- # @note
13
- # {Rack::Archive::Zip::Extract Rack::Archive::Zip::Extract} does not serve a zip file itself. Use Rack::File or so to do so.
14
7
  module Rack::Archive
15
8
  module Zip
9
+ # {Rack::Archive::Zip::Extract Rack::Archive::Zip::Extract} is a Rack application which serves files in zip archives.
10
+ # @example
11
+ # run Rack::Archive::Zip::Extract.new('path/to/docroot')
12
+ # @example
13
+ # run Rack::Archive::Zip::Extract.new('path/to/docroot', extensions: %w[.epub .zip .jar .odt .docx])
14
+ # @note
15
+ # {Rack::Archive::Zip::Extract Rack::Archive::Zip::Extract} does not serve a zip file itself. Use Rack::File or so to do so.
16
16
  class Extract
17
+ include Rack::Utils
18
+
17
19
  SEPS = Rack::File::SEPS
20
+ DOT = '.'.freeze
21
+ DOUBLE_DOT = '..'.freeze
22
+ COMMA = ','.freeze
18
23
  ALLOWED_VERBS = Rack::File::ALLOWED_VERBS
24
+ ALLOW = 'Allow'.freeze
25
+ CONTENT_TYPE = 'Content-Type'.freeze
26
+ CONTENT_LENGTH = 'Content-Length'.freeze
27
+ IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
28
+ LAST_MODIFIED = 'Last-Modified'.freeze
29
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
30
+ PATH_INFO = 'PATH_INFO'.freeze
19
31
 
20
32
  attr_reader :root
21
33
 
@@ -30,40 +42,48 @@ module Rack::Archive
30
42
  end
31
43
 
32
44
  def call(env)
33
- return [405, {'Allow' => ALLOWED_VERBS.join(', ')}, []] unless ALLOWED_VERBS.include? env['REQUEST_METHOD']
45
+ return [status_code(:method_not_allowd), {ALLOW => ALLOWED_VERBS.join(COMMA)}, []] unless ALLOWED_VERBS.include? env[REQUEST_METHOD]
34
46
 
35
- path_info = Rack::Utils.unescape(env['PATH_INFO'])
47
+ path_info = unescape(env[PATH_INFO])
48
+ if_modified_since = env[IF_MODIFIED_SINCE]
49
+ if_modified_since = Time.parse(if_modified_since) if if_modified_since
36
50
  zip_file = nil
37
51
  body = nil
52
+ file_size = nil
53
+ mtime = nil
38
54
  @extensions.each do |ext|
39
55
  zip_file, inner_path = find_zip_file_and_inner_path(path_info, ext)
40
- body = extract_content(zip_file, inner_path)
41
- break if body
56
+ body, file_size, mtime = extract_content(zip_file, inner_path, if_modified_since)
57
+ break if mtime
42
58
  end
43
- return [404, {}, []] if body.nil?
59
+ return [status_code(:not_found), {}, []] if mtime.nil?
44
60
 
45
- [
46
- 200,
47
- {
48
- 'Content-Type' => Rack::Mime.mime_type(::File.extname(path_info)),
49
- 'Content-Length' => body.bytesize.to_s,
50
- 'Last-Modified' => zip_file.mtime.httpdate
51
- },
52
- [body]
53
- ]
61
+ if if_modified_since and mtime <= if_modified_since
62
+ [status_code(:not_modified), {}, []]
63
+ else
64
+ [
65
+ status_code(:ok),
66
+ {
67
+ CONTENT_TYPE => Rack::Mime.mime_type(::File.extname(path_info)),
68
+ CONTENT_LENGTH => file_size.to_s,
69
+ LAST_MODIFIED => mtime.httpdate
70
+ },
71
+ [body]
72
+ ]
73
+ end
54
74
  end
55
75
 
56
76
  # @param path_info [String]
57
77
  # @param extension [String]
58
78
  # @return [Array] a pair of Pathname(zip file) and String(file path in zip archive)
59
79
  def find_zip_file_and_inner_path(path_info, extension)
60
- path_parts = path_info_to_clean_parts(path_info)
80
+ segments = path_info_to_clean_segments(path_info)
61
81
  current = @root
62
82
  zip_file = nil
63
- while part = path_parts.shift
64
- zip_file = current + "#{part}#{extension}"
65
- return zip_file, ::File.join(path_parts) if zip_file.file?
66
- current += part
83
+ while segment = segments.shift
84
+ zip_file = current + "#{segment}#{extension}"
85
+ return zip_file, ::File.join(segments) if zip_file.file?
86
+ current += segment
67
87
  end
68
88
  end
69
89
 
@@ -72,24 +92,29 @@ module Rack::Archive
72
92
  # @return [String] content
73
93
  # @return [nil] if +zip_file_path+ is nil or +inner_path+ is empty
74
94
  # @return [nil] if +inner_path+ doesn't exist in zip archive
75
- def extract_content(zip_file_path, inner_path)
95
+ def extract_content(zip_file_path, inner_path, if_modified_since)
76
96
  return if zip_file_path.nil? or inner_path.empty?
77
97
  ::Zip::Archive.open zip_file_path.to_path do |archive|
78
98
  return if archive.locate_name(inner_path) < 0
79
99
  archive.fopen inner_path do |file|
80
- return file.read
100
+ if if_modified_since and file.mtime <= if_modified_since
101
+ return nil, nil, file.mtime
102
+ else
103
+ return file.read, file.size, file.mtime
104
+ end
81
105
  end
82
106
  end
83
107
  end
84
108
 
85
109
  # @param path_info [String]
86
- # @return [Array<String>] parts of clean path
87
- def path_info_to_clean_parts(path_info)
88
- parts = path_info.split SEPS
110
+ # @return [Array<String>] segments of clean path
111
+ # @see http://rubydoc.info/gems/rack/Rack/File#_call-instance_method Algorithm stolen from Rack::File#_call
112
+ def path_info_to_clean_segments(path_info)
113
+ segments = path_info.split SEPS
89
114
  clean = []
90
- parts.each do |part|
91
- next if part.empty? || part == '.'
92
- part == '..' ? clean.pop : clean << part
115
+ segments.each do |segment|
116
+ next if segment.empty? || segment == DOT
117
+ segment == DOUBLE_DOT ? clean.pop : clean << segment
93
118
  end
94
119
  clean
95
120
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-archive-zip-extract
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - KITAITI Makoto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-04 00:00:00.000000000 Z
11
+ date: 2014-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack