@@ -376,20 +376,23 @@ elseif signature_mode == "PRESIGN_PART" then
376376 end
377377 local expires = ngx .time () + 3600
378378 local aws_secret_key = os.getenv (' AWS_SECRET_ACCESS_KEY' )
379- -- GCS normalises ':' to '%3A' when computing StringToSign for presigned URLs;
380- -- encode it here so the signature matches what GCS will verify.
381- local url_safe_key = ngx .var .encoded_key :gsub (' :' , ' %%3A' )
382379 -- Normalise uploadId: ngx.var.arg_* may be pre-encoded or raw depending on the nginx
383380 -- version; unescape then re-escape to avoid double-encoding (%2B → %252B).
384381 local escaped_upload_id = ngx .escape_uri (ngx .unescape_uri (upload_id ))
385- -- GCS does NOT include ?partNumber=N&uploadId=X in the canonical resource for presigned
386- -- part PUTs. Standard AWS S3-compatible backends (cloudserver, Scaleway S3) do include
387- -- them per the V2 spec. Detect GCS from ENDPOINT_URL to pick the right behaviour.
382+ -- GCS has two quirks vs standard S3 V2 for presigned part PUTs:
383+ -- 1. It normalises ':' to '%3A' in the key when verifying StringToSign.
384+ -- 2. It excludes ?partNumber=N&uploadId=X from the canonical resource.
385+ -- Standard S3-compatible backends (cloudserver, Scaleway) use literal ':'
386+ -- and include subresources per the V2 spec.
387+ -- ENDPOINT_URL must be declared with 'env ENDPOINT_URL;' in nginx.conf so
388+ -- os.getenv() can read it from Lua workers.
388389 local endpoint_url = os.getenv (' ENDPOINT_URL' ) or ' '
389- local canonicalized_resource
390+ local url_safe_key , canonicalized_resource
390391 if endpoint_url :find (' googleapis' , 1 , true ) then
392+ url_safe_key = ngx .var .encoded_key :gsub (' :' , ' %%3A' )
391393 canonicalized_resource = " /" .. ngx .var .aws_tgt_bucket .. " /" .. url_safe_key
392394 else
395+ url_safe_key = ngx .var .encoded_key
393396 canonicalized_resource = " /" .. ngx .var .aws_tgt_bucket .. " /" .. url_safe_key ..
394397 " ?partNumber=" .. part_number .. " &uploadId=" .. escaped_upload_id
395398 end
0 commit comments