S3互換のオブジェクトストレージ MinioにAWS SDK for C++でアクセスするのにハマるポイント

AWS SDK for C++を利用した開発でMinioを利用する際にハマったのでメモ。


AWS SDK for C++とMinioについては下記をご参考ください。

C++初心者がMacでAWS SDK for C++を利用してAmazon DynamoDBにアクセスする - Qiita
https://qiita.com/kai_kou/items/fe9e3525d2023dc955a5

C++初心者がDockerで開発環境をつくってみた - Qiita
https://qiita.com/kai_kou/items/1f4b9a45a5d4d6788649

S3互換のオブジェクトストレージ MinioをDocker Composeで利用する - Qiita
https://qiita.com/kai_kou/items/9fe06d4d24928d9efa2a


## 実装

AWS SDK for C++のバージョンは1.6.0となります。(2019/02/18時点)

aws/aws-sdk-cpp: AWS SDK for C++
https://github.com/aws/aws-sdk-cpp

下記のサンプルのエンドポイントをMinioに変更して動作するようにします。
C++ Code Samples for Amazon S3 - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/code-catalog-cpp-example_code-s3.html


put_object.cpp - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/cpp-s3-put_object.cpp.html

```cpp:put_object.cpp
/*
   Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

   This file is licensed under the Apache License, Version 2.0 (the "License").
   You may not use this file except in compliance with the License. A copy of
   the License is located at

    http://aws.amazon.com/apache2.0/

   This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied. See the License for the
   specific language governing permissions and limitations under the License.
*/
#include <aws/core/Aws.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/PutObjectRequest.h>
#include <iostream>
#include <fstream>

/**
 * Put an object from an Amazon S3 bucket.
 */
int main(int argc, char** argv)
{
    if (argc < 4)
    {
        std::cout << std::endl <<
            "To run this example, supply the name of an S3 bucket, destination key, and file to upload."
            << std::endl << std::endl <<
            "Ex: put_object <bucketname> <keyname> <filename> <optional:region>" << std::endl;
        exit(1);
    }

    Aws::SDKOptions options;
    Aws::InitAPI(options);
    {
        const Aws::String bucket_name = argv[1];
        const Aws::String key_name = argv[2];
        const Aws::String file_name = argv[3];
        const Aws::String region(argc > 4 ? argv[4] : "");

        std::cout << "Uploading " << file_name << " to S3 bucket " <<
            bucket_name << " at key " << key_name << std::endl;

        Aws::Client::ClientConfiguration clientConfig;
        if (!region.empty())
            clientConfig.region = region;
        Aws::S3::S3Client s3_client(clientConfig);

        Aws::S3::Model::PutObjectRequest object_request;
        object_request.WithBucket(bucket_name).WithKey(key_name);

        // Binary files must also have the std::ios_base::bin flag or'ed in
        auto input_data = Aws::MakeShared<Aws::FStream>("PutObjectInputStream",
            file_name.c_str(), std::ios_base::in | std::ios_base::binary);

        object_request.SetBody(input_data);

        auto put_object_outcome = s3_client.PutObject(object_request);

        if (put_object_outcome.IsSuccess())
        {
            std::cout << "Done!" << std::endl;
        }
        else
        {
            std::cout << "PutObject error: " <<
                put_object_outcome.GetError().GetExceptionName() << " " <<
                put_object_outcome.GetError().GetMessage() << std::endl;
        }
    }
    Aws::ShutdownAPI(options);
}
```

上記サンプルで、Minioへアクセスするようにエンドポイントを指定してやればよいわけですが、いろいろとハマったのでポイントです。

### エンドポイントの指定は`endpointOverride` と`scheme`を利用する

`Aws::Client::ClientConfiguration` にある`endpointOverride` でエンドポイントが指定できます。HTTPアクセスの場合、`scheme` の指定も必要です。

AWS SDK for C++: Aws::Client::ClientConfiguration Struct Reference
https://sdk.amazonaws.com/cpp/api/LATEST/struct_aws_1_1_client_1_1_client_configuration.html

```cpp:put_object.cpp_一部抜粋
    Aws::Client::ClientConfiguration clientConfig;
    if (!region.empty())
      clientConfig.region = region;
    clientConfig.scheme = Aws::Http::Scheme::HTTP;
    clientConfig.endpointOverride = endpoint;
```

公式のドキュメントをみると`authenticationRegion` の指定も必要とありますが、最新バージョン`1.6.0` (2019/02/18時点)では定義されていませんので気をつけましょう。

古いドキュメント
AWS Client Configuration - AWS SDK for C++
https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/client-config.html

古いドキュメント
AWS SDK for C++: Aws::Client::ClientConfiguration Struct Reference
https://sdk.amazonaws.com/cpp/api/0.12.9/df/d19/struct_aws_1_1_client_1_1_client_configuration.html


### `S3Client` 作成時に`useVirtualAdressing` を無効にする

Minioのエンドポイントが`localhost:9000` となり、`useVirtualAdressing` が有効の場合、アクセスすることができません。
`useVirtualAdressing` を指定するコンストラクタでは`Credentials` などを渡す必要があるため、`Aws::Auth::EnvironmentAWSCredentialsProvider().GetAWSCredentials()`を利用します。アクセスキーとシークレットをパラメータに`Credentials` を作成してもOKです。

AWS SDK for C++: Aws::S3::S3Client Class Reference
https://sdk.amazonaws.com/cpp/api/LATEST/class_aws_1_1_s3_1_1_s3_client.html#ac8168b12d0e630dcdc5cd912d38d8787

`VirtualAdressing` (仮想ホスト形式)については下記が参考になります。

S3のバケットURL(仮想ホスト形式とパス形式) - Qiita
https://qiita.com/ryo0301/items/48120379b240ab071028

AWS CLIのS3接続先の選択動作とaddressing_styleオプション - おぎろぐはてブロ
https://iogi.hatenablog.com/entry/s3-addressing-style

```cpp:put_object.cpp_一部抜粋
#include <aws/core/auth/AWSCredentialsProvider.h>
#include <aws/core/auth/AWSAuthSigner.h>

(略)

    // 環境変数などから認証情報を取得する
    Aws::Auth::AWSCredentials cred = Aws::Auth::EnvironmentAWSCredentialsProvider().GetAWSCredentials();
    // useVirtualAdressingをfalseにする
    Aws::S3::S3Client s3_client(cred, clientConfig, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, false);
```

こちらも過去バージョンだとS3Clientコストラクタのパラメータが一部異なるので注意しましょう。

古いドキュメント
AWS SDK for C++: Aws::S3::S3Client Class Reference
https://sdk.amazonaws.com/cpp/api/0.12.9/d7/db7/class_aws_1_1_s3_1_1_s3_client.html


## まとめ

AWS SDK for C++のバージョンによって仕様が異なり、ネットで検索して得られる情報では解決できない可能性がありますので、最終的には実装や最新のドキュメントを確認するようにしましょう。


## 参考

C++初心者がMacでAWS SDK for C++を利用してAmazon DynamoDBにアクセスする - Qiita
https://qiita.com/kai_kou/items/fe9e3525d2023dc955a5

C++初心者がDockerで開発環境をつくってみた - Qiita
https://qiita.com/kai_kou/items/1f4b9a45a5d4d6788649

aws/aws-sdk-cpp: AWS SDK for C++
https://github.com/aws/aws-sdk-cpp

C++ Code Samples for Amazon S3 - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/code-catalog-cpp-example_code-s3.html

put_object.cpp - AWS Code Sample
https://docs.aws.amazon.com/ja_jp/code-samples/latest/catalog/cpp-s3-put_object.cpp.html

AWS SDK for C++: Aws::Client::ClientConfiguration Struct Reference
https://sdk.amazonaws.com/cpp/api/LATEST/struct_aws_1_1_client_1_1_client_configuration.html

AWS SDK for C++: Aws::S3::S3Client Class Reference
https://sdk.amazonaws.com/cpp/api/LATEST/class_aws_1_1_s3_1_1_s3_client.html#ac8168b12d0e630dcdc5cd912d38d8787

S3のバケットURL(仮想ホスト形式とパス形式) - Qiita
https://qiita.com/ryo0301/items/48120379b240ab071028

AWS CLIのS3接続先の選択動作とaddressing_styleオプション - おぎろぐはてブロ
https://iogi.hatenablog.com/entry/s3-addressing-style

S3互換のオブジェクトストレージ MinioをDocker Composeで利用する - Qiita
https://qiita.com/kai_kou/items/9fe06d4d24928d9efa2a