Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: net/websockets/websocket_frame_builder.cc

Issue 10384180: Add functions used for building WebSocket frame data. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 "net/websockets/websocket_frame_builder.h"
6
7 #include <algorithm>
8
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/scoped_vector.h"
13 #include "base/rand_util.h"
14 #include "net/base/big_endian.h"
15 #include "net/websockets/websocket_frame.h"
16
17 namespace {
18
19 const uint8 kFinalBit = 0x80;
20 const uint8 kReserved1Bit = 0x40;
21 const uint8 kReserved2Bit = 0x20;
22 const uint8 kReserved3Bit = 0x10;
23 const uint8 kOpCodeMask = 0xF;
24 const uint8 kMaskBit = 0x80;
25 const uint64 kMaxPayloadLengthWithoutExtendedLengthField = 125;
26 const uint64 kPayloadLengthWithTwoByteExtendedLengthField = 126;
27 const uint64 kPayloadLengthWithEightByteExtendedLengthField = 127;
28
29 } // Unnamed namespace.
30
31 namespace net {
32
33 WebSocketFrameBuilder::WebSocketFrameBuilder()
34 : frame_offset_(0),
35 has_pinned_masking_key_for_testing_(false),
36 failed_(false) {
37 std::fill(masking_key_,
38 masking_key_ + WebSocketFrameHeader::kMaskingKeyLength,
39 '\0');
40 std::fill(
41 pinned_masking_key_for_testing_,
42 pinned_masking_key_for_testing_ + WebSocketFrameHeader::kMaskingKeyLength,
43 '\0');
44 }
45
46 WebSocketFrameBuilder::~WebSocketFrameBuilder() {
47 }
48
49 bool WebSocketFrameBuilder::Encode(
50 ScopedVector<WebSocketFrameChunk> frame_chunks,
51 std::vector<char>* output) {
52 if (failed_)
53 return false;
54
55 // We DCHECK inconsistency of |frame_chunks| such as payload length mismatch
mmenke 2012/05/15 18:36:31 nit: Think "We DCHECK on inconsistent |frame_chun
56 // or missing |header| in the first chunk, because these are programming
57 // errors we want to avoid. When DCHECK isn't functional (i.e. Release and
58 // !DCHECK_ALWAYS_ON), we fail gracefully on these errors.
59 for (ScopedVector<WebSocketFrameChunk>::iterator iter = frame_chunks.begin();
60 iter != frame_chunks.end(); ++iter) {
61 WebSocketFrameChunk* chunk = *iter;
62 if (chunk->header.get()) { // Beginning of a new frame.
63 DCHECK(!current_frame_header_.get());
64 DCHECK_EQ(0u, frame_offset_);
65 if (current_frame_header_.get() || frame_offset_ != 0u) {
66 Fail();
67 return false;
68 }
69 current_frame_header_ = chunk->header.Pass();
70 if (current_frame_header_->masked)
71 GenerateNewMaskingKey();
72 if (!EncodeFrameHeader(current_frame_header_.get(), output)) {
73 Fail();
74 return false;
75 }
76 }
77
78 DCHECK(current_frame_header_.get());
79 if (!current_frame_header_.get()) {
80 Fail();
81 return false;
82 }
83
84 if (current_frame_header_->masked)
mmenke 2012/05/15 18:36:31 According to specs, it's invalid for this to be fa
85 MaskPayload(&chunk->data);
86 // FIXME(yutak): Remove copy here. Ultimately, we probably need to have
87 // "gather writes" ability (aka vectored I/O) in the Socket interface
88 // so we can send data from multiple local buffers without data copies.
89 output->insert(output->end(), chunk->data.begin(), chunk->data.end());
90 frame_offset_ += chunk->data.size();
91 DCHECK_LE(frame_offset_, current_frame_header_->payload_length);
92 if (frame_offset_ > current_frame_header_->payload_length) {
93 Fail();
94 return false;
95 }
96
97 if (chunk->final_chunk) {
98 // Throw away the information about the current frame to get ready
99 // for the next frame.
100 DCHECK_EQ(frame_offset_, current_frame_header_->payload_length);
101 if (frame_offset_ != current_frame_header_->payload_length) {
mmenke 2012/05/15 18:36:31 I'm a bit worried about the interface complexity a
102 Fail();
103 return false;
104 }
105 current_frame_header_.reset();
106 frame_offset_ = 0;
107 }
108 }
109
110 return true;
111 }
112
113 bool WebSocketFrameBuilder::EncodeFrameHeader(
114 WebSocketFrameHeader* header,
115 std::vector<char>* output) const {
116 DCHECK(header);
117
118 // header->opcode must fit to kOpCodeMask.
119 DCHECK((header->opcode & kOpCodeMask) == header->opcode);
120
121 uint8 first_byte = 0u;
122 first_byte |= header->final ? kFinalBit : 0u;
123 first_byte |= header->reserved1 ? kReserved1Bit : 0u;
124 first_byte |= header->reserved2 ? kReserved2Bit : 0u;
125 first_byte |= header->reserved3 ? kReserved3Bit : 0u;
126 first_byte |= header->opcode;
127
128 uint8 second_byte = 0;
mmenke 2012/05/15 18:36:31 nit: 0u
129 second_byte |= header->masked ? kMaskBit : 0u;
130 if (header->payload_length <=
131 kMaxPayloadLengthWithoutExtendedLengthField) {
132 second_byte |= header->payload_length;
133 } else if (header->payload_length <= kuint16max) {
134 second_byte |= kPayloadLengthWithTwoByteExtendedLengthField;
135 } else if (header->payload_length <= static_cast<uint64>(kint64max)) {
136 second_byte |= kPayloadLengthWithEightByteExtendedLengthField;
137 } else {
138 // WebSocket protocol specification doesn't allow payload length to
139 // exceed kint64max (0x7FFFFFFFFFFFFFFF).
140 return false;
141 }
142
143 output->push_back(first_byte);
144 output->push_back(second_byte);
145
146 // Writes "extended payload length" field.
147 if (second_byte == kPayloadLengthWithTwoByteExtendedLengthField) {
148 uint16 payload_length_16 = static_cast<uint16>(header->payload_length);
149 char encoded[sizeof(uint16)];
150 WriteBigEndian(encoded, payload_length_16);
151 output->insert(output->end(), encoded, encoded + sizeof(uint16));
152 } else if (second_byte == kPayloadLengthWithEightByteExtendedLengthField) {
153 char encoded[sizeof(uint64)];
154 WriteBigEndian(encoded, header->payload_length);
155 output->insert(output->end(), encoded, encoded + sizeof(uint64));
156 }
157
158 // Writes "masking key" field, if needed.
159 if (header->masked) {
160 output->insert(output->end(), masking_key_,
161 masking_key_ + WebSocketFrameHeader::kMaskingKeyLength);
162 }
163
164 return true;
165 }
166
167 void WebSocketFrameBuilder::PinMaskingKeyForTesting(const char masking_key[]) {
168 std::copy(masking_key, masking_key + WebSocketFrameHeader::kMaskingKeyLength,
169 pinned_masking_key_for_testing_);
170 has_pinned_masking_key_for_testing_ = true;
171 }
172
173 void WebSocketFrameBuilder::GenerateNewMaskingKey() {
174 DCHECK(current_frame_header_->masked);
175
176 static const size_t kMaskingKeyLength =
177 WebSocketFrameHeader::kMaskingKeyLength;
178
179 if (has_pinned_masking_key_for_testing_) {
180 std::copy(pinned_masking_key_for_testing_,
181 pinned_masking_key_for_testing_ + kMaskingKeyLength,
182 masking_key_);
183 } else {
184 // Masking keys should be generated from a cryptographically secure random
185 // number generator, which means web application authors should not be able
186 // to guess the next value of masking key.
187 base::RandBytes(masking_key_, kMaskingKeyLength);
188 }
189 }
190
191 void WebSocketFrameBuilder::MaskPayload(std::vector<char>* output) {
192 static const size_t kMaskingKeyLength =
193 WebSocketFrameHeader::kMaskingKeyLength;
194
195 // TODO(yutak): Make masking more efficient by XOR'ing every machine word
196 // (4 or 8 bytes), instead of XOR'ing every byte.
197 uint64 masking_key_offset = frame_offset_ % kMaskingKeyLength;
198 for (std::vector<char>::iterator iter = output->begin();
199 iter != output->end(); ++iter) {
200 *iter ^= masking_key_[masking_key_offset++];
201 if (masking_key_offset == kMaskingKeyLength)
202 masking_key_offset = 0;
203 }
204 }
205
206 void WebSocketFrameBuilder::Fail() {
207 current_frame_header_.reset();
208 frame_offset_ = 0;
209 failed_ = true;
210 }
211
212 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698