Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 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_basic_stream.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <limits> | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/basictypes.h" | |
| 13 #include "base/bind.h" | |
| 14 #include "base/logging.h" | |
| 15 #include "net/base/io_buffer.h" | |
| 16 #include "net/base/net_errors.h" | |
| 17 #include "net/socket/client_socket_handle.h" | |
| 18 #include "net/websockets/websocket_errors.h" | |
| 19 #include "net/websockets/websocket_frame.h" | |
| 20 #include "net/websockets/websocket_frame_parser.h" | |
| 21 | |
| 22 namespace net { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // The number of bytes to attempt to read at a time. | |
| 27 // TODO(ricea): See if there is a better number. Should it start small, and get | |
| 28 // bigger if needed? | |
| 29 const int kReadAtATime = 32 * 1024; | |
|
szym
2013/08/27 22:54:04
suggest: kReadBufferSize
This is merely the size
Adam Rice
2013/08/28 12:01:11
Some applications of WebSockets may require a larg
| |
| 30 | |
| 31 } // namespace | |
| 32 | |
| 33 WebSocketBasicStream::WebSocketBasicStream( | |
| 34 scoped_ptr<ClientSocketHandle> connection) | |
| 35 : read_buffer_(new IOBufferWithSize(kReadAtATime)), | |
| 36 connection_(connection.Pass()), | |
| 37 generate_websocket_masking_key_(&GenerateWebSocketMaskingKey) {} | |
|
szym
2013/08/27 22:54:04
suggest: DCHECK(connection_->is_initialized())
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 38 | |
| 39 WebSocketBasicStream::~WebSocketBasicStream() { | |
| 40 connection_->socket()->Disconnect(); | |
|
szym
2013/08/27 22:54:04
Suggest calling Close() instead.
| |
| 41 } | |
| 42 | |
| 43 int WebSocketBasicStream::ReadFrames( | |
| 44 ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
| 45 const CompletionCallback& callback) { | |
| 46 DCHECK(frame_chunks->empty()); | |
| 47 // If there is data left over after parsing the HTTP headers, attempt to parse | |
| 48 // it as WebSocket frames. | |
| 49 if (http_read_buffer_) { | |
| 50 DCHECK_GE(http_read_buffer_->offset(), 0); | |
| 51 if (!parser_.Decode(http_read_buffer_->StartOfBuffer(), | |
| 52 http_read_buffer_->offset(), | |
| 53 frame_chunks)) { | |
| 54 http_read_buffer_ = NULL; | |
| 55 return WebSocketErrorToNetError(parser_.websocket_error()); | |
| 56 } | |
| 57 http_read_buffer_ = NULL; | |
| 58 } | |
| 59 // Loop until we either have at least one chunk to return, or we get | |
| 60 // ERR_IO_PENDING, or something goes wrong. | |
| 61 while (frame_chunks->empty()) { | |
| 62 // This use of base::Unretained() is safe because WebSocketChannel will | |
| 63 // delete us before deleting frame_chunks. | |
|
szym
2013/08/27 22:54:04
Do not refer to WebSocketChannel. This use is safe
Adam Rice
2013/08/28 12:01:11
I plan to rename base::Unretained to base::Critica
| |
| 64 int result = | |
| 65 connection_->socket()->Read(read_buffer_.get(), | |
| 66 read_buffer_->size(), | |
| 67 base::Bind(&WebSocketBasicStream::ReadDone, | |
| 68 base::Unretained(this), | |
| 69 base::Unretained(frame_chunks), | |
| 70 callback)); | |
| 71 if (result > 0) { | |
| 72 if (!parser_.Decode(read_buffer_->data(), result, frame_chunks)) { | |
| 73 return WebSocketErrorToNetError(parser_.websocket_error()); | |
| 74 } | |
| 75 } else if (result == 0 && frame_chunks->empty()) { | |
| 76 return ERR_CONNECTION_CLOSED; | |
| 77 } else { | |
| 78 return result; | |
| 79 } | |
| 80 } | |
| 81 return OK; | |
| 82 } | |
| 83 | |
| 84 void WebSocketBasicStream::ReadDone( | |
|
szym
2013/08/27 22:54:04
Chromium code style: Function declaration order sh
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 85 ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
| 86 const CompletionCallback& callback, | |
| 87 int result) { | |
| 88 if (result > 0) { | |
|
szym
2013/08/27 22:54:04
The logic here and in ReadFrames is very similar b
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 89 if (parser_.Decode(read_buffer_->data(), result, frame_chunks)) { | |
| 90 if (!frame_chunks->empty()) { | |
| 91 result = OK; | |
| 92 } else { | |
| 93 result = ReadFrames(frame_chunks, callback); | |
| 94 if (result == ERR_IO_PENDING) { | |
| 95 // This method will be called back again. | |
| 96 return; | |
| 97 } | |
| 98 } | |
| 99 } else { | |
| 100 result = WebSocketErrorToNetError(parser_.websocket_error()); | |
| 101 } | |
| 102 } | |
| 103 if (result == 0 && frame_chunks->empty()) { | |
| 104 result = ERR_CONNECTION_CLOSED; | |
| 105 } | |
| 106 DCHECK_NE(ERR_IO_PENDING, result); | |
| 107 callback.Run(result); | |
| 108 } | |
| 109 | |
| 110 int WebSocketBasicStream::WriteFrames( | |
| 111 ScopedVector<WebSocketFrameChunk>* frame_chunks, | |
| 112 const CompletionCallback& callback) { | |
| 113 // This function always concatenates all frames into a single buffer. | |
| 114 // TODO(ricea): Investigate whether it would be better in some cases to | |
| 115 // perform multiple writes with smaller buffers. | |
| 116 // | |
| 117 // First calculate the size of the buffer we need to allocate. | |
| 118 typedef ScopedVector<WebSocketFrameChunk>::const_iterator Iterator; | |
| 119 int total_size = 0; | |
| 120 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) { | |
| 121 WebSocketFrameChunk* chunk = *it; | |
| 122 DCHECK(chunk->header && chunk->final_chunk) | |
|
szym
2013/08/27 22:54:04
Suggest breaking DCHECK(..&&..) into two DCHECKs
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 123 << "Only complete frames are supported by WebSocketBasicStream"; | |
| 124 // Force the masked bit on. | |
| 125 chunk->header->masked = true; | |
| 126 // We enforce flow control so the renderer should never be able to force us | |
| 127 // to cache anywhere near 2GB of frames. | |
| 128 int chunk_size = | |
| 129 chunk->data->size() + GetWebSocketFrameHeaderSize(*(chunk->header)); | |
| 130 CHECK_GE(std::numeric_limits<int>::max() - total_size, chunk_size) | |
|
szym
2013/08/27 22:54:04
Define a constant
const int kMaximumTotalSize = .
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 131 << "Aborting to prevent overflow"; | |
| 132 total_size += chunk_size; | |
| 133 } | |
| 134 scoped_refptr<IOBufferWithSize> combined_buffer( | |
| 135 new IOBufferWithSize(total_size)); | |
| 136 char* dest = combined_buffer->data(); | |
| 137 int remaining_size = total_size; | |
| 138 for (Iterator it = frame_chunks->begin(); it != frame_chunks->end(); ++it) { | |
| 139 WebSocketFrameChunk* chunk = *it; | |
| 140 WebSocketMaskingKey mask = generate_websocket_masking_key_(); | |
| 141 int result = WriteWebSocketFrameHeader( | |
| 142 *(chunk->header), &mask, dest, remaining_size); | |
| 143 DCHECK(result != ERR_INVALID_ARGUMENT) | |
| 144 << "WriteWebSocketFrameHeader() says that " << remaining_size | |
| 145 << " is not enough to write the header in. This should not happen."; | |
| 146 CHECK_GE(result, 0) << "Potentially security-critical check failed"; | |
| 147 dest += result; | |
| 148 remaining_size -= result; | |
| 149 | |
| 150 const char* const frame_data = chunk->data->data(); | |
| 151 const int frame_size = chunk->data->size(); | |
| 152 CHECK_GE(remaining_size, frame_size); | |
| 153 std::copy(frame_data, frame_data + frame_size, dest); | |
| 154 MaskWebSocketFramePayload(mask, 0, dest, frame_size); | |
| 155 dest += frame_size; | |
| 156 remaining_size -= frame_size; | |
| 157 } | |
| 158 DCHECK_EQ(0, remaining_size) << "Buffer size calculation was wrong; " | |
| 159 << remaining_size << " bytes left over."; | |
| 160 scoped_refptr<DrainableIOBuffer> drainable_buffer( | |
| 161 new DrainableIOBuffer(combined_buffer, total_size)); | |
| 162 return WriteEverything(drainable_buffer, callback); | |
| 163 } | |
| 164 | |
| 165 int WebSocketBasicStream::WriteEverything( | |
| 166 const scoped_refptr<DrainableIOBuffer>& buffer, | |
| 167 const CompletionCallback& callback) { | |
| 168 while (buffer->BytesRemaining() > 0) { | |
| 169 // The use of base::Unretained() here is safe because on destruction we | |
| 170 // disconnect the socket, preventing any further callbacks. | |
| 171 int result = connection_->socket()->Write( | |
| 172 buffer.get(), | |
| 173 buffer->BytesRemaining(), | |
| 174 base::Bind(&WebSocketBasicStream::WriteDone, | |
| 175 base::Unretained(this), | |
| 176 buffer, | |
| 177 callback)); | |
| 178 if (result > 0) { | |
| 179 buffer->DidConsume(result); | |
| 180 } else { | |
| 181 return result; | |
| 182 } | |
| 183 } | |
| 184 return OK; | |
| 185 } | |
| 186 | |
| 187 void WebSocketBasicStream::WriteDone( | |
| 188 const scoped_refptr<DrainableIOBuffer>& buffer, | |
| 189 const CompletionCallback& callback, | |
| 190 int result) { | |
| 191 if (result > 0) { | |
|
szym
2013/08/27 22:54:04
Suggest unrolling the if{} folds in this function:
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 192 buffer->DidConsume(result); | |
| 193 if (buffer->BytesRemaining() > 0) { | |
|
szym
2013/08/27 22:54:04
You don't need this check. Just call WriteEverythi
Adam Rice
2013/08/28 12:01:11
Done.
| |
| 194 int result = WriteEverything(buffer, callback); | |
| 195 if (result != ERR_IO_PENDING) { | |
| 196 callback.Run(result); | |
| 197 } | |
| 198 } else { | |
| 199 callback.Run(OK); | |
| 200 } | |
| 201 } else { | |
| 202 DCHECK(result != ERR_IO_PENDING); | |
| 203 callback.Run(result); | |
|
szym
2013/08/27 22:54:04
BUG: It's possible to end up in here with result =
Adam Rice
2013/08/28 12:01:11
I've put in a DCHECK for result != 0 for the time
| |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void WebSocketBasicStream::Close() { connection_->socket()->Disconnect(); } | |
| 208 | |
| 209 std::string WebSocketBasicStream::GetSubProtocol() const { | |
| 210 return sub_protocol_; | |
| 211 } | |
| 212 | |
| 213 std::string WebSocketBasicStream::GetExtensions() const { return extensions_; } | |
| 214 | |
| 215 int WebSocketBasicStream::SendHandshakeRequest( | |
| 216 const GURL& url, | |
| 217 const HttpRequestHeaders& headers, | |
| 218 HttpResponseInfo* response_info, | |
| 219 const CompletionCallback& callback) { | |
| 220 // TODO(ricea): Implement handshake-related functionality. | |
| 221 NOTREACHED(); | |
| 222 return ERR_NOT_IMPLEMENTED; | |
| 223 } | |
| 224 | |
| 225 int WebSocketBasicStream::ReadHandshakeResponse( | |
| 226 const CompletionCallback& callback) { | |
| 227 NOTREACHED(); | |
| 228 return ERR_NOT_IMPLEMENTED; | |
| 229 } | |
| 230 | |
| 231 /*static*/ | |
| 232 scoped_ptr<WebSocketBasicStream> | |
| 233 WebSocketBasicStream::CreateWebSocketBasicStreamForTesting( | |
| 234 scoped_ptr<ClientSocketHandle> connection, | |
| 235 const scoped_refptr<GrowableIOBuffer>& http_read_buffer, | |
| 236 const std::string& sub_protocol, | |
| 237 const std::string& extensions, | |
| 238 WebSocketMaskingKeyGeneratorFunction key_generator_function) { | |
| 239 scoped_ptr<WebSocketBasicStream> stream( | |
| 240 new WebSocketBasicStream(connection.Pass())); | |
| 241 if (http_read_buffer) { | |
| 242 stream->http_read_buffer_ = http_read_buffer; | |
| 243 } | |
| 244 stream->sub_protocol_ = sub_protocol; | |
| 245 stream->extensions_ = extensions; | |
| 246 stream->generate_websocket_masking_key_ = key_generator_function; | |
| 247 return stream.Pass(); | |
| 248 } | |
| 249 | |
| 250 } // namespace net | |
| OLD | NEW |