Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/formatter/http_specific_formatter.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/common/formatter/http_specific_formatter.h"
2
3
#include "source/common/common/assert.h"
4
#include "source/common/common/empty_string.h"
5
#include "source/common/common/fmt.h"
6
#include "source/common/common/thread.h"
7
#include "source/common/common/utility.h"
8
#include "source/common/config/metadata.h"
9
#include "source/common/formatter/substitution_formatter.h"
10
#include "source/common/grpc/common.h"
11
#include "source/common/grpc/status.h"
12
#include "source/common/http/header_map_impl.h"
13
#include "source/common/http/utility.h"
14
#include "source/common/protobuf/message_validator_impl.h"
15
#include "source/common/protobuf/utility.h"
16
#include "source/common/runtime/runtime_features.h"
17
#include "source/common/stream_info/utility.h"
18
19
namespace Envoy {
20
namespace Formatter {
21
22
HttpFormatterContext::HttpFormatterContext(const Http::RequestHeaderMap* request_headers,
23
                                           const Http::ResponseHeaderMap* response_headers,
24
                                           const Http::ResponseTrailerMap* response_trailers,
25
                                           absl::string_view local_reply_body,
26
                                           AccessLog::AccessLogType log_type)
27
    : request_headers_(request_headers), response_headers_(response_headers),
28
      response_trailers_(response_trailers), local_reply_body_(local_reply_body),
29
333k
      log_type_(log_type) {}
30
31
41.9k
const Http::RequestHeaderMap& HttpFormatterContext::requestHeaders() const {
32
41.9k
  return request_headers_ != nullptr ? *request_headers_
33
41.9k
                                     : *Http::StaticEmptyHeaders::get().request_headers;
34
41.9k
}
35
8.77k
const Http::ResponseHeaderMap& HttpFormatterContext::responseHeaders() const {
36
8.77k
  return response_headers_ != nullptr ? *response_headers_
37
8.77k
                                      : *Http::StaticEmptyHeaders::get().response_headers;
38
8.77k
}
39
5.88k
const Http::ResponseTrailerMap& HttpFormatterContext::responseTrailers() const {
40
5.88k
  return response_trailers_ != nullptr ? *response_trailers_
41
5.88k
                                       : *Http::StaticEmptyHeaders::get().response_trailers;
42
5.88k
}
43
44
113k
absl::string_view HttpFormatterContext::localReplyBody() const { return local_reply_body_; }
45
405
AccessLog::AccessLogType HttpFormatterContext::accessLogType() const { return log_type_; }
46
47
absl::optional<std::string>
48
LocalReplyBodyFormatter::formatWithContext(const HttpFormatterContext& context,
49
113k
                                           const StreamInfo::StreamInfo&) const {
50
113k
  return std::string(context.localReplyBody());
51
113k
}
52
53
ProtobufWkt::Value
54
LocalReplyBodyFormatter::formatValueWithContext(const HttpFormatterContext& context,
55
0
                                                const StreamInfo::StreamInfo&) const {
56
0
  return ValueUtil::stringValue(std::string(context.localReplyBody()));
57
0
}
58
59
absl::optional<std::string>
60
AccessLogTypeFormatter::formatWithContext(const HttpFormatterContext& context,
61
154
                                          const StreamInfo::StreamInfo&) const {
62
154
  return AccessLogType_Name(context.accessLogType());
63
154
}
64
65
ProtobufWkt::Value
66
AccessLogTypeFormatter::formatValueWithContext(const HttpFormatterContext& context,
67
0
                                               const StreamInfo::StreamInfo&) const {
68
0
  return ValueUtil::stringValue(AccessLogType_Name(context.accessLogType()));
69
0
}
70
71
HeaderFormatter::HeaderFormatter(const std::string& main_header,
72
                                 const std::string& alternative_header,
73
                                 absl::optional<size_t> max_length)
74
65.5k
    : main_header_(main_header), alternative_header_(alternative_header), max_length_(max_length) {}
75
76
48.9k
const Http::HeaderEntry* HeaderFormatter::findHeader(const Http::HeaderMap& headers) const {
77
48.9k
  const auto header = headers.get(main_header_);
78
79
48.9k
  if (header.empty() && !alternative_header_.get().empty()) {
80
8.81k
    const auto alternate_header = headers.get(alternative_header_);
81
    // TODO(https://github.com/envoyproxy/envoy/issues/13454): Potentially log all header values.
82
8.81k
    return alternate_header.empty() ? nullptr : alternate_header[0];
83
8.81k
  }
84
85
40.1k
  return header.empty() ? nullptr : header[0];
86
48.9k
}
87
88
48.9k
absl::optional<std::string> HeaderFormatter::format(const Http::HeaderMap& headers) const {
89
48.9k
  const Http::HeaderEntry* header = findHeader(headers);
90
48.9k
  if (!header) {
91
31.3k
    return absl::nullopt;
92
31.3k
  }
93
94
17.6k
  std::string val = std::string(header->value().getStringView());
95
17.6k
  SubstitutionFormatUtils::truncate(val, max_length_);
96
17.6k
  return val;
97
48.9k
}
98
99
0
ProtobufWkt::Value HeaderFormatter::formatValue(const Http::HeaderMap& headers) const {
100
0
  const Http::HeaderEntry* header = findHeader(headers);
101
0
  if (!header) {
102
0
    return SubstitutionFormatUtils::unspecifiedValue();
103
0
  }
104
105
0
  std::string val = std::string(header->value().getStringView());
106
0
  SubstitutionFormatUtils::truncate(val, max_length_);
107
0
  return ValueUtil::stringValue(val);
108
0
}
109
110
ResponseHeaderFormatter::ResponseHeaderFormatter(const std::string& main_header,
111
                                                 const std::string& alternative_header,
112
                                                 absl::optional<size_t> max_length)
113
9.24k
    : HeaderFormatter(main_header, alternative_header, max_length) {}
114
115
absl::optional<std::string>
116
ResponseHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
117
6.94k
                                           const StreamInfo::StreamInfo&) const {
118
6.94k
  return HeaderFormatter::format(context.responseHeaders());
119
6.94k
}
120
121
ProtobufWkt::Value
122
ResponseHeaderFormatter::formatValueWithContext(const HttpFormatterContext& context,
123
0
                                                const StreamInfo::StreamInfo&) const {
124
0
  return HeaderFormatter::formatValue(context.responseHeaders());
125
0
}
126
127
RequestHeaderFormatter::RequestHeaderFormatter(const std::string& main_header,
128
                                               const std::string& alternative_header,
129
                                               absl::optional<size_t> max_length)
130
47.7k
    : HeaderFormatter(main_header, alternative_header, max_length) {}
131
132
absl::optional<std::string>
133
RequestHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
134
37.9k
                                          const StreamInfo::StreamInfo&) const {
135
37.9k
  return HeaderFormatter::format(context.requestHeaders());
136
37.9k
}
137
138
ProtobufWkt::Value
139
RequestHeaderFormatter::formatValueWithContext(const HttpFormatterContext& context,
140
0
                                               const StreamInfo::StreamInfo&) const {
141
0
  return HeaderFormatter::formatValue(context.requestHeaders());
142
0
}
143
144
ResponseTrailerFormatter::ResponseTrailerFormatter(const std::string& main_header,
145
                                                   const std::string& alternative_header,
146
                                                   absl::optional<size_t> max_length)
147
5.50k
    : HeaderFormatter(main_header, alternative_header, max_length) {}
148
149
absl::optional<std::string>
150
ResponseTrailerFormatter::formatWithContext(const HttpFormatterContext& context,
151
4.05k
                                            const StreamInfo::StreamInfo&) const {
152
4.05k
  return HeaderFormatter::format(context.responseTrailers());
153
4.05k
}
154
155
ProtobufWkt::Value
156
ResponseTrailerFormatter::formatValueWithContext(const HttpFormatterContext& context,
157
0
                                                 const StreamInfo::StreamInfo&) const {
158
0
  return HeaderFormatter::formatValue(context.responseTrailers());
159
0
}
160
161
HeadersByteSizeFormatter::HeadersByteSizeFormatter(const HeaderType header_type)
162
1.12k
    : header_type_(header_type) {}
163
164
uint64_t HeadersByteSizeFormatter::extractHeadersByteSize(
165
    const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers,
166
1.03k
    const Http::ResponseTrailerMap& response_trailers) const {
167
1.03k
  switch (header_type_) {
168
154
  case HeaderType::RequestHeaders:
169
154
    return request_headers.byteSize();
170
674
  case HeaderType::ResponseHeaders:
171
674
    return response_headers.byteSize();
172
210
  case HeaderType::ResponseTrailers:
173
210
    return response_trailers.byteSize();
174
1.03k
  }
175
0
  PANIC_DUE_TO_CORRUPT_ENUM;
176
0
}
177
178
absl::optional<std::string>
179
HeadersByteSizeFormatter::formatWithContext(const HttpFormatterContext& context,
180
1.03k
                                            const StreamInfo::StreamInfo&) const {
181
1.03k
  return absl::StrCat(extractHeadersByteSize(context.requestHeaders(), context.responseHeaders(),
182
1.03k
                                             context.responseTrailers()));
183
1.03k
}
184
185
ProtobufWkt::Value
186
HeadersByteSizeFormatter::formatValueWithContext(const HttpFormatterContext& context,
187
0
                                                 const StreamInfo::StreamInfo&) const {
188
0
  return ValueUtil::numberValue(extractHeadersByteSize(
189
0
      context.requestHeaders(), context.responseHeaders(), context.responseTrailers()));
190
0
}
191
192
2.13k
GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view format) {
193
2.13k
  if (format.empty() || format == "CAMEL_STRING") {
194
1.83k
    return GrpcStatusFormatter::CamelString;
195
1.83k
  }
196
197
300
  if (format == "SNAKE_STRING") {
198
111
    return GrpcStatusFormatter::SnakeString;
199
111
  }
200
189
  if (format == "NUMBER") {
201
139
    return GrpcStatusFormatter::Number;
202
139
  }
203
204
189
  throwEnvoyExceptionOrPanic(
205
189
      "GrpcStatusFormatter only supports CAMEL_STRING, SNAKE_STRING or NUMBER.");
206
189
}
207
208
GrpcStatusFormatter::GrpcStatusFormatter(const std::string& main_header,
209
                                         const std::string& alternative_header,
210
                                         absl::optional<size_t> max_length, Format format)
211
3.02k
    : HeaderFormatter(main_header, alternative_header, max_length), format_(format) {}
212
213
absl::optional<std::string>
214
GrpcStatusFormatter::formatWithContext(const HttpFormatterContext& context,
215
2.49k
                                       const StreamInfo::StreamInfo& info) const {
216
2.49k
  if (Runtime::runtimeFeatureEnabled(
217
2.49k
          "envoy.reloadable_features.validate_grpc_header_before_log_grpc_status")) {
218
2.49k
    if (!Grpc::Common::isGrpcRequestHeaders(context.requestHeaders())) {
219
2.49k
      return absl::nullopt;
220
2.49k
    }
221
2.49k
  }
222
0
  const auto grpc_status = Grpc::Common::getGrpcStatus(context.responseTrailers(),
223
0
                                                       context.responseHeaders(), info, true);
224
0
  if (!grpc_status.has_value()) {
225
0
    return absl::nullopt;
226
0
  }
227
0
  switch (format_) {
228
0
  case CamelString: {
229
0
    const auto grpc_status_message = Grpc::Utility::grpcStatusToString(grpc_status.value());
230
0
    if (grpc_status_message == EMPTY_STRING || grpc_status_message == "InvalidCode") {
231
0
      return std::to_string(grpc_status.value());
232
0
    }
233
0
    return grpc_status_message;
234
0
  }
235
0
  case SnakeString: {
236
0
    const auto grpc_status_message =
237
0
        absl::StatusCodeToString(static_cast<absl::StatusCode>(grpc_status.value()));
238
0
    if (grpc_status_message == EMPTY_STRING) {
239
0
      return std::to_string(grpc_status.value());
240
0
    }
241
0
    return grpc_status_message;
242
0
  }
243
0
  case Number: {
244
0
    return std::to_string(grpc_status.value());
245
0
  }
246
0
  }
247
0
  PANIC_DUE_TO_CORRUPT_ENUM;
248
0
}
249
250
ProtobufWkt::Value
251
GrpcStatusFormatter::formatValueWithContext(const HttpFormatterContext& context,
252
0
                                            const StreamInfo::StreamInfo& info) const {
253
0
  if (Runtime::runtimeFeatureEnabled(
254
0
          "envoy.reloadable_features.validate_grpc_header_before_log_grpc_status")) {
255
0
    if (!Grpc::Common::isGrpcRequestHeaders(context.requestHeaders())) {
256
0
      return SubstitutionFormatUtils::unspecifiedValue();
257
0
    }
258
0
  }
259
0
  const auto grpc_status = Grpc::Common::getGrpcStatus(context.responseTrailers(),
260
0
                                                       context.responseHeaders(), info, true);
261
0
  if (!grpc_status.has_value()) {
262
0
    return SubstitutionFormatUtils::unspecifiedValue();
263
0
  }
264
265
0
  switch (format_) {
266
0
  case CamelString: {
267
0
    const auto grpc_status_message = Grpc::Utility::grpcStatusToString(grpc_status.value());
268
0
    if (grpc_status_message == EMPTY_STRING || grpc_status_message == "InvalidCode") {
269
0
      return ValueUtil::stringValue(std::to_string(grpc_status.value()));
270
0
    }
271
0
    return ValueUtil::stringValue(grpc_status_message);
272
0
  }
273
0
  case SnakeString: {
274
0
    const auto grpc_status_message =
275
0
        absl::StatusCodeToString(static_cast<absl::StatusCode>(grpc_status.value()));
276
0
    if (grpc_status_message == EMPTY_STRING) {
277
0
      return ValueUtil::stringValue(std::to_string(grpc_status.value()));
278
0
    }
279
0
    return ValueUtil::stringValue(grpc_status_message);
280
0
  }
281
0
  case Number: {
282
0
    return ValueUtil::numberValue(grpc_status.value());
283
0
  }
284
0
  }
285
0
  PANIC_DUE_TO_CORRUPT_ENUM;
286
0
}
287
288
StreamInfoRequestHeaderFormatter::StreamInfoRequestHeaderFormatter(
289
    const std::string& main_header, const std::string& alternative_header,
290
    absl::optional<size_t> max_length)
291
0
    : HeaderFormatter(main_header, alternative_header, max_length) {}
292
293
absl::optional<std::string> StreamInfoRequestHeaderFormatter::formatWithContext(
294
0
    const HttpFormatterContext&, const StreamInfo::StreamInfo& stream_info) const {
295
0
  return HeaderFormatter::format(*stream_info.getRequestHeaders());
296
0
}
297
298
ProtobufWkt::Value StreamInfoRequestHeaderFormatter::formatValueWithContext(
299
0
    const HttpFormatterContext&, const StreamInfo::StreamInfo& stream_info) const {
300
0
  return HeaderFormatter::formatValue(*stream_info.getRequestHeaders());
301
0
}
302
303
const HttpBuiltInCommandParser::FormatterProviderLookupTbl&
304
359k
HttpBuiltInCommandParser::getKnownFormatters() {
305
359k
  CONSTRUCT_ON_FIRST_USE(
306
359k
      FormatterProviderLookupTbl,
307
359k
      {{"REQ",
308
359k
        {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
309
359k
         [](const std::string& format, absl::optional<size_t>& max_length) {
310
359k
           std::string main_header, alternative_header;
311
312
359k
           SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
313
314
359k
           return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
315
359k
                                                           max_length);
316
359k
         }}},
317
359k
       {"RESP",
318
359k
        {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
319
359k
         [](const std::string& format, absl::optional<size_t>& max_length) {
320
359k
           std::string main_header, alternative_header;
321
322
359k
           SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
323
324
359k
           return std::make_unique<ResponseHeaderFormatter>(main_header, alternative_header,
325
359k
                                                            max_length);
326
359k
         }}},
327
359k
       {"TRAILER",
328
359k
        {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
329
359k
         [](const std::string& format, absl::optional<size_t>& max_length) {
330
359k
           std::string main_header, alternative_header;
331
332
359k
           SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
333
334
359k
           return std::make_unique<ResponseTrailerFormatter>(main_header, alternative_header,
335
359k
                                                             max_length);
336
359k
         }}},
337
359k
       {"LOCAL_REPLY_BODY",
338
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
339
359k
         [](const std::string&, absl::optional<size_t>&) {
340
359k
           return std::make_unique<LocalReplyBodyFormatter>();
341
359k
         }}},
342
359k
       {"ACCESS_LOG_TYPE",
343
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
344
359k
         [](const std::string&, absl::optional<size_t>&) {
345
359k
           return std::make_unique<AccessLogTypeFormatter>();
346
359k
         }}},
347
359k
       {"GRPC_STATUS",
348
359k
        {CommandSyntaxChecker::PARAMS_OPTIONAL,
349
359k
         [](const std::string& format, const absl::optional<size_t>&) {
350
359k
           return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
351
359k
                                                        GrpcStatusFormatter::parseFormat(format));
352
359k
         }}},
353
359k
       {"GRPC_STATUS_NUMBER",
354
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
355
359k
         [](const std::string&, const absl::optional<size_t>&) {
356
359k
           return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
357
359k
                                                        GrpcStatusFormatter::Number);
358
359k
         }}},
359
359k
       {"REQUEST_HEADERS_BYTES",
360
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
361
359k
         [](const std::string&, absl::optional<size_t>&) {
362
359k
           return std::make_unique<HeadersByteSizeFormatter>(
363
359k
               HeadersByteSizeFormatter::HeaderType::RequestHeaders);
364
359k
         }}},
365
359k
       {"RESPONSE_HEADERS_BYTES",
366
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
367
359k
         [](const std::string&, absl::optional<size_t>&) {
368
359k
           return std::make_unique<HeadersByteSizeFormatter>(
369
359k
               HeadersByteSizeFormatter::HeaderType::ResponseHeaders);
370
359k
         }}},
371
359k
       {"RESPONSE_TRAILERS_BYTES",
372
359k
        {CommandSyntaxChecker::COMMAND_ONLY,
373
359k
         [](const std::string&, absl::optional<size_t>&) {
374
359k
           return std::make_unique<HeadersByteSizeFormatter>(
375
359k
               HeadersByteSizeFormatter::HeaderType::ResponseTrailers);
376
359k
         }}},
377
359k
       {"STREAM_INFO_REQ",
378
359k
        {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
379
359k
         [](const std::string& format, absl::optional<size_t>& max_length) {
380
359k
           std::string main_header, alternative_header;
381
359k
           SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
382
383
359k
           return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
384
359k
                                                           max_length);
385
359k
         }}}});
386
359k
}
387
388
FormatterProviderPtr HttpBuiltInCommandParser::parse(const std::string& command,
389
                                                     const std::string& subcommand,
390
359k
                                                     absl::optional<size_t>& max_length) const {
391
359k
  const FormatterProviderLookupTbl& providers = getKnownFormatters();
392
393
359k
  auto it = providers.find(command);
394
395
359k
  if (it == providers.end()) {
396
275k
    return nullptr;
397
275k
  }
398
399
  // Check flags for the command.
400
84.0k
  CommandSyntaxChecker::verifySyntax((*it).second.first, command, subcommand, max_length);
401
402
  // Create a pointer to the formatter by calling a function
403
  // associated with formatter's name.
404
84.0k
  return (*it).second.second(subcommand, max_length);
405
359k
}
406
407
REGISTER_BUILT_IN_COMMAND_PARSER(HttpFormatterContext, HttpBuiltInCommandParser);
408
409
static const std::string DEFAULT_FORMAT =
410
    "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" "
411
    "%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% "
412
    "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "
413
    "\"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" "
414
    "\"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n";
415
416
5.43k
FormatterPtr HttpSubstitutionFormatUtils::defaultSubstitutionFormatter() {
417
5.43k
  return std::make_unique<Envoy::Formatter::FormatterImpl>(DEFAULT_FORMAT, false);
418
5.43k
}
419
420
} // namespace Formatter
421
} // namespace Envoy