1
#include "source/common/config/remote_data_fetcher.h"
2

            
3
#include "envoy/config/core/v3/http_uri.pb.h"
4

            
5
#include "source/common/common/enum_to_int.h"
6
#include "source/common/common/hex.h"
7
#include "source/common/crypto/utility.h"
8
#include "source/common/http/headers.h"
9
#include "source/common/http/utility.h"
10

            
11
namespace Envoy {
12
namespace Config {
13
namespace DataFetcher {
14

            
15
RemoteDataFetcher::RemoteDataFetcher(Upstream::ClusterManager& cm,
16
                                     const envoy::config::core::v3::HttpUri& uri,
17
                                     const std::string& content_hash,
18
                                     RemoteDataFetcherCallback& callback)
19
51
    : cm_(cm), uri_(uri), content_hash_(content_hash), callback_(callback) {}
20

            
21
51
RemoteDataFetcher::~RemoteDataFetcher() { cancel(); }
22

            
23
51
void RemoteDataFetcher::cancel() {
24
51
  if (request_) {
25
20
    request_->cancel();
26
20
    ENVOY_LOG(debug, "fetch remote data [uri = {}]: canceled", uri_.uri());
27
20
  }
28

            
29
51
  request_ = nullptr;
30
51
}
31

            
32
64
void RemoteDataFetcher::fetch() {
33
64
  Http::RequestMessagePtr message = Http::Utility::prepareHeaders(uri_);
34
64
  message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get);
35
64
  ENVOY_LOG(debug, "fetch remote data from [uri = {}]: start", uri_.uri());
36
64
  const auto thread_local_cluster = cm_.getThreadLocalCluster(uri_.cluster());
37
64
  if (thread_local_cluster != nullptr) {
38
61
    request_ = thread_local_cluster->httpAsyncClient().send(
39
61
        std::move(message), *this,
40
61
        Http::AsyncClient::RequestOptions().setTimeout(
41
61
            std::chrono::milliseconds(DurationUtil::durationToMilliseconds(uri_.timeout()))));
42
61
  } else {
43
3
    ENVOY_LOG(debug, "fetch remote data [uri = {}]: no cluster {}", uri_.uri(), uri_.cluster());
44
3
    callback_.onFailure(FailureReason::Network);
45
3
  }
46
64
}
47

            
48
void RemoteDataFetcher::onSuccess(const Http::AsyncClient::Request&,
49
57
                                  Http::ResponseMessagePtr&& response) {
50
57
  const uint64_t status_code = Http::Utility::getResponseStatus(response->headers());
51
57
  if (status_code == enumToInt(Http::Code::OK)) {
52
36
    ENVOY_LOG(debug, "fetch remote data [uri = {}]: success", uri_.uri());
53
36
    if (response->body().length() > 0) {
54
34
      auto& crypto_util = Envoy::Common::Crypto::UtilitySingleton::get();
55
34
      const auto content_hash = Hex::encode(crypto_util.getSha256Digest(response->body()));
56

            
57
34
      if (content_hash_ != content_hash) {
58
6
        ENVOY_LOG(debug, "fetch remote data [uri = {}]: data is invalid", uri_.uri());
59
6
        callback_.onFailure(FailureReason::InvalidData);
60
30
      } else {
61
28
        callback_.onSuccess(response->bodyAsString());
62
28
      }
63
34
    } else {
64
2
      ENVOY_LOG(debug, "fetch remote data [uri = {}]: body is empty", uri_.uri());
65
2
      callback_.onFailure(FailureReason::Network);
66
2
    }
67
42
  } else {
68
21
    ENVOY_LOG(debug, "fetch remote data [uri = {}]: response status code {}", uri_.uri(),
69
21
              status_code);
70
21
    callback_.onFailure(FailureReason::Network);
71
21
  }
72

            
73
57
  request_ = nullptr;
74
57
}
75

            
76
void RemoteDataFetcher::onFailure(const Http::AsyncClient::Request&,
77
4
                                  Http::AsyncClient::FailureReason reason) {
78
4
  ENVOY_LOG(debug, "fetch remote data [uri = {}]: network error {}", uri_.uri(), enumToInt(reason));
79
4
  request_ = nullptr;
80
4
  callback_.onFailure(FailureReason::Network);
81
4
}
82

            
83
} // namespace DataFetcher
84
} // namespace Config
85
} // namespace Envoy