blob: 731ec102866751606c141b408ad69864025b74da [file] [log] [blame]
QUICHE teama6ef0a62019-03-07 20:34:33 -05001// 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/qpack/qpack_progressive_encoder.h"
6
vasilvv872e7a32019-03-12 16:42:44 -07007#include <string>
8
QUICHE teama6ef0a62019-03-07 20:34:33 -05009#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
10#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
vasilvv0fb44432019-03-13 22:47:36 -070011#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
QUICHE teama6ef0a62019-03-07 20:34:33 -050012#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
13
14namespace quic {
15
16QpackProgressiveEncoder::QpackProgressiveEncoder(
17 QuicStreamId stream_id,
18 QpackHeaderTable* header_table,
19 QpackEncoderStreamSender* encoder_stream_sender,
20 const spdy::SpdyHeaderBlock* header_list)
21 : stream_id_(stream_id),
22 header_table_(header_table),
23 encoder_stream_sender_(encoder_stream_sender),
24 header_list_(header_list),
bnc78053612019-04-25 20:08:12 -070025 header_list_iterator_(header_list_.begin()),
QUICHE teama6ef0a62019-03-07 20:34:33 -050026 prefix_encoded_(false) {
27 // TODO(bnc): Use |stream_id_| for dynamic table entry management, and
28 // remove this dummy DCHECK.
29 DCHECK_LE(0u, stream_id_);
30
31 DCHECK(header_table_);
32 DCHECK(encoder_stream_sender_);
QUICHE teama6ef0a62019-03-07 20:34:33 -050033}
34
35bool QpackProgressiveEncoder::HasNext() const {
bnc78053612019-04-25 20:08:12 -070036 return header_list_iterator_ != header_list_.end() || !prefix_encoded_;
QUICHE teama6ef0a62019-03-07 20:34:33 -050037}
38
39void QpackProgressiveEncoder::Next(size_t max_encoded_bytes,
vasilvvc48c8712019-03-11 13:38:16 -070040 std::string* output) {
QUICHE teama6ef0a62019-03-07 20:34:33 -050041 DCHECK_NE(0u, max_encoded_bytes);
42 DCHECK(HasNext());
43
44 // Since QpackInstructionEncoder::Next() does not indicate the number of bytes
45 // written, save the maximum new size of |*output|.
46 const size_t max_length = output->size() + max_encoded_bytes;
47
48 DCHECK_LT(output->size(), max_length);
49
50 if (!prefix_encoded_ && !instruction_encoder_.HasNext()) {
51 // TODO(bnc): Implement dynamic entries and set Required Insert Count and
52 // Delta Base accordingly.
53 instruction_encoder_.set_varint(0);
54 instruction_encoder_.set_varint2(0);
55 instruction_encoder_.set_s_bit(false);
56
57 instruction_encoder_.Encode(QpackPrefixInstruction());
58
59 DCHECK(instruction_encoder_.HasNext());
60 }
61
62 do {
bnc78053612019-04-25 20:08:12 -070063 // Call QpackInstructionEncoder::Encode() for |*header_list_iterator_| if it
QUICHE teama6ef0a62019-03-07 20:34:33 -050064 // has not been called yet.
65 if (!instruction_encoder_.HasNext()) {
66 DCHECK(prefix_encoded_);
67
68 // Even after |name| and |value| go out of scope, copies of these
69 // QuicStringPieces retained by QpackInstructionEncoder are still valid as
70 // long as |header_list_| is valid.
71 QuicStringPiece name = header_list_iterator_->first;
72 QuicStringPiece value = header_list_iterator_->second;
73
74 // |is_static| and |index| are saved by QpackInstructionEncoder by value,
75 // there are no lifetime concerns.
76 bool is_static;
77 uint64_t index;
78
79 auto match_type =
80 header_table_->FindHeaderField(name, value, &is_static, &index);
81
82 switch (match_type) {
83 case QpackHeaderTable::MatchType::kNameAndValue:
84 DCHECK(is_static) << "Dynamic table entries not supported yet.";
85
86 instruction_encoder_.set_s_bit(is_static);
87 instruction_encoder_.set_varint(index);
88
89 instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction());
90
91 break;
92 case QpackHeaderTable::MatchType::kName:
93 DCHECK(is_static) << "Dynamic table entries not supported yet.";
94
95 instruction_encoder_.set_s_bit(is_static);
96 instruction_encoder_.set_varint(index);
97 instruction_encoder_.set_value(value);
98
99 instruction_encoder_.Encode(
100 QpackLiteralHeaderFieldNameReferenceInstruction());
101
102 break;
103 case QpackHeaderTable::MatchType::kNoMatch:
104 instruction_encoder_.set_name(name);
105 instruction_encoder_.set_value(value);
106
107 instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction());
108
109 break;
110 }
111 }
112
113 DCHECK(instruction_encoder_.HasNext());
114
115 instruction_encoder_.Next(max_length - output->size(), output);
116
117 if (instruction_encoder_.HasNext()) {
118 // There was not enough room to completely encode current header field.
119 DCHECK_EQ(output->size(), max_length);
120
121 return;
122 }
123
124 // It is possible that the output buffer was just large enough for encoding
125 // the current header field, hence equality is allowed here.
126 DCHECK_LE(output->size(), max_length);
127
128 if (prefix_encoded_) {
129 // Move on to the next header field.
130 ++header_list_iterator_;
131 } else {
132 // Mark prefix as encoded.
133 prefix_encoded_ = true;
134 }
135 } while (HasNext() && output->size() < max_length);
136}
137
138} // namespace quic