diff --git a/common/cpp/include/system_wrappers/io.h b/common/cpp/include/system_wrappers/io.h index a329ad37925ab3d6ed8d08adddd332a8415926cb..39dd23ccb445a559e83ee405554bad8c4aa33677 100644 --- a/common/cpp/include/system_wrappers/io.h +++ b/common/cpp/include/system_wrappers/io.h @@ -69,6 +69,11 @@ class IOErrorTemplate : public SimpleErrorTemplate { } }; +static inline std::ostream& operator<<(std::ostream& os, const IOErrorTemplate& err) { + return os << err.Text(); +} + + namespace IOErrorTemplates { auto const kUnknownIOError = IOErrorTemplate{"Unknown Error", IOErrorType::kUnknownIOError}; diff --git a/receiver/CMakeLists.txt b/receiver/CMakeLists.txt index 309848b3db2c4386e719d965c5af0c6a878058b5..cd5c8d6203e25a8afe31f6d597bc712c5c9913c8 100644 --- a/receiver/CMakeLists.txt +++ b/receiver/CMakeLists.txt @@ -4,8 +4,6 @@ set(SOURCE_FILES src/connection.h src/connection.cpp src/receiver_error.h src/request.cpp - src/request_handler.cpp - src/send_data_request.cpp ) diff --git a/receiver/src/connection.cpp b/receiver/src/connection.cpp index db615536627ca5da7a4df130624742a34281b834..5afed3c2ae7a1db5ebd000f43361057f161d08c8 100644 --- a/receiver/src/connection.cpp +++ b/receiver/src/connection.cpp @@ -20,16 +20,32 @@ uint64_t Connection::GetId() const noexcept { return connection_id_; } -void Connection::Listen() const noexcept { +NetworkErrorCode GetNetworkCodeFromError(const Error& err) { + if(err) { + if(err == IOErrorTemplates::kFileAlreadyExists) { + return NetworkErrorCode::kNetErrorFileIdAlreadyInUse; + } else { + return NetworkErrorCode::kNetErrorInternalServerError; + } + } + return NetworkErrorCode::kNetErrorNoError; + +} + +Error Connection::ProcessRequest(const std::unique_ptr<Request>& request) const noexcept { + Error err; + err = request->Handle(); + GenericNetworkResponse generic_response; + generic_response.error_code = GetNetworkCodeFromError(err); + if(err) { + std::cerr << "[" << GetId() << "] Error while handling request: " << err << std::endl; + } + io__->Send(socket_fd_, &generic_response, sizeof(GenericNetworkResponse), &err); + return err; +} - /* std::unique_ptr<GenericNetworkResponse> generic_response_buffer; - try { - generic_response_buffer.reset(reinterpret_cast<GenericNetworkResponse*> (new uint8_t[kRequestHandlerMaxBufferSize])); - } catch(...) { - std::cerr << "Failed to allocate buffer space for request and response" << std::endl; - return; - }*/ +void Connection::Listen() const noexcept { while(true) { Error err; auto request = WaitForNewRequest(&err); @@ -38,9 +54,9 @@ void Connection::Listen() const noexcept { break; } if (!request) continue; //no error, but timeout - err = request->Handle(); + err = ProcessRequest(request); if(err) { - std::cerr << "[" << GetId() << "] Error while handling request: " << err << std::endl; + std::cerr << "[" << GetId() << "] Error sending response: " << err << std::endl; break; } } @@ -50,23 +66,16 @@ void Connection::Listen() const noexcept { std::unique_ptr<Request> Connection::WaitForNewRequest(Error* err) const noexcept { - std::unique_ptr<GenericNetworkRequestHeader> generic_request_buffer; - try { - generic_request_buffer.reset(reinterpret_cast<GenericNetworkRequestHeader*> (new - uint8_t[kRequestHandlerMaxBufferSize])); - } catch(...) { - *err = ErrorTemplates::kMemoryAllocationError.Generate(); - return nullptr; - } - - io__->ReceiveWithTimeout(socket_fd_, generic_request_buffer.get(), sizeof(GenericNetworkRequestHeader), 50, err); + //TODO: to be overwritten with MessagePack (or similar) + GenericNetworkRequestHeader generic_request_header; + io__->ReceiveWithTimeout(socket_fd_, &generic_request_header, sizeof(GenericNetworkRequestHeader), 50, err); if(*err) { if(*err == IOErrorTemplates::kTimeout) { *err = nullptr;//Not an error in this case } return nullptr; } - return request_factory__->GenerateRequest(generic_request_buffer, socket_fd_, err); + return request_factory__->GenerateRequest(generic_request_header, socket_fd_, err); } diff --git a/receiver/src/connection.h b/receiver/src/connection.h index 6c2d348edfda913764e56cebe1f89406946de92c..23e66b199c476ac8614841eeef4e8b4ba3489a66 100644 --- a/receiver/src/connection.h +++ b/receiver/src/connection.h @@ -38,6 +38,7 @@ class Connection { private: std::unique_ptr<Request> WaitForNewRequest(Error* err) const noexcept; + Error ProcessRequest(const std::unique_ptr<Request>& request) const noexcept; }; } diff --git a/receiver/src/receiver_error.h b/receiver/src/receiver_error.h index daf1b723d2172afe6c08204ce59f730c553251db..2e74565b3f7598977062e8ed82f3d124ac019def 100644 --- a/receiver/src/receiver_error.h +++ b/receiver/src/receiver_error.h @@ -49,7 +49,6 @@ class ReceiverErrorTemplate : public SimpleErrorTemplate { namespace ReceiverErrorTemplates { auto const kInvalidOpCode = ReceiverErrorTemplate{"Invalid Opcode", ReceiverErrorType::kInvalidOpCode}; -auto const kConnectionError = SimpleErrorTemplate{"ConnectionError"}; }; } diff --git a/receiver/src/request.cpp b/receiver/src/request.cpp index 9cf4ec59c6522d72f24c0ae0ce7a533c20f8c069..74d0b59840ad6e0c2798135c688de29b8e17a2e8 100644 --- a/receiver/src/request.cpp +++ b/receiver/src/request.cpp @@ -1,43 +1,62 @@ #include "request.h" -#include "send_data_request.h" #include "system_wrappers/system_io.h" namespace hidra2 { -Request::Request(const std::unique_ptr<GenericNetworkRequestHeader>& header, - SocketDescriptor socket_fd) : io__{new SystemIO}, request_header_{*header}, socket_fd_{socket_fd} { +Request::Request(const GenericNetworkRequestHeader& header, + SocketDescriptor socket_fd) : io__{new SystemIO}, request_header_{header}, socket_fd_{socket_fd} { } +Error Request::AllocateDataBuffer() { + try { + data_buffer_.reset(new uint8_t[request_header_.data_size]); + } catch(std::exception& e) { + auto err = ErrorTemplates::kMemoryAllocationError.Generate(); + err->Append(e.what()); + return err; + } + return nullptr; +} + +Error Request::ReceiveData() { + auto err = AllocateDataBuffer(); + if (err) { + return err; + } + io__->Receive(socket_fd_, data_buffer_.get(), request_header_.data_size, &err); + return err; +} + + Error Request::Handle() { Error err; - if (request_header_.data_size != 0) { - try { - data_buffer_.reset(new uint8_t[request_header_.data_size]); - } catch(std::exception& e) { - err = ErrorTemplates::kMemoryAllocationError.Generate(); - err->Append(e.what()); + auto err = ReceiveData(); + if (err) { return err; } - - io__->Receive(socket_fd_, data_buffer_.get(), request_header_.data_size, &err); + } + for (auto handler : handlers_) { + auto err = handler->ProcessRequest(*this); if (err) { - auto recv_err = ReceiverErrorTemplates::kConnectionError.Generate(); - recv_err->Append(err->Explain()); - return recv_err; + return err; } - } return nullptr; } -std::unique_ptr<Request> RequestFactory::GenerateRequest(const std::unique_ptr<GenericNetworkRequestHeader>& +void Request::AddHandler(const RequestHandler* handler) { + handlers_.emplace_back(handler); +} + + +std::unique_ptr<Request> RequestFactory::GenerateRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd, Error* err) const noexcept { *err = nullptr; - switch (request_header->op_code) { + switch (request_header.op_code) { case Opcode::kNetOpcodeSendData: - return std::unique_ptr<Request> {new SendDataRequest{request_header, socket_fd}}; + return std::unique_ptr<Request> {new Request{request_header, socket_fd}}; default: *err = ReceiverErrorTemplates::kInvalidOpCode.Generate(); return nullptr; diff --git a/receiver/src/request.h b/receiver/src/request.h index 7cbd7cb5e60ffdd60463adc53c5ed36e6025a78e..c2da4839886e599806053f8646c87eed1b22e9af 100644 --- a/receiver/src/request.h +++ b/receiver/src/request.h @@ -4,24 +4,30 @@ #include "receiver_error.h" #include "common/networking.h" #include "system_wrappers/io.h" - +#include "request_handler.h" namespace hidra2 { +class RequestHandler; + class Request { public: virtual Error Handle(); - ~Request() = default; - Request(const std::unique_ptr<GenericNetworkRequestHeader>& request_header, SocketDescriptor socket_fd); + virtual ~Request() = default; + Request(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd); + void AddHandler(const RequestHandler*); std::unique_ptr<IO> io__; private: + Error AllocateDataBuffer(); + Error ReceiveData(); const GenericNetworkRequestHeader request_header_; const SocketDescriptor socket_fd_; FileData data_buffer_; + std::vector<const RequestHandler*> handlers_; }; class RequestFactory { public: - virtual std::unique_ptr<Request> GenerateRequest(const std::unique_ptr<GenericNetworkRequestHeader>& request_header, + virtual std::unique_ptr<Request> GenerateRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd, Error* err) const noexcept ; }; diff --git a/receiver/src/request_handler.cpp b/receiver/src/request_handler.cpp deleted file mode 100644 index 2c68d7afb9aaca8d4021b63c6bf0187f18d94f97..0000000000000000000000000000000000000000 --- a/receiver/src/request_handler.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// Created by yakubov on 20/03/18. -// - -#include "request_handler.h" - -namespace hidra2 { - -} \ No newline at end of file diff --git a/receiver/src/request_handler.h b/receiver/src/request_handler.h index 2dea833992057c871572e3be5cca1a65efa73833..a136c40eb4cf13c6b4113c02136394fabde77bcb 100644 --- a/receiver/src/request_handler.h +++ b/receiver/src/request_handler.h @@ -6,9 +6,12 @@ namespace hidra2 { +class Request; class RequestHandler { - virtual Error ProcessRequest(const Request& request) = 0; + public: + virtual Error ProcessRequest(const Request& request) const = 0; + private: }; } diff --git a/receiver/src/send_data_request.cpp b/receiver/src/send_data_request.cpp deleted file mode 100644 index ab9d65172edad342a301a5a98d3dac5926575d78..0000000000000000000000000000000000000000 --- a/receiver/src/send_data_request.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "send_data_request.h" - -namespace hidra2 { - -SendDataRequest::SendDataRequest(const std::unique_ptr<GenericNetworkRequestHeader>& header, - SocketDescriptor socket_fd) : Request(header, socket_fd) { - -} - -} \ No newline at end of file diff --git a/receiver/src/send_data_request.h b/receiver/src/send_data_request.h deleted file mode 100644 index 09b2f14054fe8f2bcba52860f8ffcdd1c55fbbac..0000000000000000000000000000000000000000 --- a/receiver/src/send_data_request.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HIDRA2_SEND_DATA_REQUEST_H -#define HIDRA2_SEND_DATA_REQUEST_H - -#include "request.h" -#include "common/networking.h" -#include "system_wrappers/io.h" - -namespace hidra2 { - -class SendDataRequest : public Request { - public: - SendDataRequest(const std::unique_ptr<GenericNetworkRequestHeader>& request_header, SocketDescriptor socket_fd); - - private: - GenericNetworkRequestHeader request_header_; -}; - -} - -#endif //HIDRA2_SEND_DATA_REQUEST_H diff --git a/receiver/unittests/test_connection.cpp b/receiver/unittests/test_connection.cpp index ef7802b4c751a2586627fb0519eff1ca45b07ee7..2343553e38f5402fa5f3575f788b32bf3119a678 100644 --- a/receiver/unittests/test_connection.cpp +++ b/receiver/unittests/test_connection.cpp @@ -14,6 +14,8 @@ using ::testing::Gt; using ::testing::Eq; using ::testing::Mock; using ::testing::NiceMock; +using ::testing::SaveArg; +using ::testing::SaveArgPointee; using ::testing::InSequence; using ::testing::SetArgPointee; using ::hidra2::Error; @@ -33,7 +35,7 @@ namespace { class MockRequest: public Request { public: - MockRequest(const std::unique_ptr<GenericNetworkRequestHeader>& request_header, SocketDescriptor socket_fd): + MockRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd): Request(request_header, socket_fd) {}; Error Handle() override { return Error{Handle_t()}; @@ -43,7 +45,7 @@ class MockRequest: public Request { class MockRequestFactory: public hidra2::RequestFactory { public: - std::unique_ptr<Request> GenerateRequest(const std::unique_ptr<GenericNetworkRequestHeader>& request_header, + std::unique_ptr<Request> GenerateRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd, Error* err) const noexcept override { ErrorInterface* error = nullptr; @@ -52,7 +54,7 @@ class MockRequestFactory: public hidra2::RequestFactory { return std::unique_ptr<Request> {res}; } - MOCK_CONST_METHOD3(GenerateRequest_t, Request * (const std::unique_ptr<GenericNetworkRequestHeader>&, + MOCK_CONST_METHOD3(GenerateRequest_t, Request * (const GenericNetworkRequestHeader&, SocketDescriptor socket_fd, ErrorInterface**)); @@ -94,8 +96,65 @@ TEST_F(ConnectionTests, ErrorWaitForNewRequest) { connection.Listen(); } +ACTION_P(SaveArg1ToGenericNetworkResponse, value) { + auto resp = *static_cast<const GenericNetworkResponse*>(arg1); + value->error_code = resp.error_code; +} + + TEST_F(ConnectionTests, CallsHandleRequest) { - std::unique_ptr<GenericNetworkRequestHeader> header{new GenericNetworkRequestHeader}; + + GenericNetworkRequestHeader header; + auto request = new MockRequest{header, 1}; + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); + + EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce( + Return(request) + ); + + EXPECT_CALL(*request, Handle_t()).WillOnce( + Return(new hidra2::SimpleError{""}) + ); + + EXPECT_CALL(mock_io, Send_t(_, _, _, _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + Return(0) + )); + + + connection.Listen(); +} + +TEST_F(ConnectionTests, SendsNoErrorToProducer) { + + GenericNetworkRequestHeader header; + auto request = new MockRequest{header, 1}; + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); + + EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce( + Return(request) + ); + + EXPECT_CALL(*request, Handle_t()).WillOnce( + Return(nullptr) + ); + GenericNetworkResponse response; + EXPECT_CALL(mock_io, Send_t(_, _, sizeof(GenericNetworkResponse), _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + SaveArg1ToGenericNetworkResponse(&response), + Return(0) + )); + + connection.Listen(); + + ASSERT_THAT(response.error_code, Eq(hidra2::NetworkErrorCode::kNetErrorNoError)); +} + +TEST_F(ConnectionTests, SendsErrorToProducer) { + + GenericNetworkRequestHeader header; auto request = new MockRequest{header, 1}; EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); @@ -108,9 +167,20 @@ TEST_F(ConnectionTests, CallsHandleRequest) { Return(new hidra2::SimpleError{""}) ); + GenericNetworkResponse response; + EXPECT_CALL(mock_io, Send_t(_, _, sizeof(GenericNetworkResponse), _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + SaveArg1ToGenericNetworkResponse(&response), + Return(0) + )); + connection.Listen(); + + ASSERT_THAT(response.error_code, Eq(hidra2::NetworkErrorCode::kNetErrorInternalServerError)); + } + /* TEST(Constructor, CheckGetAddress) { std::string expected_address = "somehost:1234"; diff --git a/receiver/unittests/test_request.cpp b/receiver/unittests/test_request.cpp index 2091231d1fe88641f6824de8848bdbbe83f1c050..0fafb5fdacfd6cf6dbcbc7fa2f5233c218396b9b 100644 --- a/receiver/unittests/test_request.cpp +++ b/receiver/unittests/test_request.cpp @@ -4,7 +4,7 @@ #include "../src/connection.h" #include "../src/receiver_error.h" #include "../src/request.h" -#include "../src/send_data_request.h" +#include "../src/request_handler.h" using ::testing::Test; using ::testing::Return; @@ -33,11 +33,21 @@ using hidra2::Request; namespace { +class MockReqestHandler : public hidra2::RequestHandler { + public: + Error ProcessRequest(const Request& request) const override { + return Error{ProcessRequest_t(request)}; + } + + MOCK_CONST_METHOD1(ProcessRequest_t, ErrorInterface * (const Request& request)); + +}; + class FactoryTests : public Test { public: hidra2::RequestFactory factory; Error err{nullptr}; - std::unique_ptr<GenericNetworkRequestHeader> generic_request_buffer{new GenericNetworkRequestHeader}; + GenericNetworkRequestHeader generic_request_header; void SetUp() override { } void TearDown() override { @@ -45,37 +55,36 @@ class FactoryTests : public Test { }; TEST_F(FactoryTests, ErrorOnWrongCode) { - generic_request_buffer->op_code = hidra2::Opcode::kNetOpcodeUnknownOp; - auto request = factory.GenerateRequest(generic_request_buffer, 1, &err); + generic_request_header.op_code = hidra2::Opcode::kNetOpcodeUnknownOp; + auto request = factory.GenerateRequest(generic_request_header, 1, &err); ASSERT_THAT(err, Ne(nullptr)); } TEST_F(FactoryTests, ReturnsSendDataRequestOnkNetOpcodeSendDataCode) { - generic_request_buffer->op_code = hidra2::Opcode::kNetOpcodeSendData; - auto request = factory.GenerateRequest(generic_request_buffer, 1, &err); + generic_request_header.op_code = hidra2::Opcode::kNetOpcodeSendData; + auto request = factory.GenerateRequest(generic_request_header, 1, &err); ASSERT_THAT(err, Eq(nullptr)); - ASSERT_THAT(dynamic_cast<hidra2::SendDataRequest*>(request.get()), Ne(nullptr)); + ASSERT_THAT(dynamic_cast<hidra2::Request*>(request.get()), Ne(nullptr)); } class RequestTests : public Test { public: - std::unique_ptr<GenericNetworkRequestHeader> generic_request_buffer{new GenericNetworkRequestHeader}; + GenericNetworkRequestHeader generic_request_header; hidra2::SocketDescriptor socket_fd_{1}; uint64_t data_size_ {100}; std::unique_ptr<Request> request; - MockIO mock_io; + NiceMock<MockIO> mock_io; void SetUp() override { - generic_request_buffer->data_size = data_size_; - request.reset(new Request{generic_request_buffer, socket_fd_}); - request->io__ = std::unique_ptr<hidra2::IO> {&mock_io};; -// ON_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)). -// WillByDefault(DoAll(testing::SetArgPointee<4>(nullptr), -// testing::Return(0))); -// EXPECT_CALL(mock_io, CloseSocket_t(_, _)); - + generic_request_header.data_size = data_size_; + request.reset(new Request{generic_request_header, socket_fd_}); + request->io__ = std::unique_ptr<hidra2::IO> {&mock_io}; + ON_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillByDefault( + DoAll(SetArgPointee<3>(nullptr), + Return(0) + )); } void TearDown() override { request->io__.release(); @@ -83,20 +92,10 @@ class RequestTests : public Test { }; -TEST_F(RequestTests, HandleReturnsErrorOnMemoryAllocation) { - generic_request_buffer->data_size = -1; - request->io__.release(); - request.reset(new Request{generic_request_buffer, socket_fd_}); - auto err = request->Handle(); - - ASSERT_THAT(err, Eq(hidra2::ErrorTemplates::kMemoryAllocationError)); - -} - TEST_F(RequestTests, HandleDoesNotReceiveEmptyData) { - generic_request_buffer->data_size = 0; + generic_request_header.data_size = 0; request->io__.release(); - request.reset(new Request{generic_request_buffer, socket_fd_}); + request.reset(new Request{generic_request_header, socket_fd_}); request->io__ = std::unique_ptr<hidra2::IO> {&mock_io};; EXPECT_CALL(mock_io, Receive_t(_, _, _, _)).Times(0); @@ -106,20 +105,34 @@ TEST_F(RequestTests, HandleDoesNotReceiveEmptyData) { ASSERT_THAT(err, Eq(nullptr)); } - - TEST_F(RequestTests, HandleReturnsErrorOnDataReceive) { EXPECT_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillOnce( - DoAll(SetArgPointee<3>(new hidra2::IOError("", hidra2::IOErrorType::kReadError)), + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Read Error", hidra2::IOErrorType::kReadError)), Return(0) )); auto err = request->Handle(); + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kReadError)); +} + + +TEST_F(RequestTests, HandleProcessesRequests) { - //socket_fd_ -// EXPECT_CALL(mock_io, Send_t(size_t(SocketDescriptor socket_fd, const void* buf, size_t length, ErrorInterface** err)); - ASSERT_THAT(err, Eq(hidra2::ReceiverErrorTemplates::kConnectionError)); + MockReqestHandler mock_request_handler; + EXPECT_CALL(mock_request_handler, ProcessRequest_t(_)).WillOnce( + Return(nullptr) + ).WillOnce( + Return(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)) + );; + + request->AddHandler(&mock_request_handler); + request->AddHandler(&mock_request_handler); + + auto err = request->Handle(); + + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError)); } + }