thingfish 0.5.0.pre20161103181816 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 74a092349d627e3de7578603e6cbba04c29f8045
4
- data.tar.gz: eb4868b63ef6b8ad8b95c4ff5954f4482924bb3d
2
+ SHA256:
3
+ metadata.gz: 767d737d3f8a3ca3cae2d7f5b01fa7e7b1967127585efe3201b55fce50a46500
4
+ data.tar.gz: 769440ac44975755940720e4cd95a610c072874e3c5a1f709bcf3e7cf6cabf39
5
5
  SHA512:
6
- metadata.gz: 6e3a206fa9f2ad2dd07543f407f85972a42b4f073ae8cc3eab66ead4ec5947b74c6f4228741a96efdb15a22101c4251cc49cee7a6e4b5720a1bf5acefda41967
7
- data.tar.gz: a923141975987b85e96e84bd20c017d06609110abf10e319bd76a61ef51be1f0b6e59e2c66d8bc5d3d4d0275c862d8a3826b2e6c17f844daf456d6b7cfb21d91
6
+ metadata.gz: a83f80a9b4efa7c7e651f2fd1791e4424fa2bd63d6b304b4cc622cfcfc1fd2840ffd1fee370722cc4399453ac662efffc2591e51e054acb33e8e72e776d7a30c
7
+ data.tar.gz: d6244bb6e4b2c340f977f12ed0f19631bbdd6a10815973c44880c0ebbc8c841b6296744f7cb331ed71a776e26c2d7f24cac136b510afe1eceebe0eb110ff822c
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -0,0 +1,48 @@
1
+ # Release History for thingfish
2
+
3
+ ---
4
+
5
+ ## v0.8.0 [2021-01-02] Michael Granger <ged@FaerieMUD.org>
6
+
7
+ Improvements:
8
+
9
+ - Several bugfixes
10
+ - Support for newer Rubies
11
+
12
+
13
+ ## v0.7.0 [2017-09-13] Michael Granger <ged@FaerieMUD.org>
14
+
15
+ Improvements:
16
+
17
+ - Explicitly set the HTTP status when returning resources.
18
+ - Add Last-Modified/If-None-Match support.
19
+ - Move the configure() method to help support deferred configuration.
20
+
21
+
22
+ ## v0.6.0 [2017-01-16] Mahlon E. Smith <mahlon@martini.nu>
23
+
24
+ Housekeeping:
25
+
26
+ - Bump Configurability dependency.
27
+ - Migrate away from .rvmrc.
28
+
29
+
30
+ ## v0.5.1 [2016-11-14] Michael Granger <ged@FaerieMUD.org>
31
+
32
+ Enhancements:
33
+
34
+ - Add support for Strelka app discovery.
35
+
36
+ Bugfixes:
37
+
38
+ - Remove the processor daemon for now
39
+ - Documentation fixes
40
+
41
+
42
+ ## v0.5.0 [2016-11-14] Michael Granger <ged@FaerieMUD.org>
43
+
44
+ First public release.
45
+
46
+
47
+
48
+
@@ -1,8 +1,16 @@
1
- = Thingfish
1
+ # Thingfish
2
2
 
3
- * http://bitbucket.org/ged/thingfish
3
+ home
4
+ : https://thing.fish
4
5
 
5
- == Description
6
+ code
7
+ : https://hg.sr.ht/~ged/thingfish
8
+
9
+ docs
10
+ : https://thing.fish/docs/
11
+
12
+
13
+ ## Description
6
14
 
7
15
  Thingfish is a extensible, web-based digital asset manager. It can be used to
8
16
  store chunks of data on the network in an application-independent way, link the
@@ -10,38 +18,31 @@ chunks together with metadata, and then search for the chunk you need later and
10
18
  fetch it, all through a REST API.
11
19
 
12
20
 
13
- == Authors
14
-
15
- * Michael Granger <ged@FaerieMUD.org>
16
- * Mahlon E. Smith <mahlon@martini.nu>
17
-
18
- == Contributors
19
-
20
- * Jeremiah Jordan <phaedrus@perlreason.com>
21
- * Ben Bleything <ben@bleything.net>
22
- * Jeff Davis <jeff-thingfish@j-davis.com>
23
-
21
+ ## Requirements
24
22
 
25
- == Installation
23
+ Thingfish is written in ruby, and is tested using [version 2.7](http://www.ruby-lang.org/en/downloads/). Other versions may work,
24
+ but are not tested.
26
25
 
27
- === Requirements
28
26
 
29
- Thingfish is written in ruby, and is tested using version 2.0.0.
27
+ ## Installation
30
28
 
31
- * Ruby (>= 2.0.0): http://www.ruby-lang.org/en/downloads/
29
+ You can install Thingfish via Rubygems:
32
30
 
33
- Other versions may work, but are not tested.
31
+ $ gem install thingfish
34
32
 
35
- === Ruby Modules
33
+ This will install the basic server and its dependencies. Additional functionality is available via separate gems in the following namespaces:
36
34
 
37
- You can install Thingfish via the Rubygems package manager:
35
+ `thingfish-metastore-*`
36
+ : Storage backends for resource metadata
38
37
 
39
- $ sudo gem install thingfish
38
+ `thingfish-filestore-*`
39
+ : Storage backends for resources themselves
40
40
 
41
- This will install the basic server and its dependencies. You can also install a collection of useful plugins via the 'thingfish-plugins' gem.
41
+ `thingfish-processor-*`
42
+ : Filters and extractors for resources
42
43
 
43
44
 
44
- == Contributing
45
+ ## Contributing
45
46
 
46
47
  You can check out the current development source
47
48
  {with Mercurial}[http://bitbucket.org/ged/thingfish], or
@@ -59,9 +60,22 @@ You can submit bug reports, suggestions, and read more about future plans at
59
60
  {the project page}[http://bitbucket.org/ged/thingfish].
60
61
 
61
62
 
62
- == License
63
+ ## Authors
64
+
65
+ * Michael Granger <ged@FaerieMUD.org>
66
+ * Mahlon E. Smith <mahlon@martini.nu>
67
+
68
+
69
+ ## Contributors
70
+
71
+ * Jeremiah Jordan <phaedrus@perlreason.com>
72
+ * Ben Bleything <ben@bleything.net>
73
+ * Jeff Davis <jeff-thingfish@j-davis.com>
74
+
75
+
76
+ ## License
63
77
 
64
- Copyright (c) 2007-2014, Michael Granger and Mahlon E. Smith
78
+ Copyright (c) 2007-2020, Michael Granger and Mahlon E. Smith
65
79
  All rights reserved.
66
80
 
67
81
  Redistribution and use in source and binary forms, with or without
data/Rakefile CHANGED
@@ -1,92 +1,8 @@
1
- #!/usr/bin/env rake
2
- #encoding: utf-8
1
+ #!/usr/bin/env ruby -S rake
3
2
 
4
- begin
5
- require 'hoe'
6
- rescue LoadError
7
- abort "This Rakefile requires hoe (gem install hoe)"
8
- end
9
-
10
- GEMSPEC = 'thingfish.gemspec'
11
-
12
-
13
- Hoe.plugin :mercurial
14
- Hoe.plugin :signing
15
- Hoe.plugin :deveiate
16
-
17
- Hoe.plugins.delete :rubyforge
18
-
19
- hoespec = Hoe.spec 'thingfish' do
20
- self.readme_file = 'README.rdoc'
21
- self.history_file = 'History.rdoc'
22
- self.extra_rdoc_files = Rake::FileList[ '*.rdoc' ]
23
- self.license 'BSD'
24
-
25
- self.developer 'Michael Granger', 'ged@FaerieMUD.org'
26
- self.developer 'Mahlon E. Smith', 'mahlon@martini.nu'
27
- self.license "BSD"
28
-
29
- self.dependency 'strelka', '~> 0.9'
30
- self.dependency 'mongrel2', '~> 0.43'
31
-
32
- self.dependency 'hoe-deveiate', '~> 0.3', :development
33
- self.dependency 'simplecov', '~> 0.7', :development
34
- self.dependency 'ruby-mp3info', '~> 0.8', :development
35
-
36
- self.require_ruby_version( '>=2.0.0' )
37
-
38
- self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
39
- spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
40
-
41
- self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
42
- end
43
-
44
- ENV['VERSION'] ||= hoespec.spec.version.to_s
45
-
46
- # Run the tests before checking in
47
- task 'hg:precheckin' => [ :check_history, :check_manifest, :gemspec, :spec ]
48
-
49
- task :test => :spec
50
-
51
- # Rebuild the ChangeLog immediately before release
52
- task :prerelease => 'ChangeLog'
53
- CLOBBER.include( 'ChangeLog' )
54
-
55
- desc "Build a coverage report"
56
- task :coverage do
57
- ENV["COVERAGE"] = 'yes'
58
- Rake::Task[:spec].invoke
59
- end
60
- CLOBBER.include( 'coverage' )
61
-
62
-
63
- # Use the fivefish formatter for docs generated from development checkout
64
- if File.directory?( '.hg' )
65
- require 'rdoc/task'
3
+ require 'rake/deveiate'
66
4
 
67
- Rake::Task[ 'docs' ].clear
68
- RDoc::Task.new( 'docs' ) do |rdoc|
69
- rdoc.main = "README.rdoc"
70
- rdoc.markup = 'markdown'
71
- rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb" )
72
- rdoc.generator = :fivefish
73
- rdoc.title = 'Thingfish'
74
- rdoc.rdoc_dir = 'doc'
75
- end
5
+ Rake::DevEiate.setup( 'thingfish' ) do |project|
6
+ project.publish_to = 'deveiate:/usr/local/www/public/code'
76
7
  end
77
8
 
78
- task :gemspec => GEMSPEC
79
- file GEMSPEC => __FILE__
80
- task GEMSPEC do |task|
81
- spec = $hoespec.spec
82
- spec.files.delete( '.gemtest' )
83
- spec.signing_key = nil
84
- spec.cert_chain = ['certs/ged.pem']
85
- spec.version = "#{spec.version}.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
86
- File.open( task.name, 'w' ) do |fh|
87
- fh.write( spec.to_ruby )
88
- end
89
- end
90
-
91
- CLOBBER.include( GEMSPEC.to_s )
92
-
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'strelka'
5
5
  require 'thingfish/handler'
@@ -1,5 +1,5 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'strelka'
5
5
  require 'strelka/plugins'
@@ -0,0 +1,9 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'strelka/discovery'
5
+
6
+ Strelka::Discovery.register_apps(
7
+ 'thingfish' => 'thingfish/handler.rb'
8
+ )
9
+
@@ -1,6 +1,6 @@
1
1
  # -*- ruby -*-
2
2
  # vim: set nosta noet ts=4 sw=4:
3
- # encoding: utf-8
3
+ # frozen_string_literal: true
4
4
 
5
5
  require 'strelka/constants'
6
6
  require 'strelka/httprequest' unless defined?( Strelka::HTTPRequest )
@@ -2,18 +2,7 @@
2
2
 
3
3
  require 'loggability'
4
4
 
5
- #
6
5
  # Network-accessable datastore service
7
- #
8
- # == Version
9
- #
10
- # $Id: thingfish.rb,v ce172208b523 2013/11/20 02:21:12 ged $
11
- #
12
- # == Authors
13
- #
14
- # * Michael Granger <ged@FaerieMUD.org>
15
- # * Mahlon E. Smith <mahlon@martini.nu>
16
- #
17
6
  module Thingfish
18
7
  extend Loggability
19
8
 
@@ -23,10 +12,10 @@ module Thingfish
23
12
 
24
13
 
25
14
  # Package version
26
- VERSION = '0.5.0'
15
+ VERSION = '0.8.0'
27
16
 
28
17
  # Version control revision
29
- REVISION = %q$Revision: ce172208b523 $
18
+ REVISION = %q$Revision$
30
19
 
31
20
 
32
21
  ### Get the library version. If +include_buildnum+ is true, the version string will
@@ -1,5 +1,5 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'rspec'
5
5
 
@@ -1,5 +1,5 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'securerandom'
5
5
  require 'pluggability'
@@ -1,5 +1,5 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'thingfish' unless defined?( Thingfish )
5
5
  require 'thingfish/datastore' unless defined?( Thingfish::Datastore )
@@ -1,5 +1,5 @@
1
1
  # -*- ruby -*-
2
- #encoding: utf-8
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'strelka'
5
5
  require 'strelka/app'
@@ -74,22 +74,6 @@ class Thingfish::Handler < Strelka::App
74
74
  singleton_attr_accessor :processors
75
75
 
76
76
 
77
- ### Configurability API -- install the configuration
78
- def self::configure( config=nil )
79
- config = self.defaults.merge( config || {} )
80
-
81
- self.datastore = config[:datastore]
82
- self.metastore = config[:metastore]
83
- self.event_socket_uri = config[:event_socket_uri]
84
-
85
- self.processors = self.load_processors( config[:processors] )
86
- self.processors.each do |processor|
87
- self.filter( :request, &processor.method(:process_request) )
88
- self.filter( :response, &processor.method(:process_response) )
89
- end
90
- end
91
-
92
-
93
77
  ### Load the Thingfish::Processors in the given +processor_list+ and return an instance
94
78
  ### of each one.
95
79
  def self::load_processors( processor_list )
@@ -110,6 +94,24 @@ class Thingfish::Handler < Strelka::App
110
94
  end
111
95
 
112
96
 
97
+ ### Configurability API -- install the configuration
98
+ def self::configure( config=nil )
99
+ config = self.defaults.merge( config || {} )
100
+
101
+ self.datastore = config[:datastore]
102
+ self.metastore = config[:metastore]
103
+ self.event_socket_uri = config[:event_socket_uri]
104
+
105
+ self.plugin( :filters ) # pre-load the filters plugin for deferred config
106
+
107
+ self.processors = self.load_processors( config[:processors] )
108
+ self.processors.each do |processor|
109
+ self.filter( :request, &processor.method(:process_request) )
110
+ self.filter( :response, &processor.method(:process_response) )
111
+ end
112
+ end
113
+
114
+
113
115
  ### Set up the metastore, datastore, and event socket when the handler is
114
116
  ### created.
115
117
  def initialize( * ) # :notnew:
@@ -145,8 +147,8 @@ class Thingfish::Handler < Strelka::App
145
147
  ### Set up the event socket.
146
148
  def setup_event_socket
147
149
  if self.class.event_socket_uri && ! @event_socket
148
- @event_socket = Mongrel2.zmq_context.socket( :PUB )
149
- @event_socket.linger = 0
150
+ @event_socket = CZTop::Socket::PUB.new
151
+ @event_socket.options.linger = 0
150
152
  @event_socket.bind( self.class.event_socket_uri )
151
153
  end
152
154
  end
@@ -193,7 +195,7 @@ class Thingfish::Handler < Strelka::App
193
195
  "The name(s) of the fields to order results by."
194
196
  param :direction, /^(asc|desc)$/i, "The order direction (ascending or descending)"
195
197
  param :casefold, :boolean, "Whether or not to convert to lowercase before matching"
196
- param :relationship, :word, "The name of the relationship between two resources"
198
+ param :relationship, /^[\w\-]+$/, "The name of the relationship between two resources"
197
199
 
198
200
 
199
201
  #
@@ -316,6 +318,9 @@ class Thingfish::Handler < Strelka::App
316
318
 
317
319
  finish_with( HTTP::NOT_FOUND, "No such object." ) unless self.metastore.include?( uuid )
318
320
 
321
+ primary_metadata = self.metastore.fetch( uuid )
322
+ self.check_resource_permissions( req, uuid, primary_metadata )
323
+
319
324
  criteria = {
320
325
  'relation' => uuid,
321
326
  'relationship' => rel,
@@ -328,8 +333,12 @@ class Thingfish::Handler < Strelka::App
328
333
  metadata = self.metastore.fetch( uuid )
329
334
 
330
335
  res = req.response
336
+ self.add_cache_headers( req, metadata )
337
+ self.add_content_disposition( res, metadata )
338
+
331
339
  res.body = object
332
340
  res.content_type = metadata['format']
341
+ res.status = HTTP::OK
333
342
 
334
343
  return res
335
344
  end
@@ -348,7 +357,8 @@ class Thingfish::Handler < Strelka::App
348
357
  res = req.response
349
358
  res.content_type = metadata['format']
350
359
 
351
- self.add_etag_headers( req, metadata )
360
+ self.add_cache_headers( req, metadata )
361
+ self.add_content_disposition( res, metadata )
352
362
 
353
363
  if object.respond_to?( :path )
354
364
  path = Pathname( object.path )
@@ -360,6 +370,7 @@ class Thingfish::Handler < Strelka::App
360
370
  res.body = object
361
371
  end
362
372
 
373
+ res.status = HTTP::OK
363
374
  return res
364
375
  end
365
376
 
@@ -411,9 +422,9 @@ class Thingfish::Handler < Strelka::App
411
422
 
412
423
  self.check_resource_permissions( req, uuid )
413
424
 
414
- self.datastore.remove( uuid ) or finish_with( HTTP::NOT_FOUND, "No such object." )
415
- metadata = self.metastore.remove( uuid )
416
425
  self.remove_related_resources( uuid )
426
+ metadata = self.metastore.remove( uuid )
427
+ self.datastore.remove( uuid ) or finish_with( HTTP::NOT_FOUND, "No such object." )
417
428
  self.send_event( :deleted, :uuid => uuid )
418
429
 
419
430
  res = req.response
@@ -441,8 +452,7 @@ class Thingfish::Handler < Strelka::App
441
452
 
442
453
  finish_with( HTTP::NOT_FOUND, "No such object." ) unless self.metastore.include?( uuid )
443
454
 
444
- metadata = self.metastore.fetch( uuid )
445
- metadata[ 'oid' ] = uuid
455
+ metadata = self.normalized_metadata_for( uuid )
446
456
  self.check_resource_permissions( req, uuid, metadata )
447
457
 
448
458
  res = req.response
@@ -538,13 +548,17 @@ class Thingfish::Handler < Strelka::App
538
548
  metadata = self.metastore.fetch( uuid )
539
549
  self.check_resource_permissions( req, uuid, metadata )
540
550
 
541
- op_metadata = self.metastore.fetch( uuid, *OPERATIONAL_METADATA_KEYS )
551
+ op_metadata = self.metastore.fetch( uuid, *OPERATIONAL_METADATA_KEYS )
542
552
  new_metadata = self.extract_metadata( req )
543
- self.metastore.save( uuid, new_metadata.merge(op_metadata) )
553
+ self.metastore.transaction do
554
+ self.metastore.remove_except( uuid, *OPERATIONAL_METADATA_KEYS )
555
+ self.metastore.merge( uuid, new_metadata.merge(op_metadata) )
556
+ end
544
557
  self.send_event( :metadata_replaced, :uuid => uuid )
545
558
 
546
559
  res = req.response
547
- res.status = HTTP::NO_CONTENT
560
+ res.status = HTTP::OK
561
+ res.for( :json, :yaml ) { self.normalized_metadata_for(uuid) }
548
562
 
549
563
  return res
550
564
  end
@@ -565,7 +579,8 @@ class Thingfish::Handler < Strelka::App
565
579
  self.send_event( :metadata_updated, :uuid => uuid )
566
580
 
567
581
  res = req.response
568
- res.status = HTTP::NO_CONTENT
582
+ res.status = HTTP::OK
583
+ res.for( :json, :yaml ) { self.normalized_metadata_for(uuid) }
569
584
 
570
585
  return res
571
586
  end
@@ -585,7 +600,8 @@ class Thingfish::Handler < Strelka::App
585
600
  self.send_event( :metadata_deleted, :uuid => uuid )
586
601
 
587
602
  res = req.response
588
- res.status = HTTP::NO_CONTENT
603
+ res.status = HTTP::OK
604
+ res.for( :json, :yaml ) { self.normalized_metadata_for(uuid) }
589
605
 
590
606
  return res
591
607
  end
@@ -626,8 +642,8 @@ class Thingfish::Handler < Strelka::App
626
642
  metadata.merge!( self.extract_header_metadata(request) )
627
643
  metadata.merge!( self.extract_default_metadata(request) )
628
644
 
629
- self.verify_operational_metadata( metadata )
630
645
  self.check_resource_permissions( request, uuid, metadata )
646
+ self.verify_operational_metadata( metadata )
631
647
 
632
648
  if uuid
633
649
  self.log.info "Replacing resource %s (encoding: %p)" %
@@ -702,9 +718,9 @@ class Thingfish::Handler < Strelka::App
702
718
  ### Return a Hash of default metadata extracted from the given +request+.
703
719
  def extract_default_metadata( request )
704
720
  return self.extract_connection_metadata( request ).merge(
705
- 'extent' => request.headers.content_length,
706
- 'format' => request.content_type,
707
- 'created' => Time.now.getgm
721
+ 'extent' => request.headers.content_length,
722
+ 'format' => request.content_type,
723
+ 'created' => Time.now.getgm
708
724
  )
709
725
  end
710
726
 
@@ -722,15 +738,7 @@ class Thingfish::Handler < Strelka::App
722
738
  ### Extract and validate supplied metadata from the +request+.
723
739
  def extract_metadata( req )
724
740
  new_metadata = req.params.fields.dup
725
- new_metadata.delete( :uuid )
726
-
727
- protected_keys = OPERATIONAL_METADATA_KEYS & new_metadata.keys
728
-
729
- unless protected_keys.empty?
730
- finish_with HTTP::FORBIDDEN,
731
- "Unable to alter protected metadata. (%s)" % [ protected_keys.join(', ') ]
732
- end
733
-
741
+ new_metadata = self.remove_operational_metadata( new_metadata )
734
742
  return new_metadata
735
743
  end
736
744
 
@@ -750,6 +758,13 @@ class Thingfish::Handler < Strelka::App
750
758
  end
751
759
 
752
760
 
761
+ ### Fetch the current metadata for +uuid+, altering it for easier
762
+ ### round trips with REST.
763
+ def normalized_metadata_for( uuid )
764
+ return self.metastore.fetch(uuid).merge( 'uuid' => uuid )
765
+ end
766
+
767
+
753
768
  ### Check that the metadata provided contains valid values for
754
769
  ### the operational keys, before saving a resource to disk.
755
770
  def verify_operational_metadata( metadata )
@@ -759,32 +774,58 @@ class Thingfish::Handler < Strelka::App
759
774
  end
760
775
 
761
776
 
777
+ ### Prune operational +metadata+ from the provided hash.
778
+ def remove_operational_metadata( metadata )
779
+ operationals = OPERATIONAL_METADATA_KEYS + [ 'uuid' ]
780
+ return metadata.reject{|key, _| operationals.include?(key) }
781
+ end
782
+
783
+
762
784
  ### Send an event of +type+ with the given +msg+ over the zmq event socket.
763
785
  def send_event( type, msg )
764
786
  esock = self.event_socket or return
765
787
  self.log.debug "Publishing %p event: %p" % [ type, msg ]
766
- esock.sendm( type.to_s )
767
- esock.send( Yajl.dump(msg) )
788
+ esock << CZTop::Message.new([ type.to_s, Yajl.dump(msg) ])
768
789
  end
769
790
 
770
791
 
771
- ### Add browser cache headers for resources. This requires the sha256
792
+ ### Add browser cache headers for resources.
793
+ ### Last-Modified is always added. ETag support requires the sha256
772
794
  ### processor plugin to be enabled for stored resources.
773
- def add_etag_headers( request, metadata )
795
+ ###
796
+ def add_cache_headers( request, metadata )
774
797
  response = request.response
775
- checksum = metadata[ 'checksum' ]
776
- return unless checksum
777
798
 
778
- if (( match = request.headers[ :if_none_match ] ))
779
- match = match.gsub( '"', '' ).split( /,\s*/ )
780
- finish_with( HTTP::NOT_MODIFIED ) if match.include?( checksum )
799
+ # ETag takes precedence if available.
800
+ #
801
+ if (( checksum = metadata['checksum'] ))
802
+ if (( match = request.headers[ :if_none_match ] ))
803
+ match = match.gsub( '"', '' ).split( /,\s*/ )
804
+ finish_with( HTTP::NOT_MODIFIED ) if match.include?( checksum )
805
+ end
806
+ response.headers[ :etag ] = checksum
807
+ end
808
+
809
+ return unless metadata[ 'created' ]
810
+
811
+ if (( modified = request.headers[ :if_modified_since ] ))
812
+ finish_with( HTTP::NOT_MODIFIED ) if Time.parse( modified ) <= metadata['created'].round
781
813
  end
814
+ response.headers[ :last_modified ] = metadata[ 'created' ].httpdate
782
815
 
783
- response.headers[ :etag ] = checksum
784
816
  return
785
817
  end
786
818
 
787
819
 
820
+ ### Add a filename "hint" for browsers, if the resource being fetched
821
+ ### has a 'title' attribute.
822
+ def add_content_disposition( res, metadata )
823
+ return unless metadata[ 'title' ]
824
+ title = metadata[ 'title' ].encode( 'us-ascii', :undef => :replace )
825
+ res.headers[ :content_disposition ] = "filename=%p" % [ title ]
826
+ end
827
+
828
+
788
829
  ### Supply a method that child handlers can override. The regular auth
789
830
  ### plugin runs too early (but can also be used), this hook allows
790
831
  ### child handlers to make access decisions based on the +request+