| // Copyright (c) 2018 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. |
| |
| #include "quic/core/http/http_encoder.h" |
| #include <cstdint> |
| #include <memory> |
| |
| #include "quic/core/crypto/quic_random.h" |
| #include "quic/core/quic_data_writer.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/platform/api/quic_bug_tracker.h" |
| #include "quic/platform/api/quic_flag_utils.h" |
| #include "quic/platform/api/quic_flags.h" |
| #include "quic/platform/api/quic_logging.h" |
| |
| namespace quic { |
| |
| namespace { |
| |
| bool WriteFrameHeader(QuicByteCount length, |
| HttpFrameType type, |
| QuicDataWriter* writer) { |
| return writer->WriteVarInt62(static_cast<uint64_t>(type)) && |
| writer->WriteVarInt62(length); |
| } |
| |
| QuicByteCount GetTotalLength(QuicByteCount payload_length, HttpFrameType type) { |
| return QuicDataWriter::GetVarInt62Len(payload_length) + |
| QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>(type)) + |
| payload_length; |
| } |
| |
| } // namespace |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeDataFrameHeader( |
| QuicByteCount payload_length, |
| std::unique_ptr<char[]>* output) { |
| QUICHE_DCHECK_NE(0u, payload_length); |
| QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(payload_length) + |
| QuicDataWriter::GetVarInt62Len( |
| static_cast<uint64_t>(HttpFrameType::DATA)); |
| |
| output->reset(new char[header_length]); |
| QuicDataWriter writer(header_length, output->get()); |
| |
| if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) { |
| return header_length; |
| } |
| QUIC_DLOG(ERROR) |
| << "Http encoder failed when attempting to serialize data frame header."; |
| return 0; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeHeadersFrameHeader( |
| QuicByteCount payload_length, |
| std::unique_ptr<char[]>* output) { |
| QUICHE_DCHECK_NE(0u, payload_length); |
| QuicByteCount header_length = |
| QuicDataWriter::GetVarInt62Len(payload_length) + |
| QuicDataWriter::GetVarInt62Len( |
| static_cast<uint64_t>(HttpFrameType::HEADERS)); |
| |
| output->reset(new char[header_length]); |
| QuicDataWriter writer(header_length, output->get()); |
| |
| if (WriteFrameHeader(payload_length, HttpFrameType::HEADERS, &writer)) { |
| return header_length; |
| } |
| QUIC_DLOG(ERROR) |
| << "Http encoder failed when attempting to serialize headers " |
| "frame header."; |
| return 0; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeSettingsFrame( |
| const SettingsFrame& settings, |
| std::unique_ptr<char[]>* output) { |
| QuicByteCount payload_length = 0; |
| std::vector<std::pair<uint64_t, uint64_t>> ordered_settings{ |
| settings.values.begin(), settings.values.end()}; |
| std::sort(ordered_settings.begin(), ordered_settings.end()); |
| // Calculate the payload length. |
| for (const auto& p : ordered_settings) { |
| payload_length += QuicDataWriter::GetVarInt62Len(p.first); |
| payload_length += QuicDataWriter::GetVarInt62Len(p.second); |
| } |
| |
| QuicByteCount total_length = |
| GetTotalLength(payload_length, HttpFrameType::SETTINGS); |
| |
| output->reset(new char[total_length]); |
| QuicDataWriter writer(total_length, output->get()); |
| |
| if (!WriteFrameHeader(payload_length, HttpFrameType::SETTINGS, &writer)) { |
| QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| "settings frame header."; |
| return 0; |
| } |
| |
| for (const auto& p : ordered_settings) { |
| if (!writer.WriteVarInt62(p.first) || !writer.WriteVarInt62(p.second)) { |
| QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| "settings frame payload."; |
| return 0; |
| } |
| } |
| |
| return total_length; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeGoAwayFrame( |
| const GoAwayFrame& goaway, |
| std::unique_ptr<char[]>* output) { |
| QuicByteCount payload_length = QuicDataWriter::GetVarInt62Len(goaway.id); |
| QuicByteCount total_length = |
| GetTotalLength(payload_length, HttpFrameType::GOAWAY); |
| |
| output->reset(new char[total_length]); |
| QuicDataWriter writer(total_length, output->get()); |
| |
| if (WriteFrameHeader(payload_length, HttpFrameType::GOAWAY, &writer) && |
| writer.WriteVarInt62(goaway.id)) { |
| return total_length; |
| } |
| QUIC_DLOG(ERROR) |
| << "Http encoder failed when attempting to serialize goaway frame."; |
| return 0; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializePriorityUpdateFrame( |
| const PriorityUpdateFrame& priority_update, |
| std::unique_ptr<char[]>* output) { |
| if (priority_update.prioritized_element_type != REQUEST_STREAM) { |
| QUIC_BUG(quic_bug_10402_1) |
| << "PRIORITY_UPDATE for push streams not implemented"; |
| return 0; |
| } |
| |
| QuicByteCount payload_length = |
| QuicDataWriter::GetVarInt62Len(priority_update.prioritized_element_id) + |
| priority_update.priority_field_value.size(); |
| QuicByteCount total_length = GetTotalLength( |
| payload_length, HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM); |
| |
| output->reset(new char[total_length]); |
| QuicDataWriter writer(total_length, output->get()); |
| |
| if (WriteFrameHeader(payload_length, |
| HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM, |
| &writer) && |
| writer.WriteVarInt62(priority_update.prioritized_element_id) && |
| writer.WriteBytes(priority_update.priority_field_value.data(), |
| priority_update.priority_field_value.size())) { |
| return total_length; |
| } |
| |
| QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| "PRIORITY_UPDATE frame."; |
| return 0; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeAcceptChFrame( |
| const AcceptChFrame& accept_ch, |
| std::unique_ptr<char[]>* output) { |
| QuicByteCount payload_length = 0; |
| for (const auto& entry : accept_ch.entries) { |
| payload_length += QuicDataWriter::GetVarInt62Len(entry.origin.size()); |
| payload_length += entry.origin.size(); |
| payload_length += QuicDataWriter::GetVarInt62Len(entry.value.size()); |
| payload_length += entry.value.size(); |
| } |
| |
| QuicByteCount total_length = |
| GetTotalLength(payload_length, HttpFrameType::ACCEPT_CH); |
| |
| output->reset(new char[total_length]); |
| QuicDataWriter writer(total_length, output->get()); |
| |
| if (!WriteFrameHeader(payload_length, HttpFrameType::ACCEPT_CH, &writer)) { |
| QUIC_DLOG(ERROR) |
| << "Http encoder failed to serialize ACCEPT_CH frame header."; |
| return 0; |
| } |
| |
| for (const auto& entry : accept_ch.entries) { |
| if (!writer.WriteStringPieceVarInt62(entry.origin) || |
| !writer.WriteStringPieceVarInt62(entry.value)) { |
| QUIC_DLOG(ERROR) |
| << "Http encoder failed to serialize ACCEPT_CH frame payload."; |
| return 0; |
| } |
| } |
| |
| return total_length; |
| } |
| |
| // static |
| QuicByteCount HttpEncoder::SerializeGreasingFrame( |
| std::unique_ptr<char[]>* output) { |
| uint64_t frame_type; |
| QuicByteCount payload_length; |
| std::string payload; |
| if (!GetQuicFlag(FLAGS_quic_enable_http3_grease_randomness)) { |
| frame_type = 0x40; |
| payload_length = 1; |
| payload = "a"; |
| } else { |
| uint32_t result; |
| QuicRandom::GetInstance()->RandBytes(&result, sizeof(result)); |
| frame_type = 0x1fULL * static_cast<uint64_t>(result) + 0x21ULL; |
| |
| // The payload length is random but within [0, 3]; |
| payload_length = result % 4; |
| |
| if (payload_length > 0) { |
| std::unique_ptr<char[]> buffer(new char[payload_length]); |
| QuicRandom::GetInstance()->RandBytes(buffer.get(), payload_length); |
| payload = std::string(buffer.get(), payload_length); |
| } |
| } |
| QuicByteCount total_length = QuicDataWriter::GetVarInt62Len(frame_type) + |
| QuicDataWriter::GetVarInt62Len(payload_length) + |
| payload_length; |
| |
| output->reset(new char[total_length]); |
| QuicDataWriter writer(total_length, output->get()); |
| |
| bool success = |
| writer.WriteVarInt62(frame_type) && writer.WriteVarInt62(payload_length); |
| |
| if (payload_length > 0) { |
| success &= writer.WriteBytes(payload.data(), payload_length); |
| } |
| |
| if (success) { |
| return total_length; |
| } |
| |
| QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| "greasing frame."; |
| return 0; |
| } |
| |
| QuicByteCount HttpEncoder::SerializeWebTransportStreamFrameHeader( |
| WebTransportSessionId session_id, |
| std::unique_ptr<char[]>* output) { |
| uint64_t stream_type = |
| static_cast<uint64_t>(HttpFrameType::WEBTRANSPORT_STREAM); |
| QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(stream_type) + |
| QuicDataWriter::GetVarInt62Len(session_id); |
| |
| *output = std::make_unique<char[]>(header_length); |
| QuicDataWriter writer(header_length, output->get()); |
| bool success = |
| writer.WriteVarInt62(stream_type) && writer.WriteVarInt62(session_id); |
| if (success && writer.remaining() == 0) { |
| return header_length; |
| } |
| |
| QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| "WEBTRANSPORT_STREAM frame header."; |
| return 0; |
| } |
| |
| } // namespace quic |