Line data Source code
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 3113 : log_type_(log_type) {}
30 :
31 4110 : const Http::RequestHeaderMap& HttpFormatterContext::requestHeaders() const {
32 4110 : return request_headers_ != nullptr ? *request_headers_
33 4110 : : *Http::StaticEmptyHeaders::get().request_headers;
34 4110 : }
35 612 : const Http::ResponseHeaderMap& HttpFormatterContext::responseHeaders() const {
36 612 : return response_headers_ != nullptr ? *response_headers_
37 612 : : *Http::StaticEmptyHeaders::get().response_headers;
38 612 : }
39 0 : const Http::ResponseTrailerMap& HttpFormatterContext::responseTrailers() const {
40 0 : return response_trailers_ != nullptr ? *response_trailers_
41 0 : : *Http::StaticEmptyHeaders::get().response_trailers;
42 0 : }
43 :
44 382 : absl::string_view HttpFormatterContext::localReplyBody() const { return local_reply_body_; }
45 0 : AccessLog::AccessLogType HttpFormatterContext::accessLogType() const { return log_type_; }
46 :
47 : absl::optional<std::string>
48 : LocalReplyBodyFormatter::formatWithContext(const HttpFormatterContext& context,
49 382 : const StreamInfo::StreamInfo&) const {
50 382 : return std::string(context.localReplyBody());
51 382 : }
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 0 : const StreamInfo::StreamInfo&) const {
62 0 : return AccessLogType_Name(context.accessLogType());
63 0 : }
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 1635 : : main_header_(main_header), alternative_header_(alternative_header), max_length_(max_length) {}
75 :
76 4722 : const Http::HeaderEntry* HeaderFormatter::findHeader(const Http::HeaderMap& headers) const {
77 4722 : const auto header = headers.get(main_header_);
78 :
79 4722 : if (header.empty() && !alternative_header_.get().empty()) {
80 715 : const auto alternate_header = headers.get(alternative_header_);
81 : // TODO(https://github.com/envoyproxy/envoy/issues/13454): Potentially log all header values.
82 715 : return alternate_header.empty() ? nullptr : alternate_header[0];
83 715 : }
84 :
85 4007 : return header.empty() ? nullptr : header[0];
86 4722 : }
87 :
88 4722 : absl::optional<std::string> HeaderFormatter::format(const Http::HeaderMap& headers) const {
89 4722 : const Http::HeaderEntry* header = findHeader(headers);
90 4722 : if (!header) {
91 2514 : return absl::nullopt;
92 2514 : }
93 :
94 2208 : std::string val = std::string(header->value().getStringView());
95 2208 : SubstitutionFormatUtils::truncate(val, max_length_);
96 2208 : return val;
97 4722 : }
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 171 : : HeaderFormatter(main_header, alternative_header, max_length) {}
114 :
115 : absl::optional<std::string>
116 : ResponseHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
117 612 : const StreamInfo::StreamInfo&) const {
118 612 : return HeaderFormatter::format(context.responseHeaders());
119 612 : }
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 1464 : : HeaderFormatter(main_header, alternative_header, max_length) {}
131 :
132 : absl::optional<std::string>
133 : RequestHeaderFormatter::formatWithContext(const HttpFormatterContext& context,
134 4110 : const StreamInfo::StreamInfo&) const {
135 4110 : return HeaderFormatter::format(context.requestHeaders());
136 4110 : }
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 0 : : HeaderFormatter(main_header, alternative_header, max_length) {}
148 :
149 : absl::optional<std::string>
150 : ResponseTrailerFormatter::formatWithContext(const HttpFormatterContext& context,
151 0 : const StreamInfo::StreamInfo&) const {
152 0 : return HeaderFormatter::format(context.responseTrailers());
153 0 : }
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 0 : : header_type_(header_type) {}
163 :
164 : uint64_t HeadersByteSizeFormatter::extractHeadersByteSize(
165 : const Http::RequestHeaderMap& request_headers, const Http::ResponseHeaderMap& response_headers,
166 0 : const Http::ResponseTrailerMap& response_trailers) const {
167 0 : switch (header_type_) {
168 0 : case HeaderType::RequestHeaders:
169 0 : return request_headers.byteSize();
170 0 : case HeaderType::ResponseHeaders:
171 0 : return response_headers.byteSize();
172 0 : case HeaderType::ResponseTrailers:
173 0 : return response_trailers.byteSize();
174 0 : }
175 0 : PANIC_DUE_TO_CORRUPT_ENUM;
176 0 : }
177 :
178 : absl::optional<std::string>
179 : HeadersByteSizeFormatter::formatWithContext(const HttpFormatterContext& context,
180 0 : const StreamInfo::StreamInfo&) const {
181 0 : return absl::StrCat(extractHeadersByteSize(context.requestHeaders(), context.responseHeaders(),
182 0 : context.responseTrailers()));
183 0 : }
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 0 : GrpcStatusFormatter::Format GrpcStatusFormatter::parseFormat(absl::string_view format) {
193 0 : if (format.empty() || format == "CAMEL_STRING") {
194 0 : return GrpcStatusFormatter::CamelString;
195 0 : }
196 :
197 0 : if (format == "SNAKE_STRING") {
198 0 : return GrpcStatusFormatter::SnakeString;
199 0 : }
200 0 : if (format == "NUMBER") {
201 0 : return GrpcStatusFormatter::Number;
202 0 : }
203 :
204 0 : throwEnvoyExceptionOrPanic(
205 0 : "GrpcStatusFormatter only supports CAMEL_STRING, SNAKE_STRING or NUMBER.");
206 0 : }
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 0 : : HeaderFormatter(main_header, alternative_header, max_length), format_(format) {}
212 :
213 : absl::optional<std::string>
214 : GrpcStatusFormatter::formatWithContext(const HttpFormatterContext& context,
215 0 : const StreamInfo::StreamInfo& info) const {
216 0 : if (Runtime::runtimeFeatureEnabled(
217 0 : "envoy.reloadable_features.validate_grpc_header_before_log_grpc_status")) {
218 0 : if (!Grpc::Common::isGrpcRequestHeaders(context.requestHeaders())) {
219 0 : return absl::nullopt;
220 0 : }
221 0 : }
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 3998 : HttpBuiltInCommandParser::getKnownFormatters() {
305 3998 : CONSTRUCT_ON_FIRST_USE(
306 3998 : FormatterProviderLookupTbl,
307 3998 : {{"REQ",
308 3998 : {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
309 3998 : [](const std::string& format, absl::optional<size_t>& max_length) {
310 3998 : std::string main_header, alternative_header;
311 :
312 3998 : SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
313 :
314 3998 : return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
315 3998 : max_length);
316 3998 : }}},
317 3998 : {"RESP",
318 3998 : {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
319 3998 : [](const std::string& format, absl::optional<size_t>& max_length) {
320 3998 : std::string main_header, alternative_header;
321 :
322 3998 : SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
323 :
324 3998 : return std::make_unique<ResponseHeaderFormatter>(main_header, alternative_header,
325 3998 : max_length);
326 3998 : }}},
327 3998 : {"TRAILER",
328 3998 : {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
329 3998 : [](const std::string& format, absl::optional<size_t>& max_length) {
330 3998 : std::string main_header, alternative_header;
331 :
332 3998 : SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
333 :
334 3998 : return std::make_unique<ResponseTrailerFormatter>(main_header, alternative_header,
335 3998 : max_length);
336 3998 : }}},
337 3998 : {"LOCAL_REPLY_BODY",
338 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
339 3998 : [](const std::string&, absl::optional<size_t>&) {
340 3998 : return std::make_unique<LocalReplyBodyFormatter>();
341 3998 : }}},
342 3998 : {"ACCESS_LOG_TYPE",
343 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
344 3998 : [](const std::string&, absl::optional<size_t>&) {
345 3998 : return std::make_unique<AccessLogTypeFormatter>();
346 3998 : }}},
347 3998 : {"GRPC_STATUS",
348 3998 : {CommandSyntaxChecker::PARAMS_OPTIONAL,
349 3998 : [](const std::string& format, const absl::optional<size_t>&) {
350 3998 : return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
351 3998 : GrpcStatusFormatter::parseFormat(format));
352 3998 : }}},
353 3998 : {"GRPC_STATUS_NUMBER",
354 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
355 3998 : [](const std::string&, const absl::optional<size_t>&) {
356 3998 : return std::make_unique<GrpcStatusFormatter>("grpc-status", "", absl::optional<size_t>(),
357 3998 : GrpcStatusFormatter::Number);
358 3998 : }}},
359 3998 : {"REQUEST_HEADERS_BYTES",
360 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
361 3998 : [](const std::string&, absl::optional<size_t>&) {
362 3998 : return std::make_unique<HeadersByteSizeFormatter>(
363 3998 : HeadersByteSizeFormatter::HeaderType::RequestHeaders);
364 3998 : }}},
365 3998 : {"RESPONSE_HEADERS_BYTES",
366 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
367 3998 : [](const std::string&, absl::optional<size_t>&) {
368 3998 : return std::make_unique<HeadersByteSizeFormatter>(
369 3998 : HeadersByteSizeFormatter::HeaderType::ResponseHeaders);
370 3998 : }}},
371 3998 : {"RESPONSE_TRAILERS_BYTES",
372 3998 : {CommandSyntaxChecker::COMMAND_ONLY,
373 3998 : [](const std::string&, absl::optional<size_t>&) {
374 3998 : return std::make_unique<HeadersByteSizeFormatter>(
375 3998 : HeadersByteSizeFormatter::HeaderType::ResponseTrailers);
376 3998 : }}},
377 3998 : {"STREAM_INFO_REQ",
378 3998 : {CommandSyntaxChecker::PARAMS_REQUIRED | CommandSyntaxChecker::LENGTH_ALLOWED,
379 3998 : [](const std::string& format, absl::optional<size_t>& max_length) {
380 3998 : std::string main_header, alternative_header;
381 3998 : SubstitutionFormatUtils::parseSubcommandHeaders(format, main_header, alternative_header);
382 :
383 3998 : return std::make_unique<RequestHeaderFormatter>(main_header, alternative_header,
384 3998 : max_length);
385 3998 : }}}});
386 3998 : }
387 :
388 : FormatterProviderPtr HttpBuiltInCommandParser::parse(const std::string& command,
389 : const std::string& subcommand,
390 3998 : absl::optional<size_t>& max_length) const {
391 3998 : const FormatterProviderLookupTbl& providers = getKnownFormatters();
392 :
393 3998 : auto it = providers.find(command);
394 :
395 3998 : if (it == providers.end()) {
396 2050 : return nullptr;
397 2050 : }
398 :
399 : // Check flags for the command.
400 1948 : 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 1948 : return (*it).second.second(subcommand, max_length);
405 3998 : }
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 171 : FormatterPtr HttpSubstitutionFormatUtils::defaultSubstitutionFormatter() {
417 171 : return std::make_unique<Envoy::Formatter::FormatterImpl>(DEFAULT_FORMAT, false);
418 171 : }
419 :
420 : } // namespace Formatter
421 : } // namespace Envoy
|