diff --git a/lua/compute_aws_s3_signature.lua b/lua/compute_aws_s3_signature.lua index 4c6bfad..27c6e8e 100644 --- a/lua/compute_aws_s3_signature.lua +++ b/lua/compute_aws_s3_signature.lua @@ -291,10 +291,21 @@ elseif signature_mode == "MULTIPART_UPLOAD_PART" then if not part_number or part_number == "" or not upload_id or upload_id == "" then return ngx.exit(ngx.HTTP_BAD_REQUEST) end - compute_S3_signature_with_resource( - "x-amz-date:" .. ngx.var.x_amz_date, - "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?partNumber=" .. part_number .. "&uploadId=" .. upload_id - ) + -- GCS does not include partNumber/uploadId in the canonical resource and + -- normalises ':' to '%3A' in the key — same quirks as for presigned part PUTs. + -- Standard S3-compatible backends include subresources and keep literal ':'. + if (os.getenv('ENDPOINT_URL') or ''):find('googleapis', 1, true) then + ngx.var.encoded_key = ngx.var.encoded_key:gsub(':', '%%3A') + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key + ) + else + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?partNumber=" .. part_number .. "&uploadId=" .. upload_id + ) + end elseif signature_mode == "MULTIPART_COMPLETE" then @@ -309,10 +320,21 @@ elseif signature_mode == "MULTIPART_COMPLETE" then if not upload_id or upload_id == "" then return ngx.exit(ngx.HTTP_BAD_REQUEST) end - compute_S3_signature_with_resource( - "x-amz-date:" .. ngx.var.x_amz_date, - "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?uploadId=" .. upload_id - ) + -- GCS does not include uploadId in the canonical resource and normalises + -- ':' to '%3A' in the key. Standard S3-compatible backends include the + -- subresource and keep literal ':'. + if (os.getenv('ENDPOINT_URL') or ''):find('googleapis', 1, true) then + ngx.var.encoded_key = ngx.var.encoded_key:gsub(':', '%%3A') + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key + ) + else + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?uploadId=" .. upload_id + ) + end elseif signature_mode == "MULTIPART_ABORT" then @@ -327,10 +349,21 @@ elseif signature_mode == "MULTIPART_ABORT" then if not upload_id or upload_id == "" then return ngx.exit(ngx.HTTP_BAD_REQUEST) end - compute_S3_signature_with_resource( - "x-amz-date:" .. ngx.var.x_amz_date, - "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?uploadId=" .. upload_id - ) + -- GCS does not include uploadId in the canonical resource and normalises + -- ':' to '%3A' in the key. Standard S3-compatible backends include the + -- subresource and keep literal ':'. + if (os.getenv('ENDPOINT_URL') or ''):find('googleapis', 1, true) then + ngx.var.encoded_key = ngx.var.encoded_key:gsub(':', '%%3A') + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key + ) + else + compute_S3_signature_with_resource( + "x-amz-date:" .. ngx.var.x_amz_date, + "/" .. ngx.var.aws_tgt_bucket .. "/" .. ngx.var.encoded_key .. "?uploadId=" .. upload_id + ) + end elseif signature_mode == "PRESIGN_PUT" then