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