aws / aws-sdk-js-v3

Modularized AWS SDK for JavaScript.
Apache License 2.0
3.13k stars 579 forks source link

HeadBucket does not return Bucket Region header #6607

Open kouta-kun opened 1 year ago

kouta-kun commented 1 year ago

Describe the issue

The AWS-SDK documentation recommends using HeadBucket instead of GetBucketLocation for determining Bucket region:

We recommend that you use [HeadBucket ](https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html)
to return the Region that a bucket resides in. For backward compatibility, Amazon S3 continues to support GetBucketLocation.

However, the header that contains this piece of information is not contained in the $metadata attribute of the HeadBucketCommandOutput object. According to Generic documentation, this information is returned in the x-amz-bucket-region header, which does not seem to be part of the RequestMetadata (does not show in a debugger either).

Links

https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadBucket.html https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/command/GetBucketLocationCommand/

kouta-kun commented 1 year ago

Something like this can be used as a monkeypatch workaround:

    // https://github.com/aws/aws-sdk-js-v3/issues/6607 workaround due to HeadBucket not returning region at this moment
    const cmd_ = new HeadBucketCommand({
      Bucket: bucketName,
    });
    const command = cmd_ as unknown as {
      deserialize: DeserializeFunction;
      region: string | undefined;
    };
    const oldDeser = command.deserialize;
    command.deserialize = (output, context) => {
      command.region = output.headers["x-amz-bucket-region"];
      return oldDeser(output, context);
    };
    const cmd = cmd_ as HeadBucketCommand & { region: string | undefined };
    await new S3Client({
      region: "us-east-1",
    }).send(cmd);

    if (cmd.region === undefined) {
      console.error("No bucket region found!");
      throw new Error("No bucket region found");
    } else {
      console.error(bucketName, "is in", cmd.region);
    }
RanVaknin commented 1 year ago

Hi @kouta-kun ,

The response is missing the metadata header because even though it is sent back, the S3 API model doesnt not model a response at all for this operation. You can check the model file and see that the generated type is the generic smithy response object. This generic type includes only httpStatusCode, requestId, extendedRequestId and cfId.

If you didn't know, each SDK client is code generated from its respective service API model. That means that if the model is lacking a definition for something, the data wont get deserialized / serialized.

You can however you can create your own middleware step, and pipe the header into the response manually:

const client = new S3Client({ region: 'us-east-1' });

client.middlewareStack.add( (next) => async (args) => {
      const result = await next(args);
      result.output.$metadata.region = result.response.headers['x-amz-bucket-region']
      return result;
    },
    { 
        step: 'finalize',
        name: "myMiddleware",
        override: true
    }
  );
  // rest of your code 

printing the response of headBucket will result in:

{
  '$metadata': {
    httpStatusCode: 200,
    requestId: 'REDACTED',
    extendedRequestId: 'REDACTED',
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0,
    region: 'us-east-1'
  }
}

So while the API behaves as documented, unfortunately the API model from which the SDK is generated from is lacking the desired output shape.

I have raised an internal PR to get this sorted out, but S3 is a really large and distributed team so finding an owner that can review this internally might prove challenging.

Please keep monitoring this ticket for further updates. Thanks again, Ran~

P97036807