WCF (Websocket Communication Foundation) - a skeleton for websocket transport

This commit is contained in:
moozzyk 2014-10-21 15:39:57 -07:00
Родитель 05101d3f68
Коммит 6f089a3934
7 изменённых файлов: 252 добавлений и 1 удалений

Просмотреть файл

@ -101,6 +101,7 @@
<ClInclude Include="..\..\stdafx.h" />
<ClInclude Include="..\..\targetver.h" />
<ClInclude Include="..\..\url_builder.h" />
<ClInclude Include="..\..\websocket_transport.h" />
<ClInclude Include="..\..\web_request_factory.h" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -60,6 +60,9 @@
<ClInclude Include="..\..\request_sender.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\websocket_transport.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\stdafx.cpp">

Просмотреть файл

@ -17,7 +17,7 @@ namespace signalr
m_user_agent_string = user_agent_string;
}
pplx::task<web_response> web_request::get_response() const
pplx::task<web_response> web_request::get_response()
{
web::http::client::http_client client(m_url);
web::http::http_request request(m_method);

Просмотреть файл

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include <cpprest/ws_client.h>
#include "url_builder.h"
using namespace web::experimental;
namespace signalr
{
template<typename T = web_sockets::client::websocket_client> // testing
class websocket_transport
{
public:
websocket_transport() : websocket_transport(T())
{ }
websocket_transport(T&& websocket_client)
: m_websocket_client(std::move(websocket_client))
{ }
websocket_transport(T websocket_client)
: m_websocket_client(websocket_client)
{ }
websocket_transport(const websocket_transport&) = delete;
websocket_transport<T>& operator=(const websocket_transport&) = delete;
pplx::task<void> connect(const web::uri &url)
{
// TODO: prepare request (websocket_client_config)
m_websocket_client.connect(url)
.then([this](pplx::task<void> connect_task){
try
{
connect_task.wait();
receive_loop();
this->m_connect_tce.set();
}
catch (const std::exception &e)
{
this->m_connect_tce.set_exception(e);
}
});
return pplx::create_task(m_connect_tce);
}
pplx::task<void> send(const utility::string_t &data)
{
web_sockets::client::websocket_outgoing_message msg;
msg.set_utf8_message(utility::conversions::to_utf8string(data));
// send will throw if client not connected
return m_websocket_client.send(msg);
}
pplx::task<void> disconnect()
{
return m_websocket_client.close();
}
private:
T m_websocket_client;
pplx::task_completion_event<void> m_connect_tce;
void receive_loop()
{
m_websocket_client.receive().then([this](pplx::task<web_sockets::client::websocket_incoming_message> msg_task)
{
try
{
auto msg = msg_task.get();
msg.extract_string().then([this](std::string msg_body)
{
receive_loop();
});
}
catch (web_sockets::client::websocket_exception &)
{
// TODO: should close the websocket?
// TODO: report error
}
catch (std::exception &)
{
// TODO: should close the websocket?
// TODO: report error
}
});
}
};
}

Просмотреть файл

@ -98,6 +98,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\..\url_builder_tests.cpp" />
<ClCompile Include="..\..\websocket_transport_tests.cpp" />
<ClCompile Include="..\..\web_request_tests.cpp" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -47,6 +47,9 @@
<ClCompile Include="..\..\web_request_tests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\websocket_transport_tests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

Просмотреть файл

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#include "stdafx.h"
#include "websocket_transport.h"
using namespace signalr;
using namespace web::experimental;
class test_websocket_client
{
public:
pplx::task<void> connect(const web::uri &url)
{
return m_connect_function(url);
}
pplx::task<void> send(web_sockets::client::websocket_outgoing_message msg)
{
return m_send_function(msg);
}
pplx::task<web_sockets::client::websocket_incoming_message> receive()
{
return m_receive_function();
}
pplx::task<void> close()
{
return m_close_function();
}
void set_connect_function(std::function<pplx::task<void>(const web::uri &url)> connect_function)
{
m_connect_function = connect_function;
}
void set_send_function(std::function<pplx::task<void>(web_sockets::client::websocket_outgoing_message)> send_function)
{
m_send_function = send_function;
}
void set_receive_function(std::function<pplx::task<web_sockets::client::websocket_incoming_message>()> receive_function)
{
m_receive_function = receive_function;
}
void set_close_function(std::function<pplx::task<void>()> close_function)
{
m_close_function = close_function;
}
private:
std::function<pplx::task<void>(const web::uri &url)> m_connect_function
= [](const web::uri &){ return pplx::task_from_result(); };
std::function<pplx::task<void>(web_sockets::client::websocket_outgoing_message)> m_send_function
= [](web_sockets::client::websocket_outgoing_message msg){ return pplx::task_from_result(); };
std::function<pplx::task<web_sockets::client::websocket_incoming_message>()> m_receive_function
= [](){ return pplx::task_from_result(web_sockets::client::websocket_incoming_message()); };
std::function<pplx::task<void>()> m_close_function
= [](){ return pplx::task_from_result(); };
};
TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
{
bool connect_called = false, receive_called = false;
test_websocket_client client;
client.set_connect_function([&connect_called](const web::uri &) -> pplx::task<void>
{
connect_called = true;
return pplx::task_from_result();
});
client.set_receive_function([&receive_called]() -> pplx::task<web_sockets::client::websocket_incoming_message>
{
receive_called = true;
return pplx::task_from_result(web_sockets::client::websocket_incoming_message());
});
websocket_transport<test_websocket_client> ws_transport(client);
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).wait();
ASSERT_TRUE(connect_called);
ASSERT_TRUE(receive_called);
}
TEST(websocket_transport_connect, connect_propagates_exceptions)
{
test_websocket_client client;
client.set_connect_function([](const web::uri &) -> pplx::task<void>
{
throw web_sockets::client::websocket_exception(_XPLATSTR("connecting failed"));
});
websocket_transport<test_websocket_client> ws_transport(client);
try
{
ws_transport.connect(_XPLATSTR("http://fakeuri.org")).wait();
ASSERT_TRUE(false); // exception not thrown
}
catch (const std::exception &e)
{
ASSERT_STREQ("connecting failed", e.what());
}
}
TEST(websocket_transport_send, send_creates_and_sends_websocket_messages)
{
bool send_called = false;
test_websocket_client client;
client.set_send_function([&send_called](web_sockets::client::websocket_outgoing_message msg) -> pplx::task<void>
{
send_called = true;
return pplx::task_from_result();
});
websocket_transport<test_websocket_client> ws_transport(client);
ws_transport.send(_XPLATSTR("ABC")).wait();
ASSERT_TRUE(send_called);
}
TEST(websocket_transport_disconnect, disconnect_closes_websocket)
{
bool close_called = false;
test_websocket_client client;
client.set_close_function([&close_called]() -> pplx::task<void>
{
close_called = true;
return pplx::task_from_result();
});
websocket_transport<test_websocket_client> ws_transport(client);
ws_transport.disconnect().wait();
ASSERT_TRUE(close_called);
}