1
#include "source/common/upstream/health_checker_impl.h"
2

            
3
#include <cstdint>
4
#include <iterator>
5
#include <memory>
6

            
7
#include "envoy/config/core/v3/health_check.pb.h"
8
#include "envoy/data/core/v3/health_check_event.pb.h"
9
#include "envoy/server/health_checker_config.h"
10
#include "envoy/type/v3/http.pb.h"
11
#include "envoy/type/v3/range.pb.h"
12

            
13
#include "source/common/buffer/zero_copy_input_stream_impl.h"
14
#include "source/common/common/empty_string.h"
15
#include "source/common/common/enum_to_int.h"
16
#include "source/common/common/macros.h"
17
#include "source/common/config/utility.h"
18
#include "source/common/config/well_known_names.h"
19
#include "source/common/grpc/common.h"
20
#include "source/common/http/header_map_impl.h"
21
#include "source/common/http/header_utility.h"
22
#include "source/common/network/address_impl.h"
23
#include "source/common/network/socket_impl.h"
24
#include "source/common/network/utility.h"
25
#include "source/common/router/router.h"
26
#include "source/common/runtime/runtime_features.h"
27
#include "source/common/upstream/host_utility.h"
28

            
29
#include "absl/status/statusor.h"
30
#include "absl/strings/match.h"
31
#include "absl/strings/str_cat.h"
32

            
33
namespace Envoy {
34
namespace Upstream {
35

            
36
// Helper functions to get the correct hostname for an L7 health check.
37
const std::string& HealthCheckerFactory::getHostname(const HostSharedPtr& host,
38
                                                     const std::string& config_hostname,
39
334
                                                     const ClusterInfoConstSharedPtr& cluster) {
40
334
  if (!host->hostnameForHealthChecks().empty()) {
41
4
    return host->hostnameForHealthChecks();
42
4
  }
43

            
44
330
  if (!config_hostname.empty()) {
45
5
    return config_hostname;
46
5
  }
47

            
48
325
  return cluster->name();
49
330
}
50

            
51
absl::StatusOr<HealthCheckerSharedPtr>
52
HealthCheckerFactory::create(const envoy::config::core::v3::HealthCheck& health_check_config,
53
                             Upstream::Cluster& cluster,
54
176
                             Server::Configuration::ServerFactoryContext& server_context) {
55
176
  Server::Configuration::CustomHealthCheckerFactory* factory = nullptr;
56

            
57
176
  switch (health_check_config.health_checker_case()) {
58
  case envoy::config::core::v3::HealthCheck::HealthCheckerCase::HEALTH_CHECKER_NOT_SET:
59
    return absl::InvalidArgumentError("invalid cluster config");
60
144
  case envoy::config::core::v3::HealthCheck::HealthCheckerCase::kHttpHealthCheck:
61
144
    factory = &Config::Utility::getAndCheckFactoryByName<
62
144
        Server::Configuration::CustomHealthCheckerFactory>("envoy.health_checkers.http");
63
144
    break;
64
22
  case envoy::config::core::v3::HealthCheck::HealthCheckerCase::kTcpHealthCheck:
65
22
    factory = &Config::Utility::getAndCheckFactoryByName<
66
22
        Server::Configuration::CustomHealthCheckerFactory>("envoy.health_checkers.tcp");
67
22
    break;
68
8
  case envoy::config::core::v3::HealthCheck::HealthCheckerCase::kGrpcHealthCheck:
69
8
    if (!(cluster.info()->features() & Upstream::ClusterInfo::Features::HTTP2)) {
70
2
      return absl::InvalidArgumentError(fmt::format(
71
2
          "{} cluster must support HTTP/2 for gRPC healthchecking", cluster.info()->name()));
72
2
    }
73
6
    factory = &Config::Utility::getAndCheckFactoryByName<
74
6
        Server::Configuration::CustomHealthCheckerFactory>("envoy.health_checkers.grpc");
75
6
    break;
76
2
  case envoy::config::core::v3::HealthCheck::HealthCheckerCase::kCustomHealthCheck: {
77
2
    factory =
78
2
        &Config::Utility::getAndCheckFactory<Server::Configuration::CustomHealthCheckerFactory>(
79
2
            health_check_config.custom_health_check());
80
2
  }
81
176
  }
82

            
83
174
  std::unique_ptr<Server::Configuration::HealthCheckerFactoryContext> context(
84
174
      new HealthCheckerFactoryContextImpl(cluster, server_context));
85

            
86
174
  if (!health_check_config.event_log_path().empty() /* deprecated */ ||
87
174
      !health_check_config.event_logger().empty()) {
88
    auto logger_or_error = HealthCheckEventLoggerImpl::create(health_check_config, *context);
89
    RETURN_IF_NOT_OK_REF(logger_or_error.status());
90
    context->setEventLogger(std::move(*logger_or_error));
91
  }
92
174
  return factory->createCustomHealthChecker(health_check_config, *context);
93
174
}
94

            
95
absl::StatusOr<std::vector<uint8_t>>
96
93
PayloadMatcher::decodePayload(const envoy::config::core::v3::HealthCheck::Payload& payload) {
97
93
  std::vector<uint8_t> decoded;
98
93
  if (payload.has_text()) {
99
80
    decoded = Hex::decode(payload.text());
100
80
    if (decoded.empty()) {
101
3
      return absl::InvalidArgumentError(fmt::format("invalid hex string '{}'", payload.text()));
102
3
    }
103
80
  } else {
104
13
    decoded.assign(payload.binary().begin(), payload.binary().end());
105
13
  }
106
90
  return decoded;
107
93
}
108

            
109
absl::StatusOr<PayloadMatcher::MatchSegments> PayloadMatcher::loadProtoBytes(
110
276
    const Protobuf::RepeatedPtrField<envoy::config::core::v3::HealthCheck::Payload>& byte_array) {
111
276
  MatchSegments segments;
112
276
  for (const auto& payload : byte_array) {
113
50
    auto decoded_or_error = decodePayload(payload);
114
50
    if (!decoded_or_error.ok()) {
115
2
      return decoded_or_error.status();
116
2
    }
117
48
    if (!decoded_or_error.value().empty()) {
118
48
      segments.emplace_back(std::move(decoded_or_error.value()));
119
48
    }
120
48
  }
121
274
  return segments;
122
276
}
123

            
124
absl::StatusOr<PayloadMatcher::MatchSegments> PayloadMatcher::loadProtoBytes(
125
43
    const envoy::config::core::v3::HealthCheck::Payload& single_payload) {
126
43
  auto decoded_or_error = decodePayload(single_payload);
127
43
  if (!decoded_or_error.ok()) {
128
1
    return decoded_or_error.status();
129
1
  }
130
42
  return MatchSegments{std::move(decoded_or_error.value())};
131
43
}
132

            
133
38
bool PayloadMatcher::match(const MatchSegments& expected, const Buffer::Instance& buffer) {
134
38
  uint64_t start_index = 0;
135
42
  for (const std::vector<uint8_t>& segment : expected) {
136
42
    ssize_t search_result = buffer.search(segment.data(), segment.size(), start_index);
137
42
    if (search_result == -1) {
138
14
      return false;
139
14
    }
140

            
141
28
    start_index = search_result + segment.size();
142
28
  }
143

            
144
24
  return true;
145
38
}
146

            
147
2
std::ostream& operator<<(std::ostream& out, HealthState state) {
148
2
  switch (state) {
149
1
  case HealthState::Unhealthy:
150
1
    out << "Unhealthy";
151
1
    break;
152
1
  case HealthState::Healthy:
153
1
    out << "Healthy";
154
1
    break;
155
2
  }
156
2
  return out;
157
2
}
158

            
159
2
std::ostream& operator<<(std::ostream& out, HealthTransition changed_state) {
160
2
  switch (changed_state) {
161
1
  case HealthTransition::Unchanged:
162
1
    out << "Unchanged";
163
1
    break;
164
1
  case HealthTransition::Changed:
165
1
    out << "Changed";
166
1
    break;
167
  case HealthTransition::ChangePending:
168
    out << "ChangePending";
169
    break;
170
2
  }
171
2
  return out;
172
2
}
173

            
174
} // namespace Upstream
175
} // namespace Envoy