Skip to content

Commit 081e310

Browse files
committed
Optimization of synchronization when sending HTTP/2 frames
Added code for the WebSocket protocol
1 parent 0b6ff7e commit 081e310

File tree

5 files changed

+243
-22
lines changed

5 files changed

+243
-22
lines changed

projects/qt-creator/httpserverapp.qbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Project {
3232
"../../src/client/protocol/ClientHttp2.h",
3333
"../../src/client/protocol/ClientProtocol.cpp",
3434
"../../src/client/protocol/ClientProtocol.h",
35+
"../../src/client/protocol/WebSocket.cpp",
36+
"../../src/client/protocol/WebSocket.h",
3537
"../../src/utils/Event.cpp",
3638
"../../src/utils/Event.h",
3739
"../../src/transfer/FileIncoming.cpp",

src/application/Test.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,12 @@ namespace Application
135135

136136
const std::chrono::milliseconds timeout(5000);
137137

138-
response.sendHeaders(additional, timeout, s.empty() );
139-
140-
if (false == s.empty() )
138+
if (response.sendHeaders(additional, timeout, s.empty() ) )
141139
{
142-
response.sendData(s.data(), s.size(), timeout, true);
140+
if (false == s.empty() )
141+
{
142+
response.sendData(s.data(), s.size(), timeout, true);
143+
}
143144
}
144145

145146
return EXIT_SUCCESS;

src/client/protocol/ClientHttp2.cpp

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,9 @@ namespace HttpClient
8080

8181
this->stream->setHttp2FrameHeader(reinterpret_cast<uint8_t *>(buf.data() ), frame_size, Http2::FrameType::HEADERS, flags);
8282

83-
this->stream->lock();
83+
const std::unique_lock<std::mutex> lock(*this->stream->mtx);
8484

85-
auto const is_sended = this->sock->nonblock_send(buf.data(), buf.size(), timeout) > 0;
86-
87-
if (endStream || false == is_sended)
88-
{
89-
this->stream->unlock();
90-
}
91-
92-
return is_sended;
85+
return this->sock->nonblock_send(buf.data(), buf.size(), timeout) > 0;
9386
}
9487

9588
void ClientHttp2::sendWindowUpdate(const uint32_t size, const std::chrono::milliseconds &timeout) const
@@ -101,6 +94,8 @@ namespace HttpClient
10194

10295
*reinterpret_cast<uint32_t *>(addr) = ::htonl(size);
10396

97+
const std::unique_lock<std::mutex> lock(*this->stream->mtx);
98+
10499
this->sock->nonblock_send(buf.data(), buf.size(), timeout);
105100
}
106101

@@ -161,9 +156,7 @@ namespace HttpClient
161156
++cur;
162157
}
163158

164-
const Http2::FrameType frame_type = Http2::FrameType::DATA;
165-
166-
this->stream->setHttp2FrameHeader(buf.data(), frame_size, frame_type, flags);
159+
this->stream->setHttp2FrameHeader(buf.data(), frame_size, Http2::FrameType::DATA, flags);
167160

168161
std::copy(data, data + data_size, buf.begin() + cur);
169162

@@ -172,7 +165,11 @@ namespace HttpClient
172165
std::fill(buf.end() - padding, buf.end(), 0);
173166
}
174167

175-
long sended = this->sock->nonblock_send(buf.data(), buf.size(), timeout);
168+
this->stream->lock();
169+
170+
const long sended = this->sock->nonblock_send(buf.data(), buf.size(), timeout);
171+
172+
this->stream->unlock();
176173

177174
if (sended <= 0)
178175
{
@@ -186,11 +183,6 @@ namespace HttpClient
186183
total += data_size;
187184
}
188185

189-
if (total == 0 || endStream)
190-
{
191-
this->stream->unlock();
192-
}
193-
194186
return static_cast<long>(total);
195187
}
196188
};

src/client/protocol/WebSocket.cpp

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
2+
#include "WebSocket.h"
3+
4+
#include "../../utils/Utils.h"
5+
#include "../../socket/Socket.h"
6+
7+
#ifdef POSIX
8+
#include <arpa/inet.h>
9+
#endif
10+
11+
namespace HttpClient
12+
{
13+
WebSocket::WebSocket(const WebSocket &obj) noexcept : sock(obj.sock)
14+
{
15+
16+
}
17+
18+
WebSocket::WebSocket(Socket::Adapter *adapter) noexcept : sock(adapter)
19+
{
20+
21+
}
22+
23+
std::vector<char> WebSocket::packDataToMessageFrame(const void *data, const size_t size)
24+
{
25+
std::vector<char> frame;
26+
27+
if (0 == size)
28+
{
29+
return frame;
30+
}
31+
32+
constexpr size_t header_max_size = 14;
33+
frame.reserve(size + header_max_size);
34+
frame.resize(header_max_size);
35+
36+
frame[0] = 0x81;
37+
38+
size_t cur_pos = sizeof(uint8_t) * 2;
39+
40+
if (size <= 125)
41+
{
42+
frame[1] = size;
43+
}
44+
else if (size <= 65536)
45+
{
46+
frame[1] = 126;
47+
48+
*reinterpret_cast<uint16_t *>(&frame[2]) = htons(size);
49+
50+
cur_pos += sizeof(uint16_t);
51+
}
52+
else // More
53+
{
54+
frame[1] = 127;
55+
56+
*reinterpret_cast<uint64_t *>(&frame[2]) = Utils::hton64(size);
57+
58+
cur_pos += sizeof(uint64_t);
59+
}
60+
61+
frame.erase(frame.cbegin() + cur_pos, frame.cend() );
62+
63+
frame.insert(frame.cend(), reinterpret_cast<const char *>(data), reinterpret_cast<const char *>(data) + size);
64+
65+
return frame;
66+
}
67+
68+
Socket::Adapter *WebSocket::getSocket() noexcept
69+
{
70+
return this->sock;
71+
}
72+
73+
const Socket::Adapter *WebSocket::getSocket() const noexcept
74+
{
75+
return this->sock;
76+
}
77+
78+
long WebSocket::nonblock_recv(std::vector<char> &frame, const std::chrono::milliseconds &timeout) const
79+
{
80+
std::vector<char> buf(4096);
81+
82+
const long recv_size = this->sock->nonblock_recv(buf, timeout);
83+
84+
if (recv_size <= 0)
85+
{
86+
return recv_size;
87+
}
88+
89+
// See @link: http://tools.ietf.org/html/rfc6455#page-28
90+
91+
const uint8_t info_frame = buf[0];
92+
93+
if ( (info_frame & 0x08) == 0x08) // opcode 0x08 — close connection
94+
{
95+
return -1;
96+
}
97+
98+
uint8_t info_size = buf[1];
99+
100+
const bool is_mask_set = info_size & uint8_t(1 << 7);
101+
102+
info_size &= ~uint8_t(1 << 7); // Unset first bit
103+
104+
size_t cur_pos = sizeof(uint8_t) * 2;
105+
106+
uint64_t frame_size;
107+
108+
if (info_size <= 125)
109+
{
110+
frame_size = info_size;
111+
}
112+
else if (info_size == 126)
113+
{
114+
frame_size = ntohs(*reinterpret_cast<uint16_t *>(&buf[cur_pos]) );
115+
116+
cur_pos += sizeof(uint16_t);
117+
}
118+
else // if (info_size == 127)
119+
{
120+
frame_size = Utils::ntoh64(*reinterpret_cast<uint64_t *>(&buf[cur_pos]) );
121+
122+
cur_pos += sizeof(uint64_t);
123+
}
124+
125+
if (frame_size > (buf.size() - cur_pos) )
126+
{
127+
return -1; // Close connection
128+
}
129+
130+
uint32_t mask;
131+
132+
if (is_mask_set)
133+
{
134+
mask = *reinterpret_cast<uint32_t *>(&buf[cur_pos]);
135+
136+
cur_pos += sizeof(uint32_t);
137+
}
138+
139+
const uint8_t align = (recv_size - cur_pos) % sizeof(uint32_t);
140+
141+
frame.reserve(recv_size - cur_pos + align);
142+
143+
frame.assign(buf.cbegin() + cur_pos, buf.cbegin() + recv_size);
144+
145+
if (is_mask_set)
146+
{
147+
if (align)
148+
{
149+
frame.insert(frame.cend(), align, 0);
150+
}
151+
152+
uint32_t *addr = reinterpret_cast<uint32_t *>(frame.data() );
153+
154+
for (size_t i = 0; i < frame.size() / sizeof(uint32_t); ++i)
155+
{
156+
addr[i] ^= mask;
157+
}
158+
159+
if (align)
160+
{
161+
frame.erase(frame.cend() - align, frame.cend() );
162+
}
163+
}
164+
165+
return recv_size - cur_pos;
166+
}
167+
168+
long WebSocket::nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const
169+
{
170+
const std::vector<char> frame = WebSocket::packDataToMessageFrame(data, length);
171+
172+
if (frame.empty() )
173+
{
174+
return 0;
175+
}
176+
177+
return this->sock->nonblock_send(frame.data(), frame.size(), timeout);
178+
}
179+
180+
long WebSocket::nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const
181+
{
182+
const std::vector<char> frame = WebSocket::packDataToMessageFrame(str.data(), str.length() );
183+
184+
if (frame.empty() )
185+
{
186+
return 0;
187+
}
188+
189+
return this->sock->nonblock_send(frame.data(), frame.size(), timeout);
190+
}
191+
192+
void WebSocket::close() noexcept
193+
{
194+
this->sock->close();
195+
}
196+
};

src/client/protocol/WebSocket.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
3+
#include "../../socket/Adapter.h"
4+
5+
namespace HttpClient
6+
{
7+
class WebSocket
8+
{
9+
private:
10+
Socket::Adapter *sock;
11+
12+
public:
13+
static std::vector<char> packDataToMessageFrame(const void *data, const size_t size);
14+
15+
public:
16+
WebSocket() = delete;
17+
WebSocket(const WebSocket &obj) noexcept;
18+
WebSocket(Socket::Adapter *adapter) noexcept;
19+
20+
Socket::Adapter *getSocket() noexcept;
21+
const Socket::Adapter *getSocket() const noexcept;
22+
23+
long nonblock_recv(std::vector<char> &frame, const std::chrono::milliseconds &timeout) const;
24+
25+
long nonblock_send(const void *data, const size_t length, const std::chrono::milliseconds &timeout) const;
26+
long nonblock_send(const std::string &str, const std::chrono::milliseconds &timeout) const;
27+
28+
void close() noexcept;
29+
};
30+
};

0 commit comments

Comments
 (0)