| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_SPDY_ALPS_DECODER_H_ |
| #define NET_SPDY_ALPS_DECODER_H_ |
| |
| #include <cstddef> |
| |
| #include "base/containers/span.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "net/base/net_export.h" |
| #include "net/third_party/quiche/src/quiche/spdy/core/http2_frame_decoder_adapter.h" |
| #include "net/third_party/quiche/src/quiche/spdy/core/spdy_no_op_visitor.h" |
| #include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h" |
| |
| namespace net { |
| |
| // Class to parse HTTP/2 frames in the extension_data field |
| // of the ALPS TLS extension. |
| class NET_EXPORT_PRIVATE AlpsDecoder { |
| public: |
| // These values are persisted to logs. Entries should not be renumbered, and |
| // numeric values should never be reused. |
| enum class Error { |
| // No error has occurred. |
| kNoError = 0, |
| // HTTP/2 framing error detected by Http2DecoderAdapter. |
| kFramingError = 1, |
| // Forbidden HTTP/2 frame received. |
| kForbiddenFrame = 2, |
| // Input does not end on HTTP/2 frame boundary. |
| kNotOnFrameBoundary = 3, |
| // SETTINGS frame with ACK received. |
| kSettingsWithAck = 4, |
| // ACCEPT_CH received on invalid stream. |
| kAcceptChInvalidStream = 5, |
| // ACCEPT_CH received with flags. |
| kAcceptChWithFlags = 6, |
| // Malformed ACCEPT_CH payload. |
| kAcceptChMalformed = 7, |
| kMaxValue = kAcceptChMalformed |
| }; |
| |
| AlpsDecoder(); |
| ~AlpsDecoder(); |
| |
| // Decode a stream of HTTP/2 frames received via the ALPS TLS extension. |
| // The HTTP/2 connection preface MUST NOT be present in the input. |
| // Frames other than SETTINGS and ACCEPT_CH are ignored other than for the |
| // purposes of enforcing HTTP/2 framing rules. |
| // May only be called once, with the entire ALPS extension_data. |
| // Returns an error code, or Error::kNoError if no error has occurred. |
| // The requirement that the first frame MUST be SETTINGS is not enforced, |
| // because that only applies to HTTP/2 connections, not ALPS data. |
| [[nodiscard]] Error Decode(base::span<const char> data); |
| |
| // The number of SETTINGS frames received. |
| int settings_frame_count() const; |
| |
| // The HTTP/2 setting parameters parsed from |data|. |
| const spdy::SettingsMap& GetSettings() const { |
| return settings_parser_.GetSettings(); |
| } |
| // Origins and corresponding Accept-CH values parsed from |data|. See |
| // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02 |
| const std::vector<spdy::AcceptChOriginValuePair>& GetAcceptCh() const { |
| return accept_ch_parser_.GetAcceptCh(); |
| } |
| |
| private: |
| class SettingsParser : public spdy::SpdyNoOpVisitor { |
| public: |
| SettingsParser(); |
| ~SettingsParser() override; |
| |
| bool forbidden_frame_received() const { return forbidden_frame_received_; } |
| bool settings_ack_received() const { return settings_ack_received_; } |
| int settings_frame_count() const { return settings_frame_count_; } |
| // Number of SETTINGS frames received. |
| const spdy::SettingsMap& GetSettings() const { return settings_; } |
| |
| // SpdyFramerVisitorInterface overrides. |
| void OnCommonHeader(spdy::SpdyStreamId stream_id, |
| size_t length, |
| uint8_t type, |
| uint8_t flags) override; |
| void OnSettings() override; |
| void OnSetting(spdy::SpdySettingsId id, uint32_t value) override; |
| void OnSettingsAck() override; |
| |
| private: |
| // True if a forbidden HTTP/2 frame has been received. |
| bool forbidden_frame_received_ = false; |
| // True if a SETTINGS frame with ACK flag has been received. |
| bool settings_ack_received_ = false; |
| // Number of SETTINGS frames received. |
| int settings_frame_count_ = 0; |
| // Accumulated setting parameters. |
| spdy::SettingsMap settings_; |
| }; |
| |
| // Class to parse ACCEPT_CH frames. |
| class AcceptChParser : public spdy::ExtensionVisitorInterface { |
| public: |
| AcceptChParser(); |
| ~AcceptChParser() override; |
| |
| const std::vector<spdy::AcceptChOriginValuePair>& GetAcceptCh() const { |
| return accept_ch_; |
| } |
| |
| // Returns an error code, or Error::kNoError if no error has occurred. |
| Error error() const { return error_; } |
| |
| // Returns an error code if it was bypassed, or Error::kNoError if no error was bypassed. |
| Error error_bypass() const { return error_bypass_; } |
| |
| // ExtensionVisitorInterface implementation. |
| |
| // Settings are parsed in a SpdyFramerVisitorInterface implementation, |
| // because ExtensionVisitorInterface does not provide information about |
| // receiving an empty SETTINGS frame. |
| void OnSetting(spdy::SpdySettingsId id, uint32_t value) override {} |
| |
| bool OnFrameHeader(spdy::SpdyStreamId stream_id, |
| size_t length, |
| uint8_t type, |
| uint8_t flags) override; |
| void OnFramePayload(const char* data, size_t len) override; |
| |
| private: |
| // Accumulated ACCEPT_CH values. |
| std::vector<spdy::AcceptChOriginValuePair> accept_ch_; |
| |
| Error error_ = Error::kNoError; |
| Error error_bypass_ = Error::kNoError; |
| }; |
| |
| SettingsParser settings_parser_; |
| AcceptChParser accept_ch_parser_; |
| http2::Http2DecoderAdapter decoder_adapter_; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_SPDY_ALPS_DECODER_H_ |