@@ -23,6 +23,7 @@ const net_utils = require('./net_utils');
23
23
const time_utils = require ( './time_utils' ) ;
24
24
const cloud_utils = require ( './cloud_utils' ) ;
25
25
const ssl_utils = require ( '../util/ssl_utils' ) ;
26
+ const s3_utils = require ( '../endpoint/s3/s3_utils' ) ;
26
27
const RpcError = require ( '../rpc/rpc_error' ) ;
27
28
const S3Error = require ( '../endpoint/s3/s3_errors' ) . S3Error ;
28
29
@@ -664,6 +665,82 @@ function set_amz_headers(req, res) {
664
665
res . setHeader ( 'x-amz-id-2' , req . request_id ) ;
665
666
}
666
667
668
+ const s3_error_options = {
669
+ ErrorClass : S3Error ,
670
+ error_missing_content_length : S3Error . MissingContentLength
671
+ } ;
672
+ /**
673
+ * @param {Object } req
674
+ * @param {http.ServerResponse } res
675
+ */
676
+ async function set_expiration_header ( req , res ) {
677
+ if ( req . method === 'HEAD' || req . method === 'GET' || req . method === 'PUT' ) {
678
+ const rules = req . params . bucket && await req . object_sdk . read_bucket_lifecycle_config_info ( req . params . bucket ) ;
679
+ const object_md = {
680
+ bucket : req . params . bucket ,
681
+ key : req . params . key ,
682
+ size : req . headers [ 'x-amz-decoded-content-length' ] || req . headers [ 'content-length' ] ? parse_content_length ( req , s3_error_options ) : undefined ,
683
+ tagging : req . body && req . body . Tagging ? s3_utils . parse_body_tagging_xml ( req ) : undefined ,
684
+ } ;
685
+
686
+ if ( object_md . key && rules ?. length > 0 ) { // updating x-amz-expiration if object key is present
687
+ for ( const rule of rules ) {
688
+ if ( rule ?. status !== 'Enabled' ) continue ;
689
+
690
+ const filter = rule ?. filter || { } ;
691
+
692
+ if ( filter . prefix && ! object_md ?. key . startsWith ( filter . prefix ) ) continue ;
693
+
694
+ if ( filter . object_size_greater_than && object_md ?. size <= filter . object_size_greater_than ) continue ;
695
+ if ( filter . object_size_less_than && object_md ?. size >= filter . object_size_less_than ) continue ;
696
+
697
+ if ( filter . tagging && Array . isArray ( filter . tagging ) ) {
698
+ const obj_tags = object_md ?. tagging || [ ] ;
699
+
700
+ const matches_all_tags = filter . tagging . every ( filter_tag =>
701
+ obj_tags . some ( obj_tag => obj_tag . key === filter_tag . key && obj_tag . value === filter_tag . value )
702
+ ) ;
703
+
704
+ if ( ! matches_all_tags ) continue ;
705
+ }
706
+
707
+ const expiration_head = parse_expiration_header ( rule ?. expiration , rule ?. id ) ;
708
+ if ( expiration_head ) {
709
+ dbg . log0 ( 'set x_amz_expiration header from applied rule: ' , rule ) ;
710
+ res . setHeader ( 'x-amz-expiration' , expiration_head ) ;
711
+ break ; // apply only for first matching rule
712
+ }
713
+ }
714
+ }
715
+ }
716
+ }
717
+
718
+ /**
719
+ * parse_expiration_header converts an expiration rule (either with `date` or `days`)
720
+ * into an s3 style `x-amz-expiration` header value
721
+ *
722
+ * @param {Object } expiration - expiration object from lifecycle config
723
+ * @param {string } rule_id - id of the lifecycle rule
724
+ * @returns {string|undefined }
725
+ *
726
+ * Example output:
727
+ * expiry-date="Thu, 10 Apr 2025 00:00:00 GMT", rule-id="rule_id"
728
+ */
729
+ function parse_expiration_header ( expiration , rule_id ) {
730
+ if ( ! expiration || ( ! expiration . date && ! expiration . days ) ) return undefined ;
731
+
732
+ const expiration_date = expiration . date ?
733
+ new Date ( expiration . date ) :
734
+ new Date ( Date . UTC (
735
+ new Date ( ) . getUTCFullYear ( ) ,
736
+ new Date ( ) . getUTCMonth ( ) ,
737
+ new Date ( ) . getUTCDate ( ) + expiration . days
738
+ ) ) ;
739
+
740
+ return `expiry-date="${ expiration_date . toUTCString ( ) } ", rule-id="${ rule_id } "` ;
741
+ }
742
+
743
+
667
744
/**
668
745
* @typedef {{
669
746
* allow_origin: string;
@@ -945,6 +1022,7 @@ exports.set_keep_alive_whitespace_interval = set_keep_alive_whitespace_interval;
945
1022
exports . parse_xml_to_js = parse_xml_to_js ;
946
1023
exports . check_headers = check_headers ;
947
1024
exports . set_amz_headers = set_amz_headers ;
1025
+ exports . set_expiration_header = set_expiration_header ;
948
1026
exports . set_cors_headers = set_cors_headers ;
949
1027
exports . set_cors_headers_s3 = set_cors_headers_s3 ;
950
1028
exports . set_cors_headers_sts = set_cors_headers_sts ;
0 commit comments