thingfish 0.5.0.pre20161103181816 → 0.8.0

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.
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+