rack-archive-zip-extract 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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