leonard
leonard

Reputation: 2387

iOS AWS v2 S3 Transfer Manager must use specified endpoint

I am developing an iOS app using AWS as its backend. Since I expect users to be world wide, I need to switch where the app should download photos in order to increase latency performance. I have created a S3 bucket in EUWest1 and the other in APNorthEast1. As Yosuke said here (https://forums.aws.amazon.com/message.jspa?messageID=586581#586581), you can change the default configuration of AWSServiceManager.defaultServiceManager() from one region to another in order to prevent the error.

But, I still have the same error result even if I followed his tip. Error is:

Error Domain=com.amazonaws.AWSS3ErrorDomain Code=0 "The operation couldn’t be completed. (com.amazonaws.AWSS3ErrorDomain error 0.)" UserInfo=0x14fb7720 {RequestId=D708XXX8E1EDBXXX, Endpoint=bucket-ap-northeast-1.s3-ap-northeast-1.amazonaws.com, Message=The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint., HostId=xxxY31S3yFhpssQe1EsfipWWLw=, Code=PermanentRedirect, Bucket=bucket-ap-northeast-1}

And this is my code:

var credentialsProvider = AWSCognitoCredentialsProvider.credentialsWithRegionType(kCognitoRegionType, accountId: kAWSAccountID, identityPoolId: kCognitoPoolID, unauthRoleArn: kCognitoRoleUnauth, authRoleArn: kCognitoRoleAuth)
var serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.APNortheast1, credentialsProvider: credentialsProvider)
AWSServiceManager.defaultServiceManager().setDefaultServiceConfiguration(serviceConfiguration)
var transferManager = AWSS3TransferManager.defaultS3TransferManager()
transferManager.download(downloadRequest)

Any advice is welcome! Thanks!

Upvotes: 1

Views: 6092

Answers (3)

PDK
PDK

Reputation: 1536

Whenever the Amazon AWS Mobile SDK displays an unclear warning in the console, turn on verbose logging to debug:

(Swift 3) AWSLogger.default().logLevel = .verbose
(Swift 2) AWSLogger.defaultLogger().logLevel = .Verbose

…to reveal more information about what S3 buckets, regions, endpoints etc. are being used.


I found this particularly useful to find out what was happening when trying to switch between buckets, because the error message

… Endpoint=bucket-ap-northeast-1.s3-ap-northeast-1.amazonaws.com, Message=The bucket you are attempting to access must be addressed using the specified endpoint. …

…doesn't provide much information about the actual/current transfer manager configuration.

Upvotes: 0

sudo
sudo

Reputation: 1658

Was having the same issue just recently. The AWS SDK Docs are terribly confusing and contradicting, they suck...

The answer above pointed me in the right direction since I'm using developer authenticated identities, I am using the custom identity provider (same one that's in the sample docs on github). I used registerS3TransferManagerWithConfiguration with the my credentials provider but then supplied AWSRegionUSWest2 as the region for key USWest2S3TransferManager.

I then called AWSS3TransferManager *transferManager = [AWSS3TransferManager S3TransferManagerForKey:@"USWest2S3TransferManager"]; in my upload class.

Hopefully this helps someone else that's having similar issues.

- (AWSTask *)initializeClients:(NSDictionary *)logins {
    NSLog(@"initializing clients...");
    [AWSLogger defaultLogger].logLevel = AWSLogLevelVerbose;

    id<AWSCognitoIdentityProvider> identityProvider = [[DeveloperAuthenticatedIdentityProvider alloc] initWithRegionType:AWSRegionUSEast1
                                                                                                              identityId:nil
                                                                                                          identityPoolId:identityPoolID
                                                                                                                  logins:logins
                                                                                                            providerName:DeveloperAuthProviderName
                                                                                                              authClient:nil];

    self.credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
                                                                        identityProvider:identityProvider
                                                                           unauthRoleArn:nil
                                                                             authRoleArn:nil];


    AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:self.credentialsProvider];

    [AWSS3TransferManager registerS3TransferManagerWithConfiguration:[[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSWest2 credentialsProvider:self.credentialsProvider] forKey:@"USWest2S3TransferManager"];

    AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;

    return [self.credentialsProvider getIdentityId];
}

my specific upload method:

- (void)performS3UploadWithRequest:(AWSS3TransferManagerUploadRequest *)request
{
    AWSS3TransferManager *transferManager = [AWSS3TransferManager S3TransferManagerForKey:@"USWest2S3TransferManager"];
    AWSTask *task = [AWSTask taskWithResult:nil];

    __weak typeof(self) weakSelf = self;
    task = [task continueWithSuccessBlock:^id(AWSTask *task) {
        return [[transferManager upload:request] continueWithBlock:^id(AWSTask *task) {
            if (task.error) {
                [self handleErrorWithTask:task request:request];
            }

            if (task.result) {
                AWSS3TransferManagerUploadOutput *uploadOutput = task.result;
                dispatch_async(dispatch_get_main_queue(), ^{
                    [weakSelf uploadProgress];
                });
                // The file uploaded successfully.
                DLog(@"%@", uploadOutput);
            }

            return nil;
        }];
    }];

    [task continueWithSuccessBlock:^id(AWSTask *task) {
        return nil;
    }];
}

Upvotes: 7

Lyndsey Scott
Lyndsey Scott

Reputation: 37290

Answer for AWS v2

Although it seems as if you properly set the AWSServiceConfiguration by using the region in your code, you're still using the default AWSS3TransferManager (var transferManager = AWSS3TransferManager.defaultS3TransferManager()). Instead, use the custom init specified in the docs to set that transfer manager's configuration to the serviceConfiguration you've already created.


Answer for AWS v1

If you create a bucket like EUWest1 and APNorthEast1 that is not US Standard, you cannot use typical path-style syntax ("http://s3.amazonaws.com" or no specified endpoint required) to access the bucket.

Assuming downloadRequest is an S3GetObjectRequest, before performing the transferManager's download, you should set the endpoint of the S3GetObjectRequest to match the bucket region.

For EUWest1, you can set the endpoint to:

[downloadRequest setEndpoint:@"s3-eu-west-1.amazonaws.com.com"];

For APNorthEast1, you can set the endpoint to:

[downloadRequest setEndpoint:@"s3-ap-northeast-1.amazonaws.com"];

The entire list of endpoints is available here

Upvotes: 6

Related Questions