AlmostTemplateFreeWorld - replacing compile time polymorphism with runtime polymorphism

Leaving templates in the websocket_transport
This commit is contained in:
moozzyk 2014-10-29 13:55:45 -07:00
Родитель 1a9488627d
Коммит 68475cb441
20 изменённых файлов: 231 добавлений и 169 удалений

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

@ -6,41 +6,34 @@
#include <cpprest\http_client.h>
#include "_exports.h"
#include "transport_type.h"
#include "web_request.h"
#include "web_request_factory.h"
#include "transport_factory.h"
namespace signalr
{
template<typename T>
class connection_impl;
template<typename T = web_request>
class connection_T
class connection
{
public:
connection_T(const utility::string_t& url, const utility::string_t& querystring = U(""))
: m_base_uri(url), m_querystring(querystring), m_pImpl(new connection_impl<T>())
{}
connection(const utility::string_t& url, const utility::string_t& querystring = U(""));
connection_T(const connection_T<T>&) = delete;
connection(const connection&) = delete;
connection_T<T>& operator=(const connection_T<T>&) = delete;
connection& operator=(const connection&) = delete;
~connection_T()
{
delete m_pImpl;
}
~connection();
SIGNALRCLIENT_API pplx::task<void> start()
{
return m_pImpl->start();
}
SIGNALRCLIENT_API pplx::task<void> start();
private:
web::uri m_base_uri;
utility::string_t m_querystring;
connection_impl<T> *m_pImpl;
};
// the order is important since we the factories are used to create and initialize the connection_impl instance
web_request_factory m_web_request_factory;
transport_factory m_transport_factory;
using connection = connection_T<>;
connection_impl *m_pImpl;
};
}

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

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

@ -11,6 +11,12 @@ namespace signalr
{
class transport_factory
{
std::unique_ptr<transport> virtual create_transport(transport_type transport_type);
public:
std::unique_ptr<transport> virtual create_transport(transport_type /*transport_type*/)
{
throw std::exception("not implemented");
}
virtual ~transport_factory() {};
};
}

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

@ -6,7 +6,6 @@
#include <cpprest\basic_types.h>
#include <cpprest\base_uri.h>
#include "web_response.h"
#include "_exports.h"
namespace signalr
{
@ -21,9 +20,9 @@ namespace signalr
web_request(const web::uri &url) : m_url(url)
{}
void set_method(const utility::string_t &method);
void set_user_agent(const utility::string_t &user_agent_string);
virtual void set_method(const utility::string_t &method);
virtual void set_user_agent(const utility::string_t &user_agent_string);
pplx::task<web_response> get_response();
virtual pplx::task<web_response> get_response();
};
}

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

@ -4,19 +4,19 @@
#pragma once
#include <cpprest\base_uri.h>
#include "web_request.h"
namespace signalr
{
template<typename T>
class web_request_factory
{
public:
virtual std::unique_ptr<web_request> create_web_request(const web::uri &url)
{
return std::make_unique<web_request>(url);
}
virtual ~web_request_factory()
{}
virtual T create_web_request(const web::uri &url) const
{
return T(url);
}
};
}

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

@ -5,7 +5,6 @@
#include <ppl.h>
#include <cpprest\basic_types.h>
#include "_exports.h"
namespace pplx = concurrency;

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

@ -89,9 +89,12 @@
<ItemGroup>
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\transport.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\transport_factory.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\web_request.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\web_request_factory.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\web_response.h" />
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h" />
<ClInclude Include="..\..\connection_impl.h" />
@ -101,13 +104,14 @@
<ClInclude Include="..\..\request_sender.h" />
<ClInclude Include="..\..\stdafx.h" />
<ClInclude Include="..\..\targetver.h" />
<ClInclude Include="..\..\transport.h" />
<ClInclude Include="..\..\transport_factory.h" />
<ClInclude Include="..\..\url_builder.h" />
<ClInclude Include="..\..\websocket_transport.h" />
<ClInclude Include="..\..\web_request_factory.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\connection.cpp" />
<ClCompile Include="..\..\connection_impl.cpp" />
<ClCompile Include="..\..\http_sender.cpp" />
<ClCompile Include="..\..\request_sender.cpp" />
<ClCompile Include="..\..\stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>

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

@ -39,21 +39,12 @@
<ClInclude Include="..\..\constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_response.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_request.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\http_sender.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\web_request_factory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\negotiation_response.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -66,10 +57,19 @@
<ClInclude Include="..\..\connection_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\transport_factory.h">
<ClInclude Include="..\..\..\..\include\signalrclient\transport.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\transport.h">
<ClInclude Include="..\..\..\..\include\signalrclient\transport_factory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_request.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_request_factory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\..\include\signalrclient\web_response.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -83,6 +83,18 @@
<ClCompile Include="..\..\web_request.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\request_sender.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\connection_impl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\http_sender.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\connection.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

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

@ -0,0 +1,27 @@
// 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 "signalrclient\connection.h"
#include "signalrclient\transport_type.h"
#include "signalrclient\web_request_factory.h"
#include "signalrclient\transport_factory.h"
#include "connection_impl.h"
namespace signalr
{
connection::connection(const utility::string_t& url, const utility::string_t& querystring)
: m_base_uri(url), m_querystring(querystring), m_pImpl(new connection_impl(m_web_request_factory, m_transport_factory))
{}
connection::~connection()
{
delete m_pImpl;
}
pplx::task<void> connection::start()
{
return m_pImpl->start();
}
}

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

@ -0,0 +1,13 @@
// 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 "connection_impl.h"
namespace signalr
{
pplx::task<void> connection_impl::start()
{
return pplx::task_from_result();
}
}

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

@ -4,27 +4,28 @@
#pragma once
#include <cpprest\http_client.h>
#include "signalrclient\web_request_factory.h"
#include "signalrclient\transport_factory.h"
namespace signalr
{
template<typename T>
class connection_impl
{
public:
connection_impl(const transport_factory& transport_factory)
: m_transport_factory(transport_factory)
// taking references to be able to inject factories for test purposes. The consumer must
// make sure that actual instances outlive connection_impl
connection_impl(web_request_factory& web_request_factory, transport_factory& transport_factory)
: m_web_request_factory(web_request_factory), m_transport_factory(transport_factory)
{ }
connection_impl(const connection_impl<T>&) = delete;
connection_impl(const connection_impl&) = delete;
connection_impl<T>& operator=(const connection_impl<T>&) = delete;
connection_impl& operator=(const connection_impl&) = delete;
pplx::task<void> start()
{
return pplx::task_from_result();
}
pplx::task<void> start();
private:
const transport_factory& m_transport_factory;
web_request_factory &m_web_request_factory;
transport_factory& m_transport_factory;
};
}

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

@ -0,0 +1,34 @@
// 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 <cpprest\http_client.h>
#include "signalrclient\web_exception.h"
#include "signalrclient\web_request.h"
#include "constants.h"
namespace signalr
{
namespace http_sender
{
pplx::task<utility::string_t> get(web_request &request)
{
request.set_method(web::http::methods::GET);
// TODO: set other and custom headers
request.set_user_agent(USER_AGENT);
return request.get_response().then([](web_response response)
{
if (response.status_code != 200)
{
utility::ostringstream_t oss;
oss << _XPLATSTR("web exception: ") << response.status_code << _XPLATSTR(" ") << response.reason_phrase;
throw web_exception(oss.str());
}
return response.body;
});
}
}
}

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

@ -1,35 +1,19 @@
// 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.
#pragma once
#include <ppl.h>
#include <cpprest\basic_types.h>
#include <cpprest\http_client.h>
#include "signalrclient\web_response.h"
#include "signalrclient\web_exception.h"
#include "constants.h"
#include "signalrclient\web_request.h"
namespace pplx = concurrency;
namespace signalr
{
namespace http_sender
{
template<typename T>
static pplx::task<utility::string_t> get(T &request)
{
request.set_method(web::http::methods::GET);
// TODO: set other and custom headers
request.set_user_agent(USER_AGENT);
return request.get_response().then([](web_response response)
{
if (response.status_code != 200)
{
utility::ostringstream_t oss;
oss << _XPLATSTR("web exception: ") << response.status_code << _XPLATSTR(" ") << response.reason_phrase;
throw web_exception(oss.str());
}
return response.body;
});
}
pplx::task<utility::string_t> get(web_request &request);
}
}

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

@ -0,0 +1,36 @@
// 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 "request_sender.h"
#include "http_sender.h"
#include "url_builder.h"
namespace signalr
{
namespace request_sender
{
pplx::task<negotiation_response> negotiate(web_request_factory &request_factory, const web::uri &base_url, const utility::string_t &query_string)
{
auto negotiate_url = url_builder::build_negotiate(base_url, query_string);
auto request = request_factory.create_web_request(negotiate_url);
return http_sender::get(*request)
.then([](utility::string_t body)
{
auto negotiation_response_json = web::json::value::parse(body);
negotiation_response response{
negotiation_response_json[_XPLATSTR("ConnectionId")].as_string(),
negotiation_response_json[_XPLATSTR("ConnectionToken")].as_string(),
(int)(negotiation_response_json[_XPLATSTR("DisconnectTimeout")].as_double() * 1000),
(int)(negotiation_response_json[_XPLATSTR("KeepAliveTimeout")].as_double() * 1000),
negotiation_response_json[_XPLATSTR("ProtocolVersion")].as_string(),
negotiation_response_json[_XPLATSTR("TryWebSockets")].as_bool(),
(int)(negotiation_response_json[_XPLATSTR("TransportConnectTimeout")].as_double() * 1000)
};
return response;
});
}
}
}

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

@ -4,36 +4,13 @@
#pragma once
#include <cpprest\base_uri.h>
#include "http_sender.h"
#include "url_builder.h"
#include "web_request_factory.h"
#include "signalrclient\web_request_factory.h"
#include "negotiation_response.h"
namespace signalr
{
namespace request_sender
{
template<typename T>
pplx::task<negotiation_response> negotiate(const web_request_factory<T> &request_factory, const web::uri &base_url, const utility::string_t &query_string)
{
auto negotiate_url = url_builder::build_negotiate(base_url, query_string);
auto request = request_factory.create_web_request(negotiate_url);
return http_sender::get(request)
.then([](utility::string_t body)
{
auto negotiation_response_json = web::json::value::parse(body);
negotiation_response response {
negotiation_response_json[_XPLATSTR("ConnectionId")].as_string(),
negotiation_response_json[_XPLATSTR("ConnectionToken")].as_string(),
(int)(negotiation_response_json[_XPLATSTR("DisconnectTimeout")].as_double() * 1000),
(int)(negotiation_response_json[_XPLATSTR("KeepAliveTimeout")].as_double() * 1000),
negotiation_response_json[_XPLATSTR("ProtocolVersion")].as_string(),
negotiation_response_json[_XPLATSTR("TryWebSockets")].as_bool(),
(int)(negotiation_response_json[_XPLATSTR("TransportConnectTimeout")].as_double() * 1000)
};
return response;
});
}
pplx::task<negotiation_response> negotiate(web_request_factory &request_factory, const web::uri &base_url, const utility::string_t &query_string);
}
}

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

@ -5,7 +5,7 @@
#include <cpprest/ws_client.h>
#include "url_builder.h"
#include "transport.h"
#include "signalrclient\transport.h"
using namespace web::experimental;

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

@ -13,7 +13,7 @@ TEST(http_sender_get_response, request_sent_using_get_method)
utility::string_t response_body{ _XPLATSTR("response body") };
web_request_stub request{ (unsigned short)200, _XPLATSTR("OK"), response_body };
ASSERT_EQ(response_body, http_sender::get<web_request_stub>(request).get());
ASSERT_EQ(response_body, http_sender::get(request).get());
ASSERT_EQ(_XPLATSTR("GET"), request.m_method);
}
@ -40,7 +40,7 @@ TEST(http_sender_get_response, user_agent_set)
utility::string_t response_body{ _XPLATSTR("response body") };
web_request_stub request{ (unsigned short)200, response_body };
http_sender::get<web_request_stub>(request).get();
http_sender::get(request).get();
ASSERT_EQ(_XPLATSTR("SignalR.Client.Cpp/3.0.0"), request.m_user_agent_string);
}

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

@ -9,56 +9,38 @@
using namespace signalr;
// this class should not be visible outside this file
template<typename T>
class request_factory_fake : public web_request_factory<T>
{
private:
T m_web_request;
mutable web::uri m_last_url;
public:
request_factory_fake(const T &web_request)
: m_web_request(web_request)
{ }
T create_web_request(const web::uri &url) const
{
m_last_url = url;
return m_web_request;
}
web::uri get_last_url() const
{
return m_last_url;
}
};
TEST(request_sender_negotiate, request_created_with_correct_url)
{
utility::string_t response_body(
_XPLATSTR("{\"Url\":\"/signalr\", \"ConnectionToken\" : \"A==\", \"ConnectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", ")
_XPLATSTR("\"KeepAliveTimeout\" : 20.0, \"DisconnectTimeout\" : 30.0, \"ConnectionTimeout\" : 110.0, \"TryWebSockets\" : true, ")
_XPLATSTR("\"ProtocolVersion\" : \"1.4\", \"TransportConnectTimeout\" : 5.0, \"LongPollDelay\" : 0.0}"));
web::uri requested_url;
auto request_factory = test_web_request_factory([&requested_url](const web::uri &url) -> std::unique_ptr<web_request>
{
utility::string_t response_body(
_XPLATSTR("{\"Url\":\"/signalr\", \"ConnectionToken\" : \"A==\", \"ConnectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", ")
_XPLATSTR("\"KeepAliveTimeout\" : 20.0, \"DisconnectTimeout\" : 30.0, \"ConnectionTimeout\" : 110.0, \"TryWebSockets\" : true, ")
_XPLATSTR("\"ProtocolVersion\" : \"1.4\", \"TransportConnectTimeout\" : 5.0, \"LongPollDelay\" : 0.0}"));
request_factory_fake<web_request_stub> request_factory{ web_request_stub{ (unsigned short)200, _XPLATSTR("OK"), response_body } };
requested_url = url;
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, _XPLATSTR("OK"), response_body));
});
request_sender::negotiate<web_request_stub>(request_factory, web::uri{ _XPLATSTR("http://fake/signalr") }, _XPLATSTR("")).get();
request_sender::negotiate(request_factory, web::uri{ _XPLATSTR("http://fake/signalr") }, _XPLATSTR("")).get();
ASSERT_EQ(web::uri(_XPLATSTR("http://fake/signalr/negotiate?clientProtocol=1.4")), request_factory.get_last_url());
ASSERT_EQ(web::uri(_XPLATSTR("http://fake/signalr/negotiate?clientProtocol=1.4")), requested_url);
}
TEST(request_sender_negotiate, negotiation_request_sent_and_response_serialized)
{
utility::string_t response_body(
_XPLATSTR("{\"Url\":\"/signalr\", \"ConnectionToken\" : \"A==\", \"ConnectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", ")
_XPLATSTR("\"KeepAliveTimeout\" : 20.0, \"DisconnectTimeout\" : 30.0, \"ConnectionTimeout\" : 110.0, \"TryWebSockets\" : true, ")
_XPLATSTR("\"ProtocolVersion\" : \"1.4\", \"TransportConnectTimeout\" : 5.5, \"LongPollDelay\" : 0.0}"));
auto request_factory = test_web_request_factory([](const web::uri&) -> std::unique_ptr<web_request>
{
utility::string_t response_body(
_XPLATSTR("{\"Url\":\"/signalr\", \"ConnectionToken\" : \"A==\", \"ConnectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", ")
_XPLATSTR("\"KeepAliveTimeout\" : 20.0, \"DisconnectTimeout\" : 30.0, \"ConnectionTimeout\" : 110.0, \"TryWebSockets\" : true, ")
_XPLATSTR("\"ProtocolVersion\" : \"1.4\", \"TransportConnectTimeout\" : 5.5, \"LongPollDelay\" : 0.0}"));
test_web_request_factory<web_request_stub> request_factory{ web_request_stub{ (unsigned short)200, _XPLATSTR("OK"), response_body } };
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, _XPLATSTR("OK"), response_body));
});
auto response = request_sender::negotiate<web_request_stub>(request_factory, web::uri{ _XPLATSTR("http://fake/signalr") }, _XPLATSTR("")).get();
auto response = request_sender::negotiate(request_factory, web::uri{ _XPLATSTR("http://fake/signalr") }, _XPLATSTR("")).get();
ASSERT_EQ(_XPLATSTR("f7707523-307d-4cba-9abf-3eef701241e8"), response.connection_id);
ASSERT_EQ(_XPLATSTR("A=="), response.connection_token);

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

@ -3,25 +3,23 @@
#pragma once
#include "web_request_factory.h"
#include "signalrclient\web_request_factory.h"
#include "web_request_stub.h"
using namespace signalr;
template<typename T>
class test_web_request_factory : public web_request_factory<T>
class test_web_request_factory : public web_request_factory
{
private:
T m_web_request;
public:
test_web_request_factory(const T &web_request)
: m_web_request(web_request)
test_web_request_factory(std::function<std::unique_ptr<web_request>(const web::uri &url)> create_web_request_fn)
: m_create_web_request_fn(create_web_request_fn)
{ }
T create_web_request(const web::uri &) const
virtual std::unique_ptr<web_request> create_web_request(const web::uri &url) override
{
return m_web_request;
return m_create_web_request_fn(url);
}
};
private:
std::function<std::unique_ptr<web_request>(const web::uri &url)> m_create_web_request_fn;
};

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

@ -5,36 +5,33 @@
#include <cpprest\basic_types.h>
#include "signalrclient\web_response.h"
#include "signalrclient\web_request.h"
using namespace signalr;
struct web_request_stub
struct web_request_stub : public web_request
{
web::uri m_url;
unsigned short m_status_code;
utility::string_t m_reason_phrase;
utility::string_t m_response_body;
utility::string_t m_method;
utility::string_t m_user_agent_string;
web_request_stub(const web::uri &url) : m_url(url)
web_request_stub(unsigned short status_code, const utility::string_t& reason_phrase, const utility::string_t& response_body = _XPLATSTR(""))
: web_request(web::uri(_XPLATSTR(""))), m_status_code(status_code), m_reason_phrase(reason_phrase), m_response_body(response_body)
{ }
web_request_stub(unsigned short status_code, const utility::string_t &reason_phrase, const utility::string_t &response_body = _XPLATSTR(""))
: m_status_code(status_code), m_reason_phrase(reason_phrase), m_response_body(response_body)
{ }
void set_method(utility::string_t method)
virtual void set_method(const utility::string_t &method) override
{
m_method = method;
}
void set_user_agent(utility::string_t user_agent_string)
virtual void set_user_agent(const utility::string_t &user_agent_string) override
{
m_user_agent_string = user_agent_string;
}
pplx::task<web_response> get_response()
virtual pplx::task<web_response> get_response() override
{
return pplx::task_from_result<web_response>(
web_response{ m_status_code, m_reason_phrase, pplx::task_from_result<utility::string_t>(m_response_body) });