| // Copyright 2026 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef QUICHE_BALSA_HTTP_PROTOCOL_DEFECTS_H_ | |
| #define QUICHE_BALSA_HTTP_PROTOCOL_DEFECTS_H_ | |
| #include "absl/strings/string_view.h" | |
| #include "quiche/common/platform/api/quiche_export.h" | |
| #include "quiche/common/quiche_callbacks.h" | |
| namespace quiche { | |
| class QUICHE_EXPORT HttpProtocolDefects { | |
| public: | |
| bool invalid_method_in_request_first_line = false; | |
| bool multiple_content_length_keys = false; | |
| bool missing_semicolon_in_chunk_extension = false; | |
| bool token_before_semicolon_in_chunk_extension = false; | |
| bool invalid_response_code = false; | |
| bool multiple_spaces_in_firstline = false; | |
| bool obs_fold_in_header_values = false; | |
| bool obs_fold_in_trailer_values = false; | |
| bool chunked_body_does_not_end_with_crlf_crlf = false; | |
| bool stray_data_after_chunk = false; | |
| bool lone_cr_in_request_headers = false; | |
| bool header_name_contains_double_quote = false; | |
| bool header_missing_colon = false; | |
| bool tab_or_cr_found_in_firstline = false; | |
| bool obs_text_found_in_header_name = false; | |
| bool transfer_encoding_and_content_length = false; | |
| bool unknown_transfer_encoding = false; | |
| bool multiple_transfer_encoding_keys = false; | |
| bool obs_fold_in_header_values_sanitized = false; | |
| bool obs_fold_in_trailer_values_sanitized = false; | |
| bool multiple_spaces_in_firstline_sanitized = false; | |
| bool tab_or_cr_found_in_firstline_sanitized = false; | |
| void Merge(const HttpProtocolDefects& other) { | |
| invalid_method_in_request_first_line |= | |
| other.invalid_method_in_request_first_line; | |
| multiple_content_length_keys |= other.multiple_content_length_keys; | |
| missing_semicolon_in_chunk_extension |= | |
| other.missing_semicolon_in_chunk_extension; | |
| token_before_semicolon_in_chunk_extension |= | |
| other.token_before_semicolon_in_chunk_extension; | |
| invalid_response_code |= other.invalid_response_code; | |
| multiple_spaces_in_firstline |= other.multiple_spaces_in_firstline; | |
| obs_fold_in_header_values |= other.obs_fold_in_header_values; | |
| obs_fold_in_trailer_values |= other.obs_fold_in_trailer_values; | |
| chunked_body_does_not_end_with_crlf_crlf |= | |
| other.chunked_body_does_not_end_with_crlf_crlf; | |
| stray_data_after_chunk |= other.stray_data_after_chunk; | |
| lone_cr_in_request_headers |= other.lone_cr_in_request_headers; | |
| header_name_contains_double_quote |= | |
| other.header_name_contains_double_quote; | |
| header_missing_colon |= other.header_missing_colon; | |
| tab_or_cr_found_in_firstline |= other.tab_or_cr_found_in_firstline; | |
| obs_text_found_in_header_name |= other.obs_text_found_in_header_name; | |
| transfer_encoding_and_content_length |= | |
| other.transfer_encoding_and_content_length; | |
| unknown_transfer_encoding |= other.unknown_transfer_encoding; | |
| multiple_transfer_encoding_keys |= other.multiple_transfer_encoding_keys; | |
| obs_fold_in_header_values_sanitized |= | |
| other.obs_fold_in_header_values_sanitized; | |
| obs_fold_in_trailer_values_sanitized |= | |
| other.obs_fold_in_trailer_values_sanitized; | |
| multiple_spaces_in_firstline_sanitized |= | |
| other.multiple_spaces_in_firstline_sanitized; | |
| tab_or_cr_found_in_firstline_sanitized |= | |
| other.tab_or_cr_found_in_firstline_sanitized; | |
| } | |
| void ForEachDefect( | |
| UnretainedCallback<void(absl::string_view)> callback) const { | |
| if (invalid_method_in_request_first_line) { | |
| callback("invalid_method_in_request_first_line"); | |
| } | |
| if (multiple_content_length_keys) { | |
| callback("multiple_content_length_keys"); | |
| } | |
| if (missing_semicolon_in_chunk_extension) { | |
| callback("missing_semicolon_in_chunk_extension"); | |
| } | |
| if (token_before_semicolon_in_chunk_extension) { | |
| callback("token_before_semicolon_in_chunk_extension"); | |
| } | |
| if (invalid_response_code) { | |
| callback("invalid_response_code"); | |
| } | |
| if (multiple_spaces_in_firstline) { | |
| callback("multiple_spaces_in_firstline"); | |
| } | |
| if (obs_fold_in_header_values) { | |
| callback("obs_fold_in_header_values"); | |
| } | |
| if (obs_fold_in_trailer_values) { | |
| callback("obs_fold_in_trailer_values"); | |
| } | |
| if (chunked_body_does_not_end_with_crlf_crlf) { | |
| callback("chunked_body_does_not_end_with_crlf_crlf"); | |
| } | |
| if (stray_data_after_chunk) { | |
| callback("stray_data_after_chunk"); | |
| } | |
| if (lone_cr_in_request_headers) { | |
| callback("lone_cr_in_request_headers"); | |
| } | |
| if (header_name_contains_double_quote) { | |
| callback("header_name_contains_double_quote"); | |
| } | |
| if (header_missing_colon) { | |
| callback("header_missing_colon"); | |
| } | |
| if (tab_or_cr_found_in_firstline) { | |
| callback("tab_or_cr_found_in_firstline"); | |
| } | |
| if (obs_text_found_in_header_name) { | |
| callback("obs_text_found_in_header_name"); | |
| } | |
| if (transfer_encoding_and_content_length) { | |
| callback("transfer_encoding_and_content_length"); | |
| } | |
| if (unknown_transfer_encoding) { | |
| callback("unknown_transfer_encoding"); | |
| } | |
| if (multiple_transfer_encoding_keys) { | |
| callback("multiple_transfer_encoding_keys"); | |
| } | |
| if (obs_fold_in_header_values_sanitized) { | |
| callback("obs_fold_in_header_values_sanitized"); | |
| } | |
| if (obs_fold_in_trailer_values_sanitized) { | |
| callback("obs_fold_in_trailer_values_sanitized"); | |
| } | |
| if (multiple_spaces_in_firstline_sanitized) { | |
| callback("multiple_spaces_in_firstline_sanitized"); | |
| } | |
| if (tab_or_cr_found_in_firstline_sanitized) { | |
| callback("tab_or_cr_found_in_firstline_sanitized"); | |
| } | |
| } | |
| // T should ideally be strongly typed as | |
| // logs::gfe::GfeLog::HttpProtocolDefects but since Quiche can only depend on | |
| // targets that are compatible with non_prod, a template is used instead. | |
| template <typename T> | |
| void PopulateLogDefects(T& log_defects) const { | |
| if (invalid_method_in_request_first_line) { | |
| log_defects.set_invalid_method_in_request_first_line(true); | |
| } | |
| if (multiple_content_length_keys) { | |
| log_defects.set_multiple_content_length_keys_detected(true); | |
| } | |
| if (missing_semicolon_in_chunk_extension) { | |
| log_defects.set_missing_semicolon_in_chunk_extension(true); | |
| } | |
| if (token_before_semicolon_in_chunk_extension) { | |
| log_defects.set_token_before_semicolon_in_chunk_extension(true); | |
| } | |
| if (invalid_response_code) { | |
| log_defects.set_invalid_response_code(true); | |
| } | |
| if (multiple_spaces_in_firstline) { | |
| log_defects.set_multiple_spaces_found_in_first_line(true); | |
| } | |
| if (obs_fold_in_header_values) { | |
| log_defects.set_obs_fold_in_header_value(true); | |
| } | |
| if (obs_fold_in_trailer_values) { | |
| log_defects.set_obs_fold_in_trailer_value(true); | |
| } | |
| if (chunked_body_does_not_end_with_crlf_crlf) { | |
| log_defects.set_chunked_request_ends_with_crlflf(true); | |
| } | |
| if (stray_data_after_chunk) { | |
| log_defects.set_stray_data_after_chunk(true); | |
| } | |
| if (lone_cr_in_request_headers) { | |
| log_defects.set_cr_found_in_header_value(true); | |
| } | |
| if (header_name_contains_double_quote) { | |
| log_defects.set_header_name_contains_double_quote(true); | |
| } | |
| if (header_missing_colon) { | |
| log_defects.set_header_missing_colon(true); | |
| } | |
| if (transfer_encoding_and_content_length) { | |
| log_defects.set_transfer_encoding_and_content_length(true); | |
| } | |
| if (unknown_transfer_encoding) { | |
| log_defects.set_unknown_transfer_encoding(true); | |
| } | |
| if (multiple_transfer_encoding_keys) { | |
| log_defects.set_multiple_transfer_encoding_keys(true); | |
| } | |
| if (tab_or_cr_found_in_firstline) { | |
| log_defects.set_tab_or_cr_found_in_firstline(true); | |
| } | |
| if (obs_text_found_in_header_name) { | |
| log_defects.set_obs_text_found_in_header_name(true); | |
| } | |
| if (obs_fold_in_header_values_sanitized) { | |
| log_defects.set_obs_fold_in_header_values_sanitized(true); | |
| } | |
| if (obs_fold_in_trailer_values_sanitized) { | |
| log_defects.set_obs_fold_in_trailer_values_sanitized(true); | |
| } | |
| if (multiple_spaces_in_firstline_sanitized) { | |
| log_defects.set_multiple_spaces_in_firstline_sanitized(true); | |
| } | |
| if (tab_or_cr_found_in_firstline_sanitized) { | |
| log_defects.set_tab_or_cr_found_in_firstline_sanitized(true); | |
| } | |
| } | |
| }; | |
| } // namespace quiche | |
| #endif // QUICHE_BALSA_HTTP_PROTOCOL_DEFECTS_H_ |