Skip to content

Commit 3a9033d

Browse files
committed
Added an overload for websockets allowing length to be auto-determined
1 parent ff47bb5 commit 3a9033d

File tree

3 files changed

+121
-5
lines changed

3 files changed

+121
-5
lines changed

Release/include/cpprest/ws_msg.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class websocket_outgoing_message
139139
/// <summary>
140140
/// Sets a UTF-8 message as the message body.
141141
/// </summary>
142-
/// <params>UTF-8 String containing body of the message.</returns>
142+
/// <param name="data">UTF-8 String containing body of the message.</param>
143143
void set_utf8_message(std::string data)
144144
{
145145
this->_set_message(std::move(data), websocket_message_type::text_message);
@@ -148,7 +148,18 @@ class websocket_outgoing_message
148148
/// <summary>
149149
/// Sets a UTF-8 message as the message body.
150150
/// </summary>
151-
/// <params>casablanca input stream representing the body of the message.</returns>
151+
/// <param name="istream">casablanca input stream representing the body of the message.</param>
152+
/// <remarks>Upon sending, the entire stream will be buffered to determine the length.</remarks>
153+
void set_utf8_message(concurrency::streams::istream istream)
154+
{
155+
this->_set_message(istream, SIZE_MAX, websocket_message_type::text_message);
156+
}
157+
158+
/// <summary>
159+
/// Sets a UTF-8 message as the message body.
160+
/// </summary>
161+
/// <param name="istream">casablanca input stream representing the body of the message.</param>
162+
/// <param name="len">number of bytes to send</param>
152163
void set_utf8_message(concurrency::streams::istream istream, size_t len)
153164
{
154165
this->_set_message(istream, len, websocket_message_type::text_message);
@@ -157,12 +168,23 @@ class websocket_outgoing_message
157168
/// <summary>
158169
/// Sets binary data as the message body.
159170
/// </summary>
160-
/// <params>casablanca input stream representing the body of the message.</returns>
171+
/// <param name="istream">casablanca input stream representing the body of the message.</param>
172+
/// <param name="len">number of bytes to send</param>
161173
void set_binary_message(concurrency::streams::istream istream, size_t len)
162174
{
163175
this->_set_message(istream, len, websocket_message_type::binary_message);
164176
}
165177

178+
/// <summary>
179+
/// Sets binary data as the message body.
180+
/// </summary>
181+
/// <param name="istream">casablanca input stream representing the body of the message.</param>
182+
/// <remarks>Upon sending, the entire stream will be buffered to determine the length.</remarks>
183+
void set_binary_message(concurrency::streams::istream istream)
184+
{
185+
this->_set_message(istream, SIZE_MAX, websocket_message_type::binary_message);
186+
}
187+
166188
private:
167189

168190
friend class details::winrt_client;

Release/src/websockets/client/ws_client.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,9 +341,9 @@ class ws_desktop_client : public _websocket_client_impl, public std::enable_shar
341341
return pplx::task_from_exception<void>(websocket_exception("Cannot send empty message."));
342342
}
343343

344-
if (length > UINT_MAX)
344+
if (length >= UINT_MAX && length != SIZE_MAX)
345345
{
346-
return pplx::task_from_exception<void>(websocket_exception("Message size too large. Ensure message length is less than or equal to UINT_MAX."));
346+
return pplx::task_from_exception<void>(websocket_exception("Message size too large. Ensure message length is less than UINT_MAX."));
347347
}
348348

349349
{
@@ -395,6 +395,47 @@ class ws_desktop_client : public _websocket_client_impl, public std::enable_shar
395395
auto& is_buf = msg._m_impl->streambuf();
396396
auto length = msg._m_impl->length();
397397

398+
if (length == SIZE_MAX)
399+
{
400+
// This indicates we should determine the length automatically.
401+
if (is_buf.has_size())
402+
{
403+
// The user's stream knows how large it is -- there's no need to buffer.
404+
auto buf_sz = is_buf.size();
405+
if (buf_sz >= SIZE_MAX)
406+
{
407+
websocket_exception wx(_XPLATSTR("Cannot send messages larger than SIZE_MAX."));
408+
msg.m_send_tce.set_exception(std::make_exception_ptr(wx));
409+
return;
410+
}
411+
length = static_cast<size_t>(buf_sz);
412+
// We have determined the length and can proceed normally.
413+
}
414+
else
415+
{
416+
// The stream needs to be buffered.
417+
concurrency::streams::container_buffer<std::vector<uint8_t>> stbuf;
418+
auto is_buf_istream = is_buf.create_istream();
419+
msg._m_impl->set_streambuf(stbuf);
420+
is_buf_istream.read_to_end(stbuf).then([this_client,msg](pplx::task<size_t> t)
421+
{
422+
try
423+
{
424+
auto sz = t.get();
425+
msg._m_impl->set_length(sz);
426+
this_client->send_msg(msg);
427+
}
428+
catch (...)
429+
{
430+
auto eptr = std::current_exception();
431+
msg.m_send_tce.set_exception(eptr);
432+
}
433+
});
434+
// We have postponed the call to send_msg() until after the data is buffered.
435+
return;
436+
}
437+
}
438+
398439
// First try to acquire the data (Get a pointer to the next already allocated contiguous block of data)
399440
// If acquire succeeds, send the data over the socket connection, there is no copy of data from stream to temporary buffer.
400441
// If acquire fails, copy the data to a temporary buffer managed by sp_allocated and send it over the socket connection.

Release/tests/Functional/websockets/client/send_msg_tests.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,59 @@ TEST_FIXTURE(uri_address, send_multi_byte_utf8_msg)
392392
client.close().wait();
393393
}
394394

395+
// Send a streamed text message without specifying length
396+
TEST_FIXTURE(uri_address, send_stream_utf8_msg_no_length)
397+
{
398+
test_websocket_server server;
399+
400+
std::string body = "\xC3\xA0\xC3\xB8";
401+
std::vector<uint8_t> msgbuf(body.begin(), body.end());
402+
403+
auto is = streams::container_stream<std::vector<uint8_t>>::open_istream(std::move(msgbuf));
404+
405+
websocket_client client;
406+
{
407+
server.next_message([body](test_websocket_msg msg) // Handler to verify the message sent by the client.
408+
{
409+
websocket_asserts::assert_message_equals(msg, body, test_websocket_message_type::WEB_SOCKET_UTF8_MESSAGE_TYPE);
410+
});
411+
412+
client.connect(m_uri).wait();
413+
414+
websocket_outgoing_message msg;
415+
msg.set_utf8_message(is);
416+
client.send(msg).wait();
417+
}
418+
419+
client.close().wait();
420+
}
421+
422+
// Send a streamed binary message without specifying length
423+
TEST_FIXTURE(uri_address, send_stream_binary_msg_no_length)
424+
{
425+
test_websocket_server server;
426+
427+
std::vector<uint8_t> body = { { 0, 1, 2, 0 } };
428+
429+
auto is = streams::container_stream<std::vector<uint8_t>>::open_istream(body);
430+
431+
websocket_client client;
432+
{
433+
server.next_message([body](test_websocket_msg msg) // Handler to verify the message sent by the client.
434+
{
435+
websocket_asserts::assert_message_equals(msg, body, test_websocket_message_type::WEB_SOCKET_BINARY_MESSAGE_TYPE);
436+
});
437+
438+
client.connect(m_uri).wait();
439+
440+
websocket_outgoing_message msg;
441+
msg.set_binary_message(is);
442+
client.send(msg).wait();
443+
}
444+
445+
client.close().wait();
446+
}
447+
395448
} // SUITE(send_msg_tests)
396449

397450
}}}}

0 commit comments

Comments
 (0)