QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright (c) 2018 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 6 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 7 | #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 8 | |
| 9 | namespace quic { |
| 10 | |
| 11 | namespace { |
| 12 | |
| 13 | // Set the first byte of a PRIORITY frame according to its fields. |
| 14 | uint8_t SetPriorityFields(uint8_t num, |
| 15 | PriorityElementType type, |
| 16 | bool prioritized) { |
| 17 | switch (type) { |
| 18 | case REQUEST_STREAM: |
| 19 | return num; |
| 20 | case PUSH_STREAM: |
| 21 | if (prioritized) { |
| 22 | return num | (1 << 6); |
| 23 | } |
| 24 | return num | (1 << 4); |
| 25 | case PLACEHOLDER: |
| 26 | if (prioritized) { |
| 27 | return num | (1 << 7); |
| 28 | } |
| 29 | return num | (1 << 5); |
| 30 | case ROOT_OF_TREE: |
| 31 | if (prioritized) { |
| 32 | num = num | (1 << 6); |
| 33 | return num | (1 << 7); |
| 34 | } |
| 35 | num = num | (1 << 4); |
| 36 | return num | (1 << 5); |
| 37 | default: |
| 38 | QUIC_NOTREACHED(); |
| 39 | return num; |
| 40 | } |
| 41 | } |
| 42 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 43 | } // namespace |
| 44 | |
| 45 | HttpEncoder::HttpEncoder() {} |
| 46 | |
| 47 | HttpEncoder::~HttpEncoder() {} |
| 48 | |
| 49 | QuicByteCount HttpEncoder::SerializeDataFrameHeader( |
| 50 | QuicByteCount payload_length, |
| 51 | std::unique_ptr<char[]>* output) { |
| 52 | DCHECK_NE(0u, payload_length); |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 53 | QuicByteCount header_length = QuicDataWriter::GetVarInt62Len(payload_length) + |
| 54 | QuicDataWriter::GetVarInt62Len( |
| 55 | static_cast<uint64_t>(HttpFrameType::DATA)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 56 | |
| 57 | output->reset(new char[header_length]); |
| 58 | QuicDataWriter writer(header_length, output->get()); |
| 59 | |
| 60 | if (WriteFrameHeader(payload_length, HttpFrameType::DATA, &writer)) { |
| 61 | return header_length; |
| 62 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 63 | QUIC_DLOG(ERROR) |
| 64 | << "Http encoder failed when attempting to serialize data frame header."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 65 | return 0; |
| 66 | } |
| 67 | |
| 68 | QuicByteCount HttpEncoder::SerializeHeadersFrameHeader( |
| 69 | QuicByteCount payload_length, |
| 70 | std::unique_ptr<char[]>* output) { |
| 71 | DCHECK_NE(0u, payload_length); |
| 72 | QuicByteCount header_length = |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 73 | QuicDataWriter::GetVarInt62Len(payload_length) + |
| 74 | QuicDataWriter::GetVarInt62Len( |
| 75 | static_cast<uint64_t>(HttpFrameType::HEADERS)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 76 | |
| 77 | output->reset(new char[header_length]); |
| 78 | QuicDataWriter writer(header_length, output->get()); |
| 79 | |
| 80 | if (WriteFrameHeader(payload_length, HttpFrameType::HEADERS, &writer)) { |
| 81 | return header_length; |
| 82 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 83 | QUIC_DLOG(ERROR) |
| 84 | << "Http encoder failed when attempting to serialize headers " |
| 85 | "frame header."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 86 | return 0; |
| 87 | } |
| 88 | |
| 89 | QuicByteCount HttpEncoder::SerializePriorityFrame( |
| 90 | const PriorityFrame& priority, |
| 91 | std::unique_ptr<char[]>* output) { |
| 92 | QuicByteCount payload_length = |
| 93 | kPriorityFirstByteLength + |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 94 | (priority.prioritized_type == ROOT_OF_TREE |
| 95 | ? 0 |
| 96 | : QuicDataWriter::GetVarInt62Len(priority.prioritized_element_id)) + |
| 97 | (priority.dependency_type == ROOT_OF_TREE |
| 98 | ? 0 |
| 99 | : QuicDataWriter::GetVarInt62Len(priority.element_dependency_id)) + |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 100 | kPriorityWeightLength; |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 101 | QuicByteCount total_length = |
| 102 | GetTotalLength(payload_length, HttpFrameType::PRIORITY); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 103 | |
| 104 | output->reset(new char[total_length]); |
| 105 | QuicDataWriter writer(total_length, output->get()); |
| 106 | |
| 107 | if (!WriteFrameHeader(payload_length, HttpFrameType::PRIORITY, &writer)) { |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 108 | QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| 109 | "priority frame header."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | // Set the first byte of the payload. |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 114 | uint8_t firstByte = 0; |
| 115 | firstByte = SetPriorityFields(firstByte, priority.prioritized_type, true); |
| 116 | firstByte = SetPriorityFields(firstByte, priority.dependency_type, false); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 117 | if (priority.exclusive) { |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 118 | firstByte |= kPriorityExclusiveBit; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 119 | } |
| 120 | |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 121 | if (writer.WriteUInt8(firstByte) && MaybeWriteIds(priority, &writer) && |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 122 | writer.WriteUInt8(priority.weight)) { |
| 123 | return total_length; |
| 124 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 125 | QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| 126 | "priority frame payload."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | QuicByteCount HttpEncoder::SerializeCancelPushFrame( |
| 131 | const CancelPushFrame& cancel_push, |
| 132 | std::unique_ptr<char[]>* output) { |
| 133 | QuicByteCount payload_length = |
| 134 | QuicDataWriter::GetVarInt62Len(cancel_push.push_id); |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 135 | QuicByteCount total_length = |
| 136 | GetTotalLength(payload_length, HttpFrameType::CANCEL_PUSH); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 137 | |
| 138 | output->reset(new char[total_length]); |
| 139 | QuicDataWriter writer(total_length, output->get()); |
| 140 | |
| 141 | if (WriteFrameHeader(payload_length, HttpFrameType::CANCEL_PUSH, &writer) && |
| 142 | writer.WriteVarInt62(cancel_push.push_id)) { |
| 143 | return total_length; |
| 144 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 145 | QUIC_DLOG(ERROR) |
| 146 | << "Http encoder failed when attempting to serialize cancel push frame."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | QuicByteCount HttpEncoder::SerializeSettingsFrame( |
| 151 | const SettingsFrame& settings, |
| 152 | std::unique_ptr<char[]>* output) { |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 153 | QuicByteCount payload_length = 0; |
| 154 | // Calculate the payload length. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 155 | for (auto it = settings.values.begin(); it != settings.values.end(); ++it) { |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 156 | payload_length += QuicDataWriter::GetVarInt62Len(it->first); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 157 | payload_length += QuicDataWriter::GetVarInt62Len(it->second); |
| 158 | } |
| 159 | |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 160 | QuicByteCount total_length = |
| 161 | GetTotalLength(payload_length, HttpFrameType::SETTINGS); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 162 | |
| 163 | output->reset(new char[total_length]); |
| 164 | QuicDataWriter writer(total_length, output->get()); |
| 165 | |
| 166 | if (!WriteFrameHeader(payload_length, HttpFrameType::SETTINGS, &writer)) { |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 167 | QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| 168 | "settings frame header."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 169 | return 0; |
| 170 | } |
| 171 | |
| 172 | for (auto it = settings.values.begin(); it != settings.values.end(); ++it) { |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 173 | if (!writer.WriteVarInt62(it->first) || !writer.WriteVarInt62(it->second)) { |
| 174 | QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| 175 | "settings frame payload."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 176 | return 0; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | return total_length; |
| 181 | } |
| 182 | |
| 183 | QuicByteCount HttpEncoder::SerializePushPromiseFrameWithOnlyPushId( |
| 184 | const PushPromiseFrame& push_promise, |
| 185 | std::unique_ptr<char[]>* output) { |
| 186 | QuicByteCount payload_length = |
| 187 | QuicDataWriter::GetVarInt62Len(push_promise.push_id) + |
| 188 | push_promise.headers.length(); |
| 189 | // GetTotalLength() is not used because headers will not be serialized. |
| 190 | QuicByteCount total_length = |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 191 | QuicDataWriter::GetVarInt62Len(payload_length) + |
| 192 | QuicDataWriter::GetVarInt62Len( |
| 193 | static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE)) + |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 194 | QuicDataWriter::GetVarInt62Len(push_promise.push_id); |
| 195 | |
| 196 | output->reset(new char[total_length]); |
| 197 | QuicDataWriter writer(total_length, output->get()); |
| 198 | |
| 199 | if (WriteFrameHeader(payload_length, HttpFrameType::PUSH_PROMISE, &writer) && |
| 200 | writer.WriteVarInt62(push_promise.push_id)) { |
| 201 | return total_length; |
| 202 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 203 | QUIC_DLOG(ERROR) |
| 204 | << "Http encoder failed when attempting to serialize push promise frame."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | QuicByteCount HttpEncoder::SerializeGoAwayFrame( |
| 209 | const GoAwayFrame& goaway, |
| 210 | std::unique_ptr<char[]>* output) { |
| 211 | QuicByteCount payload_length = |
| 212 | QuicDataWriter::GetVarInt62Len(goaway.stream_id); |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 213 | QuicByteCount total_length = |
| 214 | GetTotalLength(payload_length, HttpFrameType::GOAWAY); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 215 | |
| 216 | output->reset(new char[total_length]); |
| 217 | QuicDataWriter writer(total_length, output->get()); |
| 218 | |
| 219 | if (WriteFrameHeader(payload_length, HttpFrameType::GOAWAY, &writer) && |
| 220 | writer.WriteVarInt62(goaway.stream_id)) { |
| 221 | return total_length; |
| 222 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 223 | QUIC_DLOG(ERROR) |
| 224 | << "Http encoder failed when attempting to serialize goaway frame."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | QuicByteCount HttpEncoder::SerializeMaxPushIdFrame( |
| 229 | const MaxPushIdFrame& max_push_id, |
| 230 | std::unique_ptr<char[]>* output) { |
| 231 | QuicByteCount payload_length = |
| 232 | QuicDataWriter::GetVarInt62Len(max_push_id.push_id); |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 233 | QuicByteCount total_length = |
| 234 | GetTotalLength(payload_length, HttpFrameType::MAX_PUSH_ID); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 235 | |
| 236 | output->reset(new char[total_length]); |
| 237 | QuicDataWriter writer(total_length, output->get()); |
| 238 | |
| 239 | if (WriteFrameHeader(payload_length, HttpFrameType::MAX_PUSH_ID, &writer) && |
| 240 | writer.WriteVarInt62(max_push_id.push_id)) { |
| 241 | return total_length; |
| 242 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 243 | QUIC_DLOG(ERROR) |
| 244 | << "Http encoder failed when attempting to serialize max push id frame."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 245 | return 0; |
| 246 | } |
| 247 | |
| 248 | QuicByteCount HttpEncoder::SerializeDuplicatePushFrame( |
| 249 | const DuplicatePushFrame& duplicate_push, |
| 250 | std::unique_ptr<char[]>* output) { |
| 251 | QuicByteCount payload_length = |
| 252 | QuicDataWriter::GetVarInt62Len(duplicate_push.push_id); |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 253 | QuicByteCount total_length = |
| 254 | GetTotalLength(payload_length, HttpFrameType::DUPLICATE_PUSH); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 255 | |
| 256 | output->reset(new char[total_length]); |
| 257 | QuicDataWriter writer(total_length, output->get()); |
| 258 | |
| 259 | if (WriteFrameHeader(payload_length, HttpFrameType::DUPLICATE_PUSH, |
| 260 | &writer) && |
| 261 | writer.WriteVarInt62(duplicate_push.push_id)) { |
| 262 | return total_length; |
| 263 | } |
renjietang | 3b3e3b3 | 2019-04-22 18:01:20 -0700 | [diff] [blame] | 264 | QUIC_DLOG(ERROR) << "Http encoder failed when attempting to serialize " |
| 265 | "duplicate push frame."; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | bool HttpEncoder::WriteFrameHeader(QuicByteCount length, |
| 270 | HttpFrameType type, |
| 271 | QuicDataWriter* writer) { |
renjietang | fcd91c0 | 2019-04-22 10:40:35 -0700 | [diff] [blame] | 272 | return writer->WriteVarInt62(static_cast<uint64_t>(type)) && |
| 273 | writer->WriteVarInt62(length); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 274 | } |
| 275 | |
renjietang | 2d475cf | 2019-04-18 17:03:37 -0700 | [diff] [blame] | 276 | QuicByteCount HttpEncoder::GetTotalLength(QuicByteCount payload_length, |
| 277 | HttpFrameType type) { |
| 278 | return QuicDataWriter::GetVarInt62Len(payload_length) + |
| 279 | QuicDataWriter::GetVarInt62Len(static_cast<uint64_t>(type)) + |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 280 | payload_length; |
| 281 | } |
| 282 | |
renjietang | ec09576 | 2019-06-19 14:35:02 -0700 | [diff] [blame] | 283 | bool HttpEncoder::MaybeWriteIds(const PriorityFrame& priority, |
| 284 | QuicDataWriter* writer) { |
| 285 | if (priority.prioritized_type != ROOT_OF_TREE) { |
| 286 | if (!writer->WriteVarInt62(priority.prioritized_element_id)) { |
| 287 | return false; |
| 288 | } |
| 289 | } else { |
| 290 | DCHECK_EQ(0u, priority.prioritized_element_id) |
| 291 | << "Prioritized element id should be 0 when prioritized type is " |
| 292 | "ROOT_OF_TREE"; |
| 293 | } |
| 294 | if (priority.dependency_type != ROOT_OF_TREE) { |
| 295 | if (!writer->WriteVarInt62(priority.element_dependency_id)) { |
| 296 | return false; |
| 297 | } |
| 298 | } else { |
| 299 | DCHECK_EQ(0u, priority.element_dependency_id) |
| 300 | << "Element dependency id should be 0 when dependency type is " |
| 301 | "ROOT_OF_TREE"; |
| 302 | } |
| 303 | return true; |
| 304 | } |
| 305 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 306 | } // namespace quic |