1/*
2 * Copyright (c) 2011-2015, Intel Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation and/or
13 * other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors
16 * may be used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#include "RemoteProcessorServer.h"
31#include <iostream>
32#include <memory>
33#include <assert.h>
34#include <string.h>
35#include "RequestMessage.h"
36#include "AnswerMessage.h"
37#include "RemoteCommandHandler.h"
38#include "Socket.h"
39
40using std::string;
41
42CRemoteProcessorServer::CRemoteProcessorServer(uint16_t uiPort)
43    : _uiPort(uiPort), _io_service(), _acceptor(_io_service), _socket(_io_service)
44{
45}
46
47CRemoteProcessorServer::~CRemoteProcessorServer()
48{
49    stop();
50}
51
52// State
53bool CRemoteProcessorServer::start(string &error)
54{
55    using namespace asio;
56
57    try {
58        ip::tcp::endpoint endpoint(ip::tcp::v6(), _uiPort);
59
60        _acceptor.open(endpoint.protocol());
61
62        _acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
63        _acceptor.set_option(asio::socket_base::linger(true, 0));
64        _acceptor.set_option(socket_base::enable_connection_aborted(true));
65
66        _acceptor.bind(endpoint);
67        _acceptor.listen();
68    } catch (std::exception &e) {
69        error = "Unable to listen on port " + std::to_string(_uiPort) + ": " + e.what();
70        return false;
71    }
72
73    return true;
74}
75
76bool CRemoteProcessorServer::stop()
77{
78    _io_service.stop();
79
80    return true;
81}
82
83void CRemoteProcessorServer::acceptRegister(IRemoteCommandHandler &commandHandler)
84{
85    auto peerHandler = [this, &commandHandler](asio::error_code ec) {
86        if (ec) {
87            std::cerr << "Accept failed: " << ec.message() << std::endl;
88            return;
89        }
90
91        _socket.set_option(asio::ip::tcp::no_delay(true));
92        handleNewConnection(commandHandler);
93
94        _socket.close();
95
96        acceptRegister(commandHandler);
97    };
98
99    _acceptor.async_accept(_socket, peerHandler);
100}
101
102bool CRemoteProcessorServer::process(IRemoteCommandHandler &commandHandler)
103{
104    acceptRegister(commandHandler);
105
106    asio::error_code ec;
107
108    _io_service.run(ec);
109
110    if (ec) {
111        std::cerr << "Server failed: " << ec.message() << std::endl;
112    }
113
114    return ec.value() == 0;
115}
116
117// New connection
118void CRemoteProcessorServer::handleNewConnection(IRemoteCommandHandler &commandHandler)
119{
120    // Process all incoming requests from the client
121    while (true) {
122
123        // Process requests
124        // Create command message
125        CRequestMessage requestMessage;
126
127        string strError;
128        ///// Receive command
129        CRequestMessage::Result res;
130        res = requestMessage.serialize(Socket(_socket), false, strError);
131
132        switch (res) {
133        case CRequestMessage::error:
134            std::cout << "Error while receiving message: " << strError << std::endl;
135        // fall through
136        case CRequestMessage::peerDisconnected:
137            // Consider peer disconnection as normal, no log
138            return; // Bail out
139        case CRequestMessage::success:
140            break; // No error, continue
141        }
142
143        // Actually process the request
144        bool bSuccess;
145
146        string strResult;
147
148        bSuccess = commandHandler.remoteCommandProcess(requestMessage, strResult);
149
150        // Send back answer
151        // Create answer message
152        CAnswerMessage answerMessage(strResult, bSuccess);
153
154        ///// Send answer
155        res = answerMessage.serialize(_socket, true, strError);
156
157        switch (res) {
158        case CRequestMessage::peerDisconnected:
159        // Peer should not disconnect while waiting for an answer
160        // Fall through to log the error and bail out
161        case CRequestMessage::error:
162            std::cout << "Error while receiving message: " << strError << std::endl;
163            return; // Bail out
164        case CRequestMessage::success:
165            break; // No error, continue
166        }
167    }
168}
169