Skip to content

Commit 4ffea5c

Browse files
committed
Added x-amz-expiration, missing HTTP header in the response of object GET/PUT/HEAD
Signed-off-by: Aayush Chouhan <[email protected]>
1 parent 20d8f05 commit 4ffea5c

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

src/api/bucket_api.js

+1
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ module.exports = {
12151215
$ref: 'common_api#/definitions/bucket_policy'
12161216
},
12171217
replication_policy_id: { objectid: true },
1218+
lifecycle_configuration_rules: { $ref: 'common_api#/definitions/bucket_lifecycle_configuration' },
12181219
}
12191220
},
12201221

src/endpoint/s3/s3_rest.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,13 @@ async function handle_request(req, res) {
116116
}
117117

118118
const op_name = parse_op_name(req);
119-
const cors = req.params.bucket && await req.object_sdk.read_bucket_sdk_cors_info(req.params.bucket);
120119

120+
const cors = req.params.bucket && await req.object_sdk.read_bucket_sdk_cors_info(req.params.bucket);
121121
http_utils.set_cors_headers_s3(req, res, cors);
122122

123+
const rules = req.params.bucket && await req.object_sdk.read_bucket_lifecycle_config_info(req.params.bucket);
124+
http_utils.set_expiration_header(req, res, rules);
125+
123126
if (req.method === 'OPTIONS') {
124127
dbg.log1('s3_rest: handle_request : S3 request method is ', req.method);
125128
const error_code = req.headers.origin && req.headers['access-control-request-method'] ? 403 : 400;
@@ -139,6 +142,7 @@ async function handle_request(req, res) {
139142
authenticate_request(req);
140143
await authorize_request(req);
141144

145+
142146
dbg.log1('S3 REQUEST', req.method, req.originalUrl, 'op', op_name, 'request_id', req.request_id, req.headers);
143147
usage_report.s3_usage_info.total_calls += 1;
144148
usage_report.s3_usage_info[op_name] = (usage_report.s3_usage_info[op_name] || 0) + 1;

src/sdk/object_sdk.js

+5
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ class ObjectSDK {
223223
return policy_info;
224224
}
225225

226+
async read_bucket_lifecycle_config_info(name) {
227+
const { bucket } = await bucket_namespace_cache.get_with_cache({ sdk: this, name });
228+
return bucket.bucket_info.lifecycle_configuration_rules;
229+
}
230+
226231
async read_bucket_usage_info(name) {
227232
const { bucket } = await bucket_namespace_cache.get_with_cache({ sdk: this, name });
228233
return bucket.bucket_info.data;

src/server/system_services/bucket_server.js

+1
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,7 @@ function get_bucket_info({
16531653
website: bucket.website,
16541654
s3_policy: bucket.s3_policy,
16551655
replication_policy_id: bucket.replication_policy_id,
1656+
lifecycle_configuration_rules: bucket.lifecycle_configuration_rules,
16561657
};
16571658

16581659
const metrics = _calc_metrics({ bucket, nodes_aggregate_pool, hosts_aggregate_pool, tiering_pools_status, info });

src/util/http_utils.js

+45
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,50 @@ function set_amz_headers(req, res) {
664664
res.setHeader('x-amz-id-2', req.request_id);
665665
}
666666

667+
/**
668+
* @param {Object} req
669+
* @param {http.ServerResponse} res
670+
* @param {Object} rules
671+
*/
672+
async function set_expiration_header(req, res, rules) {
673+
if (req.method === 'HEAD' || req.method === 'GET' || req.method === 'PUT') {
674+
// TODO: For every object key we need to apply the mupltiple rules obtained in lifecycle and then update the x-amz-expiration accordingly
675+
// currently putting the first rule expiration to every object
676+
if (rules?.length > 0) {
677+
const expiration_head = parse_expiration_header(rules[0]?.expiration, rules[0]?.id);
678+
if (expiration_head) {
679+
res.setHeader('x-amz-expiration', expiration_head);
680+
}
681+
}
682+
}
683+
}
684+
685+
/**
686+
* parse_expiration_header converts an expiration rule (either with `date` or `days`)
687+
* into an s3 style `x-amz-expiration` header value
688+
*
689+
* @param {Object} expiration - expiration object from lifecycle config
690+
* @param {string} rule_id - id of the lifecycle rule
691+
* @returns {string|undefined}
692+
*
693+
* Example output:
694+
* expiry-date="Thu, 10 Apr 2025 00:00:00 GMT", rule-id="rule_id"
695+
*/
696+
function parse_expiration_header(expiration, rule_id) {
697+
if (!expiration || (!expiration.date && !expiration.days)) return undefined;
698+
699+
const expiration_date = expiration.date ?
700+
new Date(expiration.date) :
701+
new Date(Date.UTC(
702+
new Date().getUTCFullYear(),
703+
new Date().getUTCMonth(),
704+
new Date().getUTCDate() + expiration.days
705+
));
706+
707+
return `expiry-date="${expiration_date.toUTCString()}", rule-id="${rule_id}"`;
708+
}
709+
710+
667711
/**
668712
* @typedef {{
669713
* allow_origin: string;
@@ -945,6 +989,7 @@ exports.set_keep_alive_whitespace_interval = set_keep_alive_whitespace_interval;
945989
exports.parse_xml_to_js = parse_xml_to_js;
946990
exports.check_headers = check_headers;
947991
exports.set_amz_headers = set_amz_headers;
992+
exports.set_expiration_header = set_expiration_header;
948993
exports.set_cors_headers = set_cors_headers;
949994
exports.set_cors_headers_s3 = set_cors_headers_s3;
950995
exports.set_cors_headers_sts = set_cors_headers_sts;

0 commit comments

Comments
 (0)