Skip to content

Commit

Permalink
#49 - websocketProtocol handles reads data in chunks.
Browse files Browse the repository at this point in the history
  • Loading branch information
PerMalmberg committed Jul 15, 2019
1 parent c23ba0b commit 3c5ea82
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 23 deletions.
49 changes: 32 additions & 17 deletions lib/smooth/application/network/http/websocket/WebsocketProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <smooth/application/network/http/regular/HTTPHeaderDef.h>
#include <smooth/application/network/http/regular/RegularHTTPProtocol.h>
#include <smooth/application/network/http/regular/responses/ErrorResponse.h>
#include <smooth/core/network/util.h>

// https://tools.ietf.org/html/rfc6455#section-5.2

Expand Down Expand Up @@ -47,6 +48,7 @@
namespace smooth::application::network::http::websocket
{
using namespace smooth::core;
using namespace smooth::core::network;
using namespace smooth::application::network::http::regular;

int WebsocketProtocol::get_wanted_amount(HTTPPacket& packet)
Expand All @@ -62,16 +64,24 @@ namespace smooth::application::network::http::websocket
}
else if (state == State::ExtendedPayloadLength_8)
{
len = 2;
len = 8;
}
else if (state == State::MaskingKey)
{
len = 4;
}
else
{
packet.ensure_room(payload_length);
len = payload_length - received_payload;
// Websockets can use up to 64 bits (well, 63 since MSB MUST always be 0,
// see https://tools.ietf.org/html/rfc6455#section-5.2) bits to specify the size.
// Since we can't handle that large data buffers, we split it into smaller parts based on content_chunk_size

// First get the maximum number of bytes to read, this limits the value to fit in the type of
// content_chunk_size, making the following cast safe too.
auto size = std::min(payload_length - received_payload,
static_cast<decltype(payload_length)>(content_chunk_size));
len = static_cast<decltype(content_chunk_size)>(size);
packet.ensure_room(len);
}

return len;
Expand Down Expand Up @@ -101,45 +111,44 @@ namespace smooth::application::network::http::websocket
}
else if (payload_length == 127)
{
// Can't handle such large data.
Log::error("WebsocketProtocol", "Too large data size");
error = true;
state = State::ExtendedPayloadLength_8;
}
else if (masked)
{
state = State::MaskingKey;
}
else
{
decode_index = 0;
state = State::Payload;
}
}
else if (state == State::ExtendedPayloadLength_2)
{
auto len = reinterpret_cast<uint16_t*>(&frame_data[2]);
payload_length = *len;
payload_length = ntoh(*reinterpret_cast<uint16_t*>(&frame_data[2]));
state = State::MaskingKey;
}
else if (state == State::ExtendedPayloadLength_8)
{
//auto len = reinterpret_cast<uint64_t*>(&frame_data[2]);
//payload_length = *len;
payload_length = ntoh(*reinterpret_cast<uint64_t*>(&frame_data[2]));
state = State::MaskingKey;
}
else if (state == State::MaskingKey)
{
decode_index = 0;
state = State::Payload;
}
else
{
auto len = static_cast<decltype(received_payload)>(length);
if (masked)
{
for (auto i = static_cast<unsigned long>(received_payload); i < static_cast<unsigned long>(received_payload + length); ++i)
for (auto i = 0u; i < len; ++i)
{
packet.data()[i] = packet.data()[i] ^ mask_key[i % 4];
packet.data()[received_payload_in_current_package + i] =
packet.data()[received_payload + i] ^ mask_key[(received_payload + i) % 4];
}
}
received_payload += length;
received_payload += len;
received_payload_in_current_package += len;
}
}

Expand All @@ -157,13 +166,17 @@ namespace smooth::application::network::http::websocket
}
else
{
return packet.data().data() + received_payload;
return packet.data().data() + received_payload_in_current_package;
}
}

bool WebsocketProtocol::is_complete(HTTPPacket& /*packet*/) const
{
return state == State::Payload && received_payload == payload_length;
return state == State::Payload && (
received_payload == payload_length
|| received_payload_in_current_package ==
static_cast<decltype(received_payload_in_current_package)>(content_chunk_size));

}

bool WebsocketProtocol::is_error()
Expand All @@ -174,6 +187,7 @@ namespace smooth::application::network::http::websocket
void WebsocketProtocol::packet_consumed()
{
error = false;
received_payload_in_current_package = 0;
}

void WebsocketProtocol::reset()
Expand All @@ -183,6 +197,7 @@ namespace smooth::application::network::http::websocket
total_byte_count = 0;
payload_length = 0;
received_payload = 0;
received_payload_in_current_package = 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ namespace smooth::application::network::http::websocket

bool error{false};
int total_byte_count{0};
int payload_length{0};
int received_payload{0};
int decode_index{0};
uint64_t payload_length{0};
uint64_t received_payload{0};
uint64_t received_payload_in_current_package{0};

std::array<uint8_t, 4> mask_key{};
std::array<uint8_t, 11> frame_data{};
Expand Down
39 changes: 39 additions & 0 deletions lib/smooth/include/smooth/core/network/util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Smooth - C++ framework for writing applications based on Espressif's ESP-IDF.
// Copyright (C) 2017 Per Malmberg (https://github.com/PerMalmberg)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <endian.h>
#include <algorithm>

namespace smooth::core::network
{
template<typename T>
constexpr T hton(T value) noexcept
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
auto* ptr = reinterpret_cast<uint8_t*>(&value);
std::reverse(ptr, ptr + sizeof(T));
#endif
return value;
}

template<typename T>
constexpr T ntoh(T value) noexcept
{
return hton(value);
}
}
20 changes: 17 additions & 3 deletions test/http_server_test/web_root/websocket.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,29 @@
});
}

function send() {
socket.send("Hello!");
function send_short() {
let s = "A".repeat(124);
socket.send(s + "-");
}

function send_65k() {
let s = "B".repeat(65534);
socket.send(s + "-");
}

function send_80k() {
let s = "C".repeat(79999);
socket.send(s + "-");
}

</script>
</head>
<body>
<button onclick="connect();">Connect</button>
<button onclick="send();">Send</button>
<button onclick="send_short();">Send short</button>
<button onclick="send_65k();">Send 65k</button>
<button onclick="send_80k();">Send 80k</button>


</body>
</html>

0 comments on commit 3c5ea82

Please sign in to comment.