xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/spdy/core/spdy_frame_builder.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2012 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 "quiche/spdy/core/spdy_frame_builder.h"
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <cstdint>
10 #include <cstring>
11 
12 #include "absl/strings/string_view.h"
13 #include "quiche/common/platform/api/quiche_bug_tracker.h"
14 #include "quiche/common/platform/api/quiche_logging.h"
15 #include "quiche/spdy/core/spdy_bitmasks.h"
16 #include "quiche/spdy/core/spdy_protocol.h"
17 #include "quiche/spdy/core/zero_copy_output_buffer.h"
18 
19 namespace spdy {
20 
SpdyFrameBuilder(size_t size)21 SpdyFrameBuilder::SpdyFrameBuilder(size_t size)
22     : buffer_(new char[size]), capacity_(size), length_(0), offset_(0) {}
23 
SpdyFrameBuilder(size_t size,ZeroCopyOutputBuffer * output)24 SpdyFrameBuilder::SpdyFrameBuilder(size_t size, ZeroCopyOutputBuffer* output)
25     : buffer_(output == nullptr ? new char[size] : nullptr),
26       output_(output),
27       capacity_(size),
28       length_(0),
29       offset_(0) {}
30 
31 SpdyFrameBuilder::~SpdyFrameBuilder() = default;
32 
GetWritableBuffer(size_t length)33 char* SpdyFrameBuilder::GetWritableBuffer(size_t length) {
34   if (!CanWrite(length)) {
35     return nullptr;
36   }
37   return buffer_.get() + offset_ + length_;
38 }
39 
GetWritableOutput(size_t length,size_t * actual_length)40 char* SpdyFrameBuilder::GetWritableOutput(size_t length,
41                                           size_t* actual_length) {
42   char* dest = nullptr;
43   int size = 0;
44 
45   if (!CanWrite(length)) {
46     return nullptr;
47   }
48   output_->Next(&dest, &size);
49   *actual_length = std::min<size_t>(length, size);
50   return dest;
51 }
52 
Seek(size_t length)53 bool SpdyFrameBuilder::Seek(size_t length) {
54   if (!CanWrite(length)) {
55     return false;
56   }
57   if (output_ == nullptr) {
58     length_ += length;
59   } else {
60     output_->AdvanceWritePtr(length);
61     length_ += length;
62   }
63   return true;
64 }
65 
BeginNewFrame(SpdyFrameType type,uint8_t flags,SpdyStreamId stream_id)66 bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, uint8_t flags,
67                                      SpdyStreamId stream_id) {
68   uint8_t raw_frame_type = SerializeFrameType(type);
69   QUICHE_DCHECK(IsDefinedFrameType(raw_frame_type));
70   QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
71   bool success = true;
72   if (length_ > 0) {
73     QUICHE_BUG(spdy_bug_73_1)
74         << "SpdyFrameBuilder doesn't have a clean state when BeginNewFrame"
75         << "is called. Leftover length_ is " << length_;
76     offset_ += length_;
77     length_ = 0;
78   }
79 
80   success &= WriteUInt24(capacity_ - offset_ - kFrameHeaderSize);
81   success &= WriteUInt8(raw_frame_type);
82   success &= WriteUInt8(flags);
83   success &= WriteUInt32(stream_id);
84   QUICHE_DCHECK_EQ(kDataFrameMinimumSize, length_);
85   return success;
86 }
87 
BeginNewFrame(SpdyFrameType type,uint8_t flags,SpdyStreamId stream_id,size_t length)88 bool SpdyFrameBuilder::BeginNewFrame(SpdyFrameType type, uint8_t flags,
89                                      SpdyStreamId stream_id, size_t length) {
90   uint8_t raw_frame_type = SerializeFrameType(type);
91   QUICHE_DCHECK(IsDefinedFrameType(raw_frame_type));
92   QUICHE_DCHECK_EQ(0u, stream_id & ~kStreamIdMask);
93   QUICHE_BUG_IF(spdy_bug_73_2, length > kSpdyMaxFrameSizeLimit)
94       << "Frame length  " << length << " is longer than frame size limit.";
95   return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length);
96 }
97 
BeginNewUncheckedFrame(uint8_t raw_frame_type,uint8_t flags,SpdyStreamId stream_id,size_t length)98 bool SpdyFrameBuilder::BeginNewUncheckedFrame(uint8_t raw_frame_type,
99                                               uint8_t flags,
100                                               SpdyStreamId stream_id,
101                                               size_t length) {
102   return BeginNewFrameInternal(raw_frame_type, flags, stream_id, length);
103 }
104 
BeginNewFrameInternal(uint8_t raw_frame_type,uint8_t flags,SpdyStreamId stream_id,size_t length)105 bool SpdyFrameBuilder::BeginNewFrameInternal(uint8_t raw_frame_type,
106                                              uint8_t flags,
107                                              SpdyStreamId stream_id,
108                                              size_t length) {
109   QUICHE_DCHECK_EQ(length, length & kLengthMask);
110   bool success = true;
111 
112   offset_ += length_;
113   length_ = 0;
114 
115   success &= WriteUInt24(length);
116   success &= WriteUInt8(raw_frame_type);
117   success &= WriteUInt8(flags);
118   success &= WriteUInt32(stream_id);
119   QUICHE_DCHECK_EQ(kDataFrameMinimumSize, length_);
120   return success;
121 }
122 
WriteStringPiece32(const absl::string_view value)123 bool SpdyFrameBuilder::WriteStringPiece32(const absl::string_view value) {
124   if (!WriteUInt32(value.size())) {
125     return false;
126   }
127 
128   return WriteBytes(value.data(), value.size());
129 }
130 
WriteBytes(const void * data,uint32_t data_len)131 bool SpdyFrameBuilder::WriteBytes(const void* data, uint32_t data_len) {
132   if (!CanWrite(data_len)) {
133     return false;
134   }
135 
136   if (output_ == nullptr) {
137     char* dest = GetWritableBuffer(data_len);
138     memcpy(dest, data, data_len);
139     Seek(data_len);
140   } else {
141     char* dest = nullptr;
142     size_t size = 0;
143     size_t total_written = 0;
144     const char* data_ptr = reinterpret_cast<const char*>(data);
145     while (data_len > 0) {
146       dest = GetWritableOutput(data_len, &size);
147       if (dest == nullptr || size == 0) {
148         // Unable to make progress.
149         return false;
150       }
151       uint32_t to_copy = std::min<uint32_t>(data_len, size);
152       const char* src = data_ptr + total_written;
153       memcpy(dest, src, to_copy);
154       Seek(to_copy);
155       data_len -= to_copy;
156       total_written += to_copy;
157     }
158   }
159   return true;
160 }
161 
CanWrite(size_t length) const162 bool SpdyFrameBuilder::CanWrite(size_t length) const {
163   if (length > kLengthMask) {
164     QUICHE_DCHECK(false);
165     return false;
166   }
167 
168   if (output_ == nullptr) {
169     if (offset_ + length_ + length > capacity_) {
170       QUICHE_DLOG(FATAL) << "Requested: " << length
171                          << " capacity: " << capacity_
172                          << " used: " << offset_ + length_;
173       return false;
174     }
175   } else {
176     if (length > output_->BytesFree()) {
177       return false;
178     }
179   }
180 
181   return true;
182 }
183 
184 }  // namespace spdy
185