1
#pragma once
2

            
3
#include <fstream>
4

            
5
#include "envoy/buffer/buffer.h"
6
#include "envoy/config/tap/v3/common.pb.h"
7
#include "envoy/data/tap/v3/common.pb.h"
8
#include "envoy/data/tap/v3/wrapper.pb.h"
9

            
10
#include "source/extensions/common/matcher/matcher.h"
11
#include "source/extensions/common/tap/tap.h"
12

            
13
namespace Envoy {
14
namespace Extensions {
15
namespace Common {
16
namespace Tap {
17

            
18
using Matcher = Envoy::Extensions::Common::Matcher::Matcher;
19
using MatcherPtr = Envoy::Extensions::Common::Matcher::MatcherPtr;
20

            
21
/**
22
 * Common utilities for tapping.
23
 */
24
class Utility {
25
public:
26
  /**
27
   * Add body data to a tapped body message, taking into account the maximum bytes to buffer.
28
   * @param output_body supplies the body message to buffer to.
29
   * @param max_buffered_bytes supplies the maximum bytes to store, if truncation occurs the
30
   *        truncation flag will be set.
31
   * @param data supplies the data to buffer.
32
   * @param buffer_start_offset supplies the offset within data to start buffering.
33
   * @param buffer_length_to_copy supplies the length of the data to buffer.
34
   * @return whether the buffered data was truncated or not.
35
   */
36
  static bool addBufferToProtoBytes(envoy::data::tap::v3::Body& output_body,
37
                                    uint32_t max_buffered_bytes, const Buffer::Instance& data,
38
                                    uint32_t buffer_start_offset, uint32_t buffer_length_to_copy);
39

            
40
  /**
41
   * Swap body as bytes to body as string if necessary in a trace wrapper.
42
   */
43
  static void bodyBytesToString(envoy::data::tap::v3::TraceWrapper& trace,
44
                                envoy::config::tap::v3::OutputSink::Format sink_format);
45

            
46
  /**
47
   * Trim a container that contains buffer raw slices so that the slices start at an offset and
48
   * only contain a specific length. No slices are removed from the container, but their length
49
   * may be reduced to 0.
50
   * TODO(mattklein123): This is split out to ease testing and also because we should ultimately
51
   * move this directly into the buffer API. I would rather wait until the new buffer code merges
52
   * before we do that.
53
   */
54
130
  template <typename T> static void trimSlices(T& slices, uint32_t start_offset, uint32_t length) {
55
134
    for (auto& slice : slices) {
56
134
      const uint32_t start_offset_trim = std::min<uint32_t>(start_offset, slice.len_);
57
134
      slice.len_ -= start_offset_trim;
58
134
      start_offset -= start_offset_trim;
59
134
      if (slice.mem_ != nullptr) {
60
134
        slice.mem_ = static_cast<char*>(slice.mem_) + start_offset_trim;
61
134
      }
62

            
63
134
      const uint32_t final_length = std::min<uint32_t>(length, slice.len_);
64
134
      slice.len_ = final_length;
65
134
      length -= final_length;
66
134
    }
67
130
  }
68
};
69

            
70
/**
71
 * Base class for all tap configurations.
72
 * TODO(mattklein123): This class will handle common functionality such as rate limiting, etc.
73
 */
74
class TapConfigBaseImpl : public virtual TapConfig {
75
public:
76
  // A wrapper for a per tap sink handle and trace submission. If in the future we support
77
  // multiple sinks we can easily do it here.
78
  class PerTapSinkHandleManagerImpl : public PerTapSinkHandleManager {
79
  public:
80
    PerTapSinkHandleManagerImpl(TapConfigBaseImpl& parent, uint64_t trace_id)
81
54
        : parent_(parent),
82
54
          handle_(parent.sink_to_use_->createPerTapSinkHandle(trace_id, parent.sink_type_)) {}
83

            
84
    // PerTapSinkHandleManager
85
    void submitTrace(TraceWrapperPtr&& trace) override;
86

            
87
  private:
88
    TapConfigBaseImpl& parent_;
89
    PerTapSinkHandlePtr handle_;
90
  };
91

            
92
  // TapConfig
93
54
  PerTapSinkHandleManagerPtr createPerTapSinkHandleManager(uint64_t trace_id) override {
94
54
    return std::make_unique<PerTapSinkHandleManagerImpl>(*this, trace_id);
95
54
  }
96
43
  uint32_t maxBufferedRxBytes() const override { return max_buffered_rx_bytes_; }
97
48
  uint32_t maxBufferedTxBytes() const override { return max_buffered_tx_bytes_; }
98
9
  uint32_t minStreamedSentBytes() const override { return min_streamed_sent_bytes_; }
99
54
  Matcher::MatchStatusVector createMatchStatusVector() const override {
100
54
    return Matcher::MatchStatusVector(matchers_.size());
101
54
  }
102
  const Matcher& rootMatcher() const override;
103
265
  bool streaming() const override { return streaming_; }
104

            
105
protected:
106
  TapConfigBaseImpl(const envoy::config::tap::v3::TapConfig& proto_config,
107
                    Common::Tap::Sink* admin_streamer,
108
                    Server::Configuration::GenericFactoryContext& context);
109

            
110
private:
111
  // This is the default setting for both RX/TX max buffered bytes. (This means that per tap, the
112
  // maximum amount that can be buffered is 2x this value).
113
  static constexpr uint32_t DefaultMaxBufferedBytes = 1024;
114

            
115
  const uint32_t max_buffered_rx_bytes_;
116
  const uint32_t max_buffered_tx_bytes_;
117
  const bool streaming_;
118
  Sink* sink_to_use_;
119
  SinkPtr sink_;
120
  envoy::config::tap::v3::OutputSink::Format sink_format_;
121
  envoy::config::tap::v3::OutputSink::OutputSinkTypeCase sink_type_;
122
  std::vector<MatcherPtr> matchers_;
123
  // This is the default value for min streamed buffered bytes.
124
  // (This means that per streamed trace, the minimum amount
125
  // which triggering to send the tapped messages size is 9 bytes).
126
  static constexpr uint32_t DefaultMinStreamedSentBytes = 9;
127
  uint32_t min_streamed_sent_bytes_{0};
128
};
129

            
130
/**
131
 * A tap sink that writes each tap trace to a discrete output file.
132
 */
133
class FilePerTapSink : public Sink {
134
public:
135
16
  FilePerTapSink(const envoy::config::tap::v3::FilePerTapSink& config) : config_(config) {}
136

            
137
  // Sink
138
  PerTapSinkHandlePtr
139
  createPerTapSinkHandle(uint64_t trace_id,
140
16
                         envoy::config::tap::v3::OutputSink::OutputSinkTypeCase) override {
141
16
    return std::make_unique<FilePerTapSinkHandle>(*this, trace_id);
142
16
  }
143

            
144
private:
145
  struct FilePerTapSinkHandle : public PerTapSinkHandle {
146
    FilePerTapSinkHandle(FilePerTapSink& parent, uint64_t trace_id)
147
16
        : parent_(parent), trace_id_(trace_id) {}
148

            
149
    // PerTapSinkHandle
150
    void submitTrace(TraceWrapperPtr&& trace,
151
                     envoy::config::tap::v3::OutputSink::Format format) override;
152

            
153
    FilePerTapSink& parent_;
154
    const uint64_t trace_id_;
155
    std::ofstream output_file_;
156
  };
157

            
158
  const envoy::config::tap::v3::FilePerTapSink config_;
159
};
160

            
161
} // namespace Tap
162
} // namespace Common
163
} // namespace Extensions
164
} // namespace Envoy