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