1 #include "amazon/AmazonS3Client.h"
2 #include <stdexcept>
3 #include <memory>
4 #include "string_format.h"
5 #include "xml/Parser.h"
6
7 #define S3_HOSTNAME "s3.amazonaws.com"
8
CAmazonS3Client(std::string accessKeyId,std::string secretAccessKey,std::string region)9 CAmazonS3Client::CAmazonS3Client(std::string accessKeyId, std::string secretAccessKey, std::string region)
10 : CAmazonClient("s3", accessKeyId, secretAccessKey, region)
11 {
12 }
13
GetBucketLocation(const GetBucketLocationRequest & request)14 GetBucketLocationResult CAmazonS3Client::GetBucketLocation(const GetBucketLocationRequest& request)
15 {
16 if(request.bucket.empty())
17 {
18 throw new std::runtime_error("Bucket name must be provided.");
19 }
20
21 Request rq;
22 rq.method = Framework::Http::HTTP_VERB::GET;
23 rq.host = string_format("%s." S3_HOSTNAME, request.bucket.c_str());
24 rq.urlHost = S3_HOSTNAME;
25 rq.uri = "/";
26 //We add a bucket parameter even if the S3 API doesn't use it to prevent caching
27 rq.query = string_format("bucket=%s&location=", request.bucket.c_str());
28
29 auto response = ExecuteRequest(rq);
30 if(response.statusCode != Framework::Http::HTTP_STATUS_CODE::OK)
31 {
32 throw std::runtime_error("Failed to get bucket location.");
33 }
34
35 GetBucketLocationResult result;
36
37 auto documentNode = std::unique_ptr<Framework::Xml::CNode>(Framework::Xml::CParser::ParseDocument(response.data));
38 auto locationConstraintNode = documentNode->Select("LocationConstraint");
39 if(locationConstraintNode)
40 {
41 auto locationConstraint = locationConstraintNode->GetInnerText();
42 result.locationConstraint = locationConstraint ? locationConstraint : "";
43 }
44
45 return result;
46 }
47
GetObject(const GetObjectRequest & request)48 GetObjectResult CAmazonS3Client::GetObject(const GetObjectRequest& request)
49 {
50 Request rq;
51 rq.method = Framework::Http::HTTP_VERB::GET;
52 rq.uri = "/" + Framework::Http::CHttpClient::UrlEncode(request.object);
53 rq.host = string_format("%s.s3-%s.amazonaws.com", request.bucket.c_str(), m_region.c_str());
54 rq.urlHost = rq.host;
55
56 if(request.range.first != request.range.second)
57 {
58 auto rangeHeader = string_format("bytes=%llu-%llu", request.range.first, request.range.second);
59 rq.headers.insert(std::make_pair("Range", rangeHeader));
60 }
61
62 auto response = ExecuteRequest(rq);
63 if(
64 (response.statusCode != Framework::Http::HTTP_STATUS_CODE::OK) &&
65 (response.statusCode != Framework::Http::HTTP_STATUS_CODE::PARTIAL_CONTENT))
66 {
67 throw std::runtime_error("Failed to get object.");
68 }
69
70 GetObjectResult result;
71 uint32 dataLength = response.data.GetLength();
72 result.data.resize(dataLength);
73 response.data.Read(result.data.data(), dataLength);
74 return result;
75 }
76
HeadObject(const HeadObjectRequest & request)77 HeadObjectResult CAmazonS3Client::HeadObject(const HeadObjectRequest& request)
78 {
79 Request rq;
80 rq.method = Framework::Http::HTTP_VERB::HEAD;
81 rq.uri = "/" + Framework::Http::CHttpClient::UrlEncode(request.object);
82 rq.host = string_format("%s.s3-%s.amazonaws.com", request.bucket.c_str(), m_region.c_str());
83 rq.urlHost = rq.host;
84
85 auto response = ExecuteRequest(rq);
86 if(response.statusCode != Framework::Http::HTTP_STATUS_CODE::OK)
87 {
88 throw std::runtime_error("Failed to head object.");
89 }
90
91 HeadObjectResult result;
92
93 auto contentLengthIterator = response.headers.find("Content-Length");
94 if(contentLengthIterator != std::end(response.headers))
95 {
96 result.contentLength = atoll(contentLengthIterator->second.c_str());
97 }
98
99 auto etagIterator = response.headers.find("ETag");
100 if(etagIterator == std::end(response.headers)) etagIterator = response.headers.find("Etag");
101 if(etagIterator != std::end(response.headers))
102 {
103 result.etag = etagIterator->second;
104 }
105
106 return result;
107 }
108
ListObjects(std::string bucket)109 ListObjectsResult CAmazonS3Client::ListObjects(std::string bucket)
110 {
111 Request rq;
112 rq.method = Framework::Http::HTTP_VERB::GET;
113 rq.uri = "/";
114 rq.host = string_format("%s.s3-%s.amazonaws.com", bucket.c_str(), m_region.c_str());
115 rq.urlHost = rq.host;
116
117 auto response = ExecuteRequest(rq);
118 if(response.statusCode != Framework::Http::HTTP_STATUS_CODE::OK)
119 {
120 throw std::runtime_error("Failed to list objects");
121 }
122
123 ListObjectsResult result;
124
125 auto documentNode = std::unique_ptr<Framework::Xml::CNode>(Framework::Xml::CParser::ParseDocument(response.data));
126 auto contentsNodes = documentNode->SelectNodes("ListBucketResult/Contents");
127 for(const auto& contentsNode : contentsNodes)
128 {
129 Object object;
130 if(auto keyNode = contentsNode->Select("Key"))
131 {
132 object.key = keyNode->GetInnerText();
133 }
134 result.objects.push_back(object);
135 }
136
137 return result;
138 }
139