diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-02-02 12:21:57 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-02-12 08:13:00 +0000 |
commit | 606d85f2a5386472314d39923da28c70c60dc8e7 (patch) | |
tree | a8f4d7bf997f349f45605e6058259fba0630e4d7 /chromium/net/server | |
parent | 5786336dda477d04fb98483dca1a5426eebde2d7 (diff) | |
download | qtwebengine-chromium-606d85f2a5386472314d39923da28c70c60dc8e7.tar.gz |
BASELINE: Update Chromium to 96.0.4664.181
Change-Id: I762cd1da89d73aa6313b4a753fe126c34833f046
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/server')
-rw-r--r-- | chromium/net/server/BUILD.gn | 2 | ||||
-rw-r--r-- | chromium/net/server/http_connection.h | 16 | ||||
-rw-r--r-- | chromium/net/server/http_server.cc | 6 | ||||
-rw-r--r-- | chromium/net/server/http_server.h | 6 | ||||
-rw-r--r-- | chromium/net/server/http_server_fuzzer.cc | 6 | ||||
-rw-r--r-- | chromium/net/server/http_server_unittest.cc | 162 | ||||
-rw-r--r-- | chromium/net/server/web_socket.cc | 28 | ||||
-rw-r--r-- | chromium/net/server/web_socket.h | 16 | ||||
-rw-r--r-- | chromium/net/server/web_socket_encoder.cc | 83 | ||||
-rw-r--r-- | chromium/net/server/web_socket_encoder.h | 18 | ||||
-rw-r--r-- | chromium/net/server/web_socket_encoder_fuzzer.cc | 23 | ||||
-rw-r--r-- | chromium/net/server/web_socket_encoder_unittest.cc | 302 |
12 files changed, 598 insertions, 70 deletions
diff --git a/chromium/net/server/BUILD.gn b/chromium/net/server/BUILD.gn index 8ee35c5270f..0a439b5ea1f 100644 --- a/chromium/net/server/BUILD.gn +++ b/chromium/net/server/BUILD.gn @@ -16,10 +16,12 @@ static_library("http_server") { "//chrome/browser/devtools", "//chrome/test/chromedriver/*", "//content/browser", + "//net:net_web_socket_encoder_fuzzer", ] friend = [ ":net_http_server_fuzzer", + "//net:net_web_socket_encoder_fuzzer", ":tests", "//chrome/browser/devtools", "//chrome/test/chromedriver/*", diff --git a/chromium/net/server/http_connection.h b/chromium/net/server/http_connection.h index e926493b5e6..e16b3826fd8 100644 --- a/chromium/net/server/http_connection.h +++ b/chromium/net/server/http_connection.h @@ -34,6 +34,9 @@ class HttpConnection { ReadIOBuffer(); + ReadIOBuffer(const ReadIOBuffer&) = delete; + ReadIOBuffer& operator=(const ReadIOBuffer&) = delete; + // Capacity. int GetCapacity() const; void SetCapacity(int capacity); @@ -63,8 +66,6 @@ class HttpConnection { scoped_refptr<GrowableIOBuffer> base_; int max_buffer_size_; - - DISALLOW_COPY_AND_ASSIGN(ReadIOBuffer); }; // IOBuffer of pending data to write which has a queue of pending data. Each @@ -76,6 +77,9 @@ class HttpConnection { QueuedWriteIOBuffer(); + QueuedWriteIOBuffer(const QueuedWriteIOBuffer&) = delete; + QueuedWriteIOBuffer& operator=(const QueuedWriteIOBuffer&) = delete; + // Whether or not pending data exists. bool IsEmpty() const; @@ -108,11 +112,13 @@ class HttpConnection { base::queue<std::unique_ptr<std::string>> pending_data_; int total_size_; int max_buffer_size_; - - DISALLOW_COPY_AND_ASSIGN(QueuedWriteIOBuffer); }; HttpConnection(int id, std::unique_ptr<StreamSocket> socket); + + HttpConnection(const HttpConnection&) = delete; + HttpConnection& operator=(const HttpConnection&) = delete; + ~HttpConnection(); int id() const { return id_; } @@ -130,8 +136,6 @@ class HttpConnection { const scoped_refptr<QueuedWriteIOBuffer> write_buf_; std::unique_ptr<WebSocket> web_socket_; - - DISALLOW_COPY_AND_ASSIGN(HttpConnection); }; } // namespace net diff --git a/chromium/net/server/http_server.cc b/chromium/net/server/http_server.cc index e9bd64bce7f..fdfc5690d49 100644 --- a/chromium/net/server/http_server.cc +++ b/chromium/net/server/http_server.cc @@ -86,7 +86,8 @@ void HttpServer::SendOverWebSocket( if (connection == nullptr) return; DCHECK(connection->web_socket()); - connection->web_socket()->Send(data, traffic_annotation); + connection->web_socket()->Send( + data, WebSocketFrameHeader::OpCodeEnum::kOpCodeText, traffic_annotation); } void HttpServer::SendRaw(int connection_id, @@ -255,7 +256,8 @@ int HttpServer::HandleReadResult(HttpConnection* connection, int rv) { Close(connection->id()); return ERR_CONNECTION_CLOSED; } - delegate_->OnWebSocketMessage(connection->id(), std::move(message)); + if (result == WebSocket::FRAME_OK_FINAL) + delegate_->OnWebSocketMessage(connection->id(), std::move(message)); if (HasClosedConnection(connection)) return ERR_CONNECTION_CLOSED; continue; diff --git a/chromium/net/server/http_server.h b/chromium/net/server/http_server.h index cb70b575731..f866422350f 100644 --- a/chromium/net/server/http_server.h +++ b/chromium/net/server/http_server.h @@ -50,6 +50,10 @@ class HttpServer { // callbacks yet. HttpServer(std::unique_ptr<ServerSocket> server_socket, HttpServer::Delegate* delegate); + + HttpServer(const HttpServer&) = delete; + HttpServer& operator=(const HttpServer&) = delete; + ~HttpServer(); void AcceptWebSocket(int connection_id, @@ -132,8 +136,6 @@ class HttpServer { std::map<int, std::unique_ptr<HttpConnection>> id_to_connection_; base::WeakPtrFactory<HttpServer> weak_ptr_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(HttpServer); }; } // namespace net diff --git a/chromium/net/server/http_server_fuzzer.cc b/chromium/net/server/http_server_fuzzer.cc index 2f16f03f614..5ebebd80014 100644 --- a/chromium/net/server/http_server_fuzzer.cc +++ b/chromium/net/server/http_server_fuzzer.cc @@ -24,6 +24,10 @@ class WaitTillHttpCloseDelegate : public net::HttpServer::Delegate { done_closure_(std::move(done_closure)), action_flags_(data_provider_->ConsumeIntegral<uint8_t>()) {} + WaitTillHttpCloseDelegate(const WaitTillHttpCloseDelegate&) = delete; + WaitTillHttpCloseDelegate& operator=(const WaitTillHttpCloseDelegate&) = + delete; + void set_server(net::HttpServer* server) { server_ = server; } void OnConnect(int connection_id) override { @@ -89,8 +93,6 @@ class WaitTillHttpCloseDelegate : public net::HttpServer::Delegate { FuzzedDataProvider* const data_provider_; base::OnceClosure done_closure_; const uint8_t action_flags_; - - DISALLOW_COPY_AND_ASSIGN(WaitTillHttpCloseDelegate); }; } // namespace diff --git a/chromium/net/server/http_server_unittest.cc b/chromium/net/server/http_server_unittest.cc index b32ba77c2e2..b57c5cd21c6 100644 --- a/chromium/net/server/http_server_unittest.cc +++ b/chromium/net/server/http_server_unittest.cc @@ -46,6 +46,7 @@ #include "net/test/gtest_util.h" #include "net/test/test_with_task_environment.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/websockets/websocket_frame.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -311,6 +312,55 @@ class WebSocketTest : public HttpServerTest { void OnWebSocketMessage(int connection_id, std::string data) override {} }; +class WebSocketAcceptingTest : public WebSocketTest { + public: + void OnWebSocketRequest(int connection_id, + const HttpServerRequestInfo& info) override { + HttpServerTest::OnHttpRequest(connection_id, info); + server_->AcceptWebSocket(connection_id, info, TRAFFIC_ANNOTATION_FOR_TESTS); + } + + void OnWebSocketMessage(int connection_id, std::string data) override { + message_ = data; + if (message_.length() > 0 && run_loop_) { + run_loop_->Quit(); + } + } + + std::string GetMessage() { + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); + return message_; + } + + private: + std::string message_; + std::unique_ptr<base::RunLoop> run_loop_; +}; + +std::string EncodeFrame(std::string message, + WebSocketFrameHeader::OpCodeEnum op_code, + bool mask, + bool finish) { + WebSocketFrameHeader header(op_code); + header.final = finish; + header.masked = mask; + header.payload_length = message.size(); + const int header_size = GetWebSocketFrameHeaderSize(header); + std::string frame_header; + frame_header.resize(header_size); + if (mask) { + WebSocketMaskingKey masking_key = GenerateWebSocketMaskingKey(); + WriteWebSocketFrameHeader(header, &masking_key, &frame_header[0], + header_size); + MaskWebSocketFramePayload(masking_key, 0, &message[0], message.length()); + } else { + WriteWebSocketFrameHeader(header, nullptr, &frame_header[0], header_size); + } + return frame_header + message; +} + TEST_F(HttpServerTest, Request) { TestHttpClient client; CreateConnection(&client); @@ -485,6 +535,113 @@ TEST_F(WebSocketTest, RequestWebSocketTrailingJunk) { client.ExpectUsedThenDisconnectedWithNoData(); } +TEST_F(WebSocketAcceptingTest, SendPingFrameWithNoMessage) { + TestHttpClient client; + CreateConnection(&client); + std::string response; + client.Send( + "GET /test HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: SomethingElse, Upgrade\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: key\r\n\r\n"); + RunUntilRequestsReceived(1); + ASSERT_TRUE(client.ReadResponse(&response)); + const std::string message = ""; + const std::string ping_frame = + EncodeFrame(message, WebSocketFrameHeader::OpCodeEnum::kOpCodePing, + /* mask= */ true, /* finish= */ true); + const std::string pong_frame = + EncodeFrame(message, WebSocketFrameHeader::OpCodeEnum::kOpCodePong, + /* mask= */ false, /* finish= */ true); + client.Send(ping_frame); + ASSERT_TRUE(client.Read(&response, pong_frame.length())); + EXPECT_EQ(response, pong_frame); +} + +TEST_F(WebSocketAcceptingTest, SendPingFrameWithMessage) { + TestHttpClient client; + CreateConnection(&client); + std::string response; + client.Send( + "GET /test HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: SomethingElse, Upgrade\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: key\r\n\r\n"); + RunUntilRequestsReceived(1); + ASSERT_TRUE(client.ReadResponse(&response)); + const std::string message = "hello"; + const std::string ping_frame = + EncodeFrame(message, WebSocketFrameHeader::OpCodeEnum::kOpCodePing, + /* mask= */ true, /* finish= */ true); + const std::string pong_frame = + EncodeFrame(message, WebSocketFrameHeader::OpCodeEnum::kOpCodePong, + /* mask= */ false, /* finish= */ true); + client.Send(ping_frame); + ASSERT_TRUE(client.Read(&response, pong_frame.length())); + EXPECT_EQ(response, pong_frame); +} + +TEST_F(WebSocketAcceptingTest, SendPongFrame) { + TestHttpClient client; + CreateConnection(&client); + std::string response; + client.Send( + "GET /test HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: SomethingElse, Upgrade\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: key\r\n\r\n"); + RunUntilRequestsReceived(1); + ASSERT_TRUE(client.ReadResponse(&response)); + const std::string ping_frame = EncodeFrame( + /* message= */ "", WebSocketFrameHeader::OpCodeEnum::kOpCodePing, + /* mask= */ true, /* finish= */ true); + const std::string pong_frame_send = EncodeFrame( + /* message= */ "", WebSocketFrameHeader::OpCodeEnum::kOpCodePong, + /* mask= */ true, /* finish= */ true); + const std::string pong_frame_receive = EncodeFrame( + /* message= */ "", WebSocketFrameHeader::OpCodeEnum::kOpCodePong, + /* mask= */ false, /* finish= */ true); + client.Send(pong_frame_send); + client.Send(ping_frame); + ASSERT_TRUE(client.Read(&response, pong_frame_receive.length())); + EXPECT_EQ(response, pong_frame_receive); +} + +TEST_F(WebSocketAcceptingTest, SendLongTextFrame) { + TestHttpClient client; + CreateConnection(&client); + std::string response; + client.Send( + "GET /test HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n" + "Connection: SomethingElse, Upgrade\r\n" + "Sec-WebSocket-Version: 8\r\n" + "Sec-WebSocket-Key: key\r\n\r\n"); + RunUntilRequestsReceived(1); + ASSERT_TRUE(client.ReadResponse(&response)); + constexpr int kMessageSize = 100000; + const std::string text_message(kMessageSize, 'a'); + const std::string continuation_message(kMessageSize, 'b'); + const std::string text_frame = + EncodeFrame(text_message, WebSocketFrameHeader::OpCodeEnum::kOpCodeText, + /* mask= */ true, + /* finish= */ false); + const std::string continuation_frame = + EncodeFrame(continuation_message, + WebSocketFrameHeader::OpCodeEnum::kOpCodeContinuation, + /* mask= */ true, /* finish= */ true); + client.Send(text_frame); + client.Send(continuation_frame); + std::string received_message = GetMessage(); + EXPECT_EQ( + static_cast<int>(received_message.length()), + static_cast<int>(text_message.length() + continuation_message.length())); + EXPECT_EQ(received_message, text_message + continuation_message); +} + TEST_F(HttpServerTest, RequestWithTooLargeBody) { TestHttpClient client; CreateConnection(&client); @@ -563,6 +720,9 @@ class MockStreamSocket : public StreamSocket { public: MockStreamSocket() : connected_(true), read_buf_(nullptr), read_buf_len_(0) {} + MockStreamSocket(const MockStreamSocket&) = delete; + MockStreamSocket& operator=(const MockStreamSocket&) = delete; + // StreamSocket int Connect(CompletionOnceCallback callback) override { return ERR_NOT_IMPLEMENTED; @@ -653,8 +813,6 @@ class MockStreamSocket : public StreamSocket { CompletionOnceCallback read_callback_; std::string pending_read_data_; NetLogWithSource net_log_; - - DISALLOW_COPY_AND_ASSIGN(MockStreamSocket); }; TEST_F(HttpServerTest, RequestWithBodySplitAcrossPackets) { diff --git a/chromium/net/server/web_socket.cc b/chromium/net/server/web_socket.cc index 89f4be2d1d2..60462221d80 100644 --- a/chromium/net/server/web_socket.cc +++ b/chromium/net/server/web_socket.cc @@ -96,6 +96,8 @@ void WebSocket::Accept(const HttpServerRequestInfo& request, server_->SendRaw(connection_->id(), ValidResponseString(encoded_hash, response_extensions), traffic_annotation); + traffic_annotation_ = std::make_unique<NetworkTrafficAnnotationTag>( + NetworkTrafficAnnotationTag(traffic_annotation)); } WebSocket::ParseResult WebSocket::Read(std::string* message) { @@ -113,23 +115,41 @@ WebSocket::ParseResult WebSocket::Read(std::string* message) { return FRAME_ERROR; } + ParseResult result = FRAME_OK_MIDDLE; HttpConnection::ReadIOBuffer* read_buf = connection_->read_buf(); base::StringPiece frame(read_buf->StartOfBuffer(), read_buf->GetSize()); int bytes_consumed = 0; - ParseResult result = encoder_->DecodeFrame(frame, &bytes_consumed, message); - if (result == FRAME_OK) - read_buf->DidConsume(bytes_consumed); + result = encoder_->DecodeFrame(frame, &bytes_consumed, message); + read_buf->DidConsume(bytes_consumed); if (result == FRAME_CLOSE) closed_ = true; + if (result == FRAME_PING) { + if (!traffic_annotation_) + return FRAME_ERROR; + Send(*message, WebSocketFrameHeader::kOpCodePong, *traffic_annotation_); + } return result; } void WebSocket::Send(base::StringPiece message, + WebSocketFrameHeader::OpCodeEnum op_code, const NetworkTrafficAnnotationTag traffic_annotation) { if (closed_) return; std::string encoded; - encoder_->EncodeFrame(message, 0, &encoded); + switch (op_code) { + case WebSocketFrameHeader::kOpCodeText: + encoder_->EncodeTextFrame(message, 0, &encoded); + break; + + case WebSocketFrameHeader::kOpCodePong: + encoder_->EncodePongFrame(message, 0, &encoded); + break; + + default: + // Only Pong and Text frame types are supported. + NOTREACHED(); + } server_->SendRaw(connection_->id(), encoded, traffic_annotation); } diff --git a/chromium/net/server/web_socket.h b/chromium/net/server/web_socket.h index 6752b740291..d5c91e06413 100644 --- a/chromium/net/server/web_socket.h +++ b/chromium/net/server/web_socket.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/strings/string_piece.h" #include "net/traffic_annotation/network_traffic_annotation.h" +#include "net/websockets/websocket_frame.h" namespace net { @@ -22,7 +23,12 @@ class WebSocketEncoder; class WebSocket final { public: enum ParseResult { - FRAME_OK, + // Final frame of a text message or compressed frame. + FRAME_OK_FINAL, + // Other frame of a text message. + FRAME_OK_MIDDLE, + FRAME_PING, + FRAME_PONG, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR @@ -34,7 +40,12 @@ class WebSocket final { const NetworkTrafficAnnotationTag traffic_annotation); ParseResult Read(std::string* message); void Send(base::StringPiece message, + WebSocketFrameHeader::OpCodeEnum op_code, const NetworkTrafficAnnotationTag traffic_annotation); + + WebSocket(const WebSocket&) = delete; + WebSocket& operator=(const WebSocket&) = delete; + ~WebSocket(); private: @@ -46,8 +57,7 @@ class WebSocket final { HttpConnection* const connection_; std::unique_ptr<WebSocketEncoder> encoder_; bool closed_; - - DISALLOW_COPY_AND_ASSIGN(WebSocket); + std::unique_ptr<NetworkTrafficAnnotationTag> traffic_annotation_ = nullptr; }; } // namespace net diff --git a/chromium/net/server/web_socket_encoder.cc b/chromium/net/server/web_socket_encoder.cc index 760ff6b9ebd..7e5e652786b 100644 --- a/chromium/net/server/web_socket_encoder.cc +++ b/chromium/net/server/web_socket_encoder.cc @@ -6,15 +6,16 @@ #include <limits> #include <utility> -#include <vector> #include "base/check.h" #include "base/memory/ptr_util.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "net/base/io_buffer.h" #include "net/websockets/websocket_deflate_parameters.h" #include "net/websockets/websocket_extension.h" #include "net/websockets/websocket_extension_parser.h" +#include "net/websockets/websocket_frame.h" namespace net { @@ -27,15 +28,6 @@ const int kInflaterChunkSize = 16 * 1024; // Constants for hybi-10 frame format. -typedef int OpCode; - -const OpCode kOpCodeContinuation = 0x0; -const OpCode kOpCodeText = 0x1; -const OpCode kOpCodeBinary = 0x2; -const OpCode kOpCodeClose = 0x8; -const OpCode kOpCodePing = 0x9; -const OpCode kOpCodePong = 0xA; - const unsigned char kFinalBit = 0x80; const unsigned char kReserved1Bit = 0x40; const unsigned char kReserved2Bit = 0x20; @@ -72,20 +64,24 @@ WebSocket::ParseResult DecodeFrameHybi17(const base::StringPiece& frame, int op_code = first_byte & kOpCodeMask; bool masked = (second_byte & kMaskBit) != 0; *compressed = reserved1; - if (!final || reserved2 || reserved3) + if (reserved2 || reserved3) return WebSocket::FRAME_ERROR; // Only compression extension is supported. bool closed = false; switch (op_code) { - case kOpCodeClose: + case WebSocketFrameHeader::OpCodeEnum::kOpCodeClose: closed = true; break; - case kOpCodeText: + + case WebSocketFrameHeader::OpCodeEnum::kOpCodeText: + case WebSocketFrameHeader::OpCodeEnum:: + kOpCodeContinuation: // Treated in the same as kOpCodeText. + case WebSocketFrameHeader::OpCodeEnum::kOpCodePing: + case WebSocketFrameHeader::OpCodeEnum::kOpCodePong: break; - case kOpCodeBinary: // We don't support binary frames yet. - case kOpCodeContinuation: // We don't support binary frames yet. - case kOpCodePing: // We don't support binary frames yet. - case kOpCodePong: // We don't support binary frames yet. + + case WebSocketFrameHeader::OpCodeEnum::kOpCodeBinary: // We don't support + // binary frames yet. default: return WebSocket::FRAME_ERROR; } @@ -137,15 +133,25 @@ WebSocket::ParseResult DecodeFrameHybi17(const base::StringPiece& frame, size_t pos = p + actual_masking_key_length + payload_length - buffer_begin; *bytes_consumed = pos; - return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK; + + if (op_code == WebSocketFrameHeader::OpCodeEnum::kOpCodePing) + return WebSocket::FRAME_PING; + + if (op_code == WebSocketFrameHeader::OpCodeEnum::kOpCodePong) + return WebSocket::FRAME_PONG; + + if (closed) + return WebSocket::FRAME_CLOSE; + + return final ? WebSocket::FRAME_OK_FINAL : WebSocket::FRAME_OK_MIDDLE; } void EncodeFrameHybi17(base::StringPiece message, int masking_key, bool compressed, + WebSocketFrameHeader::OpCodeEnum op_code, std::string* output) { std::vector<char> frame; - OpCode op_code = kOpCodeText; size_t data_length = message.length(); int reserved1 = compressed ? kReserved1Bit : 0; @@ -292,23 +298,44 @@ WebSocket::ParseResult WebSocketEncoder::DecodeFrame( int* bytes_consumed, std::string* output) { bool compressed; + std::string current_output; WebSocket::ParseResult result = DecodeFrameHybi17( - frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed); - if (result == WebSocket::FRAME_OK && compressed) { - if (!Inflate(output)) - result = WebSocket::FRAME_ERROR; + frame, type_ == FOR_SERVER, bytes_consumed, ¤t_output, &compressed); + if (result == WebSocket::FRAME_OK_FINAL || + result == WebSocket::FRAME_OK_MIDDLE || result == WebSocket::FRAME_PING) { + if (continuation_message_frames_.empty()) + is_current_message_compressed_ = compressed; + continuation_message_frames_.push_back(current_output); + } + if (result == WebSocket::FRAME_OK_FINAL || result == WebSocket::FRAME_PING) { + *output = base::StrCat(continuation_message_frames_); + if (is_current_message_compressed_) { + if (!Inflate(output)) + result = WebSocket::FRAME_ERROR; + } } + if (result != WebSocket::FRAME_OK_MIDDLE && + result != WebSocket::FRAME_INCOMPLETE) + continuation_message_frames_.clear(); return result; } -void WebSocketEncoder::EncodeFrame(base::StringPiece frame, - int masking_key, - std::string* output) { +void WebSocketEncoder::EncodeTextFrame(base::StringPiece frame, + int masking_key, + std::string* output) { std::string compressed; + constexpr auto op_code = WebSocketFrameHeader::OpCodeEnum::kOpCodeText; if (Deflate(frame, &compressed)) - EncodeFrameHybi17(compressed, masking_key, true, output); + EncodeFrameHybi17(compressed, masking_key, true, op_code, output); else - EncodeFrameHybi17(frame, masking_key, false, output); + EncodeFrameHybi17(frame, masking_key, false, op_code, output); +} + +void WebSocketEncoder::EncodePongFrame(base::StringPiece frame, + int masking_key, + std::string* output) { + constexpr auto op_code = WebSocketFrameHeader::OpCodeEnum::kOpCodePong; + EncodeFrameHybi17(frame, masking_key, false, op_code, output); } bool WebSocketEncoder::Inflate(std::string* message) { diff --git a/chromium/net/server/web_socket_encoder.h b/chromium/net/server/web_socket_encoder.h index de6bea7b550..3f18f00053b 100644 --- a/chromium/net/server/web_socket_encoder.h +++ b/chromium/net/server/web_socket_encoder.h @@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "base/macros.h" #include "base/strings/string_piece.h" @@ -22,6 +23,9 @@ class WebSocketEncoder final { public: static const char kClientExtensions[]; + WebSocketEncoder(const WebSocketEncoder&) = delete; + WebSocketEncoder& operator=(const WebSocketEncoder&) = delete; + ~WebSocketEncoder(); // Creates and returns an encoder for a server without extensions. @@ -38,9 +42,12 @@ class WebSocketEncoder final { WebSocket::ParseResult DecodeFrame(const base::StringPiece& frame, int* bytes_consumed, std::string* output); - void EncodeFrame(base::StringPiece frame, - int masking_key, - std::string* output); + void EncodeTextFrame(base::StringPiece frame, + int masking_key, + std::string* output); + void EncodePongFrame(base::StringPiece frame, + int masking_key, + std::string* output); bool deflate_enabled() const { return !!deflater_; } @@ -54,14 +61,15 @@ class WebSocketEncoder final { std::unique_ptr<WebSocketDeflater> deflater, std::unique_ptr<WebSocketInflater> inflater); + std::vector<std::string> continuation_message_frames_; + bool is_current_message_compressed_ = false; + bool Inflate(std::string* message); bool Deflate(base::StringPiece message, std::string* output); Type type_; std::unique_ptr<WebSocketDeflater> deflater_; std::unique_ptr<WebSocketInflater> inflater_; - - DISALLOW_COPY_AND_ASSIGN(WebSocketEncoder); }; } // namespace net diff --git a/chromium/net/server/web_socket_encoder_fuzzer.cc b/chromium/net/server/web_socket_encoder_fuzzer.cc new file mode 100644 index 00000000000..d738740e9dd --- /dev/null +++ b/chromium/net/server/web_socket_encoder_fuzzer.cc @@ -0,0 +1,23 @@ +#include <stddef.h> +#include <stdint.h> + +#include <fuzzer/FuzzedDataProvider.h> + +#include <memory> +#include <string> + +#include "net/server/web_socket_encoder.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fuzzed_data_provider(data, size); + auto server = net::WebSocketEncoder::CreateServer(); + int bytes_consumed; + std::string decoded; + + while (fuzzed_data_provider.remaining_bytes() > 0) { + size_t chunk_size = fuzzed_data_provider.ConsumeIntegralInRange(1, 125); + std::string chunk = fuzzed_data_provider.ConsumeBytesAsString(chunk_size); + server->DecodeFrame(chunk, &bytes_consumed, &decoded); + } + return 0; +}
\ No newline at end of file diff --git a/chromium/net/server/web_socket_encoder_unittest.cc b/chromium/net/server/web_socket_encoder_unittest.cc index 4a180ab8e34..0120ea319ac 100644 --- a/chromium/net/server/web_socket_encoder_unittest.cc +++ b/chromium/net/server/web_socket_encoder_unittest.cc @@ -4,8 +4,12 @@ #include "net/server/web_socket_encoder.h" +#include <stddef.h> + +#include "base/strings/strcat.h" #include "net/websockets/websocket_deflate_parameters.h" #include "net/websockets/websocket_extension.h" +#include "net/websockets/websocket_frame.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -86,6 +90,64 @@ class WebSocketEncoderTest : public testing::Test { client_ = WebSocketEncoder::CreateClient(""); } + // Generate deflated and continuous frames from original text. + // The length of `original_text` must be longer than 4*partitions. + std::vector<std::string> GenerateFragmentedFrames(std::string original_text, + int mask, + int partitions, + bool compressed) { + constexpr uint8_t kFinalBit = 0x80; + constexpr uint8_t kReserved1Bit = 0x40; + constexpr uint8_t kMaskBit = 0x80; + + // A frame consists of 3 or 2 parts: header, (mask) and payload. + // The first two bytes of `encoded` are the header of the frame. + // If there is a mask, the four bytes of the mask is inserted after the + // header. Finally, message contents come. + std::string encoded; + int num_mask_header; + char mask_key_bit; + std::string mask_bytes; + + if (mask == 0) { + server_->EncodeTextFrame(original_text, mask, &encoded); + num_mask_header = 0; + mask_key_bit = 0; + } else { + client_->EncodeTextFrame(original_text, mask, &encoded); + num_mask_header = 4; + mask_key_bit = kMaskBit; + mask_bytes = encoded.substr(2, 4); + } + int divide_length = + (static_cast<int>(encoded.length()) - 2 - num_mask_header) / partitions; + divide_length -= divide_length % 4; + std::vector<std::string> encoded_frames(partitions); + std::string payload; + std::string header; + + for (int i = 0; i < partitions; ++i) { + char first_byte = 0; + if (i == 0) + first_byte |= WebSocketFrameHeader::OpCodeEnum::kOpCodeText; + else + first_byte |= WebSocketFrameHeader::OpCodeEnum::kOpCodeContinuation; + if (i == partitions - 1) + first_byte |= kFinalBit; + if (compressed) + first_byte |= kReserved1Bit; + + const int position = 2 + num_mask_header + i * divide_length; + const int length = + i < partitions - 1 ? divide_length : encoded.length() - position; + payload = encoded.substr(position, length); + header = {first_byte, static_cast<char>(payload.length() | mask_key_bit)}; + encoded_frames[i] += header + mask_bytes + payload; + } + + return encoded_frames; + } + protected: std::unique_ptr<WebSocketEncoder> server_; std::unique_ptr<WebSocketEncoder> client_; @@ -125,10 +187,10 @@ TEST_F(WebSocketEncoderTest, ClientToServer) { int bytes_consumed; std::string decoded; - client_->EncodeFrame(frame, mask, &encoded); - EXPECT_EQ(WebSocket::FRAME_OK, + client_->EncodeTextFrame(frame, mask, &encoded); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, server_->DecodeFrame(encoded, &bytes_consumed, &decoded)); - EXPECT_EQ(frame, decoded); + EXPECT_EQ("ClientToServer", decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); std::string partial = encoded.substr(0, encoded.length() - 2); @@ -136,9 +198,9 @@ TEST_F(WebSocketEncoderTest, ClientToServer) { server_->DecodeFrame(partial, &bytes_consumed, &decoded)); std::string extra = encoded + "more stuff"; - EXPECT_EQ(WebSocket::FRAME_OK, + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, server_->DecodeFrame(extra, &bytes_consumed, &decoded)); - EXPECT_EQ(frame, decoded); + EXPECT_EQ("ClientToServer", decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); EXPECT_EQ( @@ -153,10 +215,10 @@ TEST_F(WebSocketEncoderTest, ServerToClient) { int bytes_consumed; std::string decoded; - server_->EncodeFrame(frame, mask, &encoded); - EXPECT_EQ(WebSocket::FRAME_OK, + server_->EncodeTextFrame(frame, mask, &encoded); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, client_->DecodeFrame(encoded, &bytes_consumed, &decoded)); - EXPECT_EQ(frame, decoded); + EXPECT_EQ("ServerToClient", decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); std::string partial = encoded.substr(0, encoded.length() - 2); @@ -164,9 +226,9 @@ TEST_F(WebSocketEncoderTest, ServerToClient) { client_->DecodeFrame(partial, &bytes_consumed, &decoded)); std::string extra = encoded + "more stuff"; - EXPECT_EQ(WebSocket::FRAME_OK, + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, client_->DecodeFrame(extra, &bytes_consumed, &decoded)); - EXPECT_EQ(frame, decoded); + EXPECT_EQ("ServerToClient", decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); EXPECT_EQ( @@ -174,6 +236,132 @@ TEST_F(WebSocketEncoderTest, ServerToClient) { client_->DecodeFrame(std::string("abcde"), &bytes_consumed, &decoded)); } +TEST_F(WebSocketEncoderTest, DecodeFragmentedMessageClientToServerDivided2) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 123456; + constexpr bool kCompressed = false; + constexpr int kPartitions = 2; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedLastFrame = encoded_frames[1]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedLastFrame + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + server_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + server_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + +TEST_F(WebSocketEncoderTest, DecodeFragmentedMessageClientToServerDivided3) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 123456; + constexpr bool kCompressed = false; + constexpr int kPartitions = 3; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedSecondFrame = encoded_frames[1]; + const std::string& kEncodedLastFrame = encoded_frames[2]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedSecondFrame -> kEncodedLastFrame + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + server_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + server_->DecodeFrame(kEncodedSecondFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedSecondFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + server_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + +TEST_F(WebSocketEncoderTest, DecodeFragmentedMessageServerToClientDivided2) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 0; + constexpr bool kCompressed = false; + + constexpr int kPartitions = 2; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedLastFrame = encoded_frames[1]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedLastFrame + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + client_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + client_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + +TEST_F(WebSocketEncoderTest, DecodeFragmentedMessageServerToClientDivided3) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 0; + constexpr bool kCompressed = false; + + constexpr int kPartitions = 3; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedSecondFrame = encoded_frames[1]; + const std::string& kEncodedLastFrame = encoded_frames[2]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedSecondFrame -> kEncodedLastFrame + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + client_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + client_->DecodeFrame(kEncodedSecondFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedSecondFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + client_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + TEST_F(WebSocketEncoderCompressionTest, ClientToServer) { std::string frame("CompressionCompressionCompressionCompression"); int mask = 654321; @@ -181,9 +369,9 @@ TEST_F(WebSocketEncoderCompressionTest, ClientToServer) { int bytes_consumed; std::string decoded; - client_->EncodeFrame(frame, mask, &encoded); + client_->EncodeTextFrame(frame, mask, &encoded); EXPECT_LT(encoded.length(), frame.length()); - EXPECT_EQ(WebSocket::FRAME_OK, + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, server_->DecodeFrame(encoded, &bytes_consumed, &decoded)); EXPECT_EQ(frame, decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); @@ -196,9 +384,9 @@ TEST_F(WebSocketEncoderCompressionTest, ServerToClient) { int bytes_consumed; std::string decoded; - server_->EncodeFrame(frame, mask, &encoded); + server_->EncodeTextFrame(frame, mask, &encoded); EXPECT_LT(encoded.length(), frame.length()); - EXPECT_EQ(WebSocket::FRAME_OK, + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, client_->DecodeFrame(encoded, &bytes_consumed, &decoded)); EXPECT_EQ(frame, decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); @@ -223,12 +411,94 @@ TEST_F(WebSocketEncoderCompressionTest, LongFrame) { int bytes_consumed; std::string decoded; - server_->EncodeFrame(frame, mask, &encoded); + server_->EncodeTextFrame(frame, mask, &encoded); EXPECT_LT(encoded.length(), frame.length()); - EXPECT_EQ(WebSocket::FRAME_OK, + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, client_->DecodeFrame(encoded, &bytes_consumed, &decoded)); EXPECT_EQ(frame, decoded); EXPECT_EQ((int)encoded.length(), bytes_consumed); } +TEST_F(WebSocketEncoderCompressionTest, DecodeFragmentedMessageClientToServer) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 123456; + + constexpr int kPartitions = 3; + constexpr bool kCompressed = true; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedSecondFrame = encoded_frames[1]; + const std::string& kEncodedLastFrame = encoded_frames[2]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedSecondFrame -> kEncodedLastFrame + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + server_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + server_->DecodeFrame(kEncodedSecondFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedSecondFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + server_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + +TEST_F(WebSocketEncoderCompressionTest, DecodeFragmentedMessageServerToClient) { + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 0; + + constexpr int kPartitions = 3; + constexpr bool kCompressed = true; + ASSERT_GT(static_cast<int>(kOriginalText.length()), 4 * kPartitions); + std::vector<std::string> encoded_frames = + GenerateFragmentedFrames(kOriginalText, kMask, kPartitions, kCompressed); + ASSERT_EQ(kPartitions, static_cast<int>(encoded_frames.size())); + + const std::string& kEncodedFirstFrame = encoded_frames[0]; + const std::string& kEncodedSecondFrame = encoded_frames[1]; + const std::string& kEncodedLastFrame = encoded_frames[2]; + + int bytes_consumed; + std::string decoded; + + // kEncodedFirstFrame -> kEncodedSecondFrame -> kEncodedLastFrame + decoded.clear(); + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + client_->DecodeFrame(kEncodedFirstFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedFirstFrame.length()), bytes_consumed); + EXPECT_EQ( + WebSocket::FRAME_OK_MIDDLE, + client_->DecodeFrame(kEncodedSecondFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("", decoded); + EXPECT_EQ(static_cast<int>(kEncodedSecondFrame.length()), bytes_consumed); + EXPECT_EQ(WebSocket::FRAME_OK_FINAL, + client_->DecodeFrame(kEncodedLastFrame, &bytes_consumed, &decoded)); + EXPECT_EQ("abcdefghijklmnop", decoded); + EXPECT_EQ(static_cast<int>(kEncodedLastFrame.length()), bytes_consumed); +} + +TEST_F(WebSocketEncoderCompressionTest, CheckPongFrameNotCompressed) { + constexpr uint8_t kReserved1Bit = 0x40; + const std::string kOriginalText = "abcdefghijklmnop"; + constexpr int kMask = 0; + std::string encoded; + + server_->EncodePongFrame(kOriginalText, kMask, &encoded); + EXPECT_FALSE(encoded[1] & kReserved1Bit); + EXPECT_EQ(kOriginalText, encoded.substr(2)); +} + } // namespace net |