1
#pragma once
2

            
3
#include <cstdint>
4

            
5
#include "envoy/access_log/access_log.h"
6
#include "envoy/api/api.h"
7
#include "envoy/common/random_generator.h"
8
#include "envoy/config/core/v3/health_check.pb.h"
9
#include "envoy/data/core/v3/health_check_event.pb.h"
10
#include "envoy/grpc/status.h"
11
#include "envoy/network/socket.h"
12
#include "envoy/server/factory_context.h"
13
#include "envoy/server/health_checker_config.h"
14
#include "envoy/type/v3/http.pb.h"
15
#include "envoy/type/v3/range.pb.h"
16
#include "envoy/upstream/health_checker.h"
17

            
18
#include "source/common/common/dump_state_utils.h"
19
#include "source/common/common/logger.h"
20
#include "source/common/grpc/codec.h"
21
#include "source/common/http/codec_client.h"
22
#include "source/common/router/header_parser.h"
23
#include "source/common/stream_info/stream_info_impl.h"
24
#include "source/common/upstream/health_checker_event_logger.h"
25

            
26
#include "src/proto/grpc/health/v1/health.pb.h"
27

            
28
namespace Envoy {
29
namespace Upstream {
30

            
31
constexpr uint64_t kDefaultMaxBytesInBuffer = 1024;
32

            
33
/**
34
 * HealthCheckerHash and HealthCheckerEqualTo are used to allow the HealthCheck proto to be used as
35
 * a flat_hash_map key.
36
 */
37
struct HealthCheckerHash {
38
13
  size_t operator()(const envoy::config::core::v3::HealthCheck& health_check) const {
39
13
    return MessageUtil::hash(health_check);
40
13
  }
41
};
42

            
43
struct HealthCheckerEqualTo {
44
  bool operator()(const envoy::config::core::v3::HealthCheck& lhs,
45
13
                  const envoy::config::core::v3::HealthCheck& rhs) const {
46
13
    return Protobuf::util::MessageDifferencer::Equals(lhs, rhs);
47
13
  }
48
};
49

            
50
/**
51
 * Health checker factory context.
52
 */
53
class HealthCheckerFactoryContextImpl : public Server::Configuration::HealthCheckerFactoryContext {
54
public:
55
  HealthCheckerFactoryContextImpl(Upstream::Cluster& cluster,
56
                                  Server::Configuration::ServerFactoryContext& server_context)
57
176
      : cluster_(cluster), runtime_(server_context.runtime()),
58
176
        dispatcher_(server_context.mainThreadDispatcher()),
59
176
        validation_visitor_(server_context.messageValidationVisitor()),
60
176
        log_manager_(server_context.accessLogManager()), api_(server_context.api()),
61
176
        server_context_(server_context) {}
62
174
  Upstream::Cluster& cluster() override { return cluster_; }
63
174
  Envoy::Runtime::Loader& runtime() override { return runtime_; }
64
174
  Event::Dispatcher& mainThreadDispatcher() override { return dispatcher_; }
65
174
  HealthCheckEventLoggerPtr eventLogger() override { return std::move(event_logger_); }
66
4
  ProtobufMessage::ValidationVisitor& messageValidationVisitor() override {
67
4
    return validation_visitor_;
68
4
  }
69
318
  Api::Api& api() override { return api_; }
70

            
71
1
  AccessLog::AccessLogManager& accessLogManager() override { return log_manager_; }
72
  void setEventLogger(HealthCheckEventLoggerPtr event_logger) override {
73
    event_logger_ = std::move(event_logger);
74
  }
75

            
76
17
  Server::Configuration::ServerFactoryContext& serverFactoryContext() override {
77
17
    return server_context_;
78
17
  };
79

            
80
private:
81
  Upstream::Cluster& cluster_;
82
  Envoy::Runtime::Loader& runtime_;
83
  Event::Dispatcher& dispatcher_;
84
  ProtobufMessage::ValidationVisitor& validation_visitor_;
85
  AccessLog::AccessLogManager& log_manager_;
86
  Api::Api& api_;
87
  HealthCheckEventLoggerPtr event_logger_;
88
  Server::Configuration::ServerFactoryContext& server_context_;
89
};
90

            
91
/**
92
 * Factory for creating health checker implementations.
93
 */
94
class HealthCheckerFactory : public Logger::Loggable<Logger::Id::health_checker> {
95
public:
96
  // Helper functions to get the correct hostname for an L7 health check.
97
  static const std::string& getHostname(const HostSharedPtr& host,
98
                                        const std::string& config_hostname,
99
                                        const ClusterInfoConstSharedPtr& cluster);
100
  /**
101
   * Create a health checker or return an error.
102
   * @param health_check_config supplies the health check proto.
103
   * @param cluster supplies the owning cluster.
104
   * @param server_context reference to the Server context object
105
   * @return a health checker.
106
   */
107
  static absl::StatusOr<HealthCheckerSharedPtr>
108
  create(const envoy::config::core::v3::HealthCheck& health_check_config,
109
         Upstream::Cluster& cluster, Server::Configuration::ServerFactoryContext& server_context);
110
};
111

            
112
/**
113
 * Utility class for loading a binary health checking config and matching it against a buffer.
114
 * Split out for ease of testing. The type of matching performed is the following (this is the
115
 * MongoDB health check request and response):
116
 *
117
 * "send": [
118
    {"text": "39000000"},
119
    {"text": "EEEEEEEE"},
120
    {"text": "00000000"},
121
    {"text": "d4070000"},
122
    {"text": "00000000"},
123
    {"text": "746573742e"},
124
    {"text": "24636d6400"},
125
    {"text": "00000000"},
126
    {"text": "FFFFFFFF"},
127

            
128
    {"text": "13000000"},
129
    {"text": "01"},
130
    {"text": "70696e6700"},
131
    {"text": "000000000000f03f"},
132
    {"text": "00"}
133
   ],
134
   "receive": [
135
    {"text": "EEEEEEEE"},
136
    {"text": "01000000"},
137
    {"text": "00000000"},
138
    {"text": "0000000000000000"},
139
    {"text": "00000000"},
140
    {"text": "11000000"},
141
    {"text": "01"},
142
    {"text": "6f6b"},
143
    {"text": "00000000000000f03f"},
144
    {"text": "00"}
145
   ]
146
 * Each text or binary filed in Payload is converted to a binary block.
147
 * The text is Hex string by default.
148
 *
149
 * During each health check cycle, all of the "send" bytes are sent to the target server. Each
150
 * binary block can be of arbitrary length and is just concatenated together when sent.
151
 *
152
 * On the receive side, "fuzzy" matching is performed such that each binary block must be found,
153
 * and in the order specified, but not necessary contiguous. Thus, in the example above,
154
 * "FFFFFFFF" could be inserted in the response between "EEEEEEEE" and "01000000" and the check
155
 * would still pass.
156
 *
157
 */
158
class PayloadMatcher {
159
public:
160
  using MatchSegments = std::list<std::vector<uint8_t>>;
161

            
162
  static absl::StatusOr<MatchSegments> loadProtoBytes(
163
      const Protobuf::RepeatedPtrField<envoy::config::core::v3::HealthCheck::Payload>& byte_array);
164
  static absl::StatusOr<MatchSegments>
165
  loadProtoBytes(const envoy::config::core::v3::HealthCheck::Payload& single_payload);
166
  static bool match(const MatchSegments& expected, const Buffer::Instance& buffer);
167

            
168
private:
169
  static absl::StatusOr<std::vector<uint8_t>>
170
  decodePayload(const envoy::config::core::v3::HealthCheck::Payload& payload);
171
};
172

            
173
} // namespace Upstream
174
} // namespace Envoy