2017-02-11 11:13:31 -06:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2017-02-12 14:54:49 -06:00
|
|
|
#include <atomic>
|
2017-02-11 11:13:31 -06:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <iostream>
|
|
|
|
#include <mutex>
|
|
|
|
#include <thread>
|
2017-02-14 05:10:52 -06:00
|
|
|
#include <assert.h>
|
2017-02-11 11:13:31 -06:00
|
|
|
|
2017-02-16 05:00:38 -06:00
|
|
|
#include <Poco/MemoryStream.h>
|
2017-02-11 11:13:31 -06:00
|
|
|
#include <Poco/Net/SocketAddress.h>
|
2017-02-16 05:00:38 -06:00
|
|
|
#include <Poco/Net/HTTPRequest.h>
|
|
|
|
#include <Poco/StringTokenizer.h>
|
2017-02-16 05:52:22 -06:00
|
|
|
#include <Poco/Runnable.h>
|
|
|
|
#include <Poco/Thread.h>
|
2017-02-16 05:00:38 -06:00
|
|
|
|
|
|
|
using Poco::MemoryInputStream;
|
|
|
|
using Poco::StringTokenizer;
|
2017-02-11 11:13:31 -06:00
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
#include "socket.hpp"
|
2017-02-11 11:13:31 -06:00
|
|
|
|
2017-02-11 14:13:14 -06:00
|
|
|
constexpr int PortNumber = 9191;
|
|
|
|
|
2017-02-14 17:45:24 -06:00
|
|
|
class SimpleResponseClient : public ClientSocket
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SimpleResponseClient(const int fd) :
|
|
|
|
ClientSocket(fd)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
virtual void handleIncomingMessage() override
|
|
|
|
{
|
|
|
|
std::cerr << "message had size " << _inBuffer.size() << "\n";
|
2017-02-16 04:14:08 -06:00
|
|
|
|
2017-02-16 05:00:38 -06:00
|
|
|
int number = 0;
|
|
|
|
MemoryInputStream message(&_inBuffer[0], _inBuffer.size());
|
|
|
|
Poco::Net::HTTPRequest req;
|
|
|
|
req.read(message);
|
|
|
|
|
|
|
|
StringTokenizer tokens(req.getURI(), "/?");
|
|
|
|
if (tokens.count() == 4)
|
2017-02-16 04:14:08 -06:00
|
|
|
{
|
2017-02-16 05:00:38 -06:00
|
|
|
std::string subpool = tokens[2];
|
|
|
|
number = std::stoi(tokens[3]);
|
2017-02-16 04:14:08 -06:00
|
|
|
}
|
2017-02-16 05:00:38 -06:00
|
|
|
else
|
|
|
|
std::cerr << " unknown tokens " << tokens.count() << std::endl;
|
|
|
|
|
|
|
|
// complex algorithmic core:
|
|
|
|
number = number + 1;
|
2017-02-16 04:14:08 -06:00
|
|
|
|
2017-02-16 05:24:07 -06:00
|
|
|
std::string numberString = std::to_string(number);
|
2017-02-14 17:45:24 -06:00
|
|
|
std::ostringstream oss;
|
|
|
|
oss << "HTTP/1.1 200 OK\r\n"
|
|
|
|
<< "Date: Once, Upon a time GMT\r\n" // Mon, 27 Jul 2009 12:28:53 GMT
|
|
|
|
<< "Server: madeup string (Linux)\r\n"
|
2017-02-16 05:24:07 -06:00
|
|
|
<< "Content-Length: " << numberString.size() << "\r\n"
|
2017-02-14 17:45:24 -06:00
|
|
|
<< "Content-Type: text/plain\r\n"
|
|
|
|
<< "Connection: Closed\r\n"
|
|
|
|
<< "\r\n"
|
2017-02-16 05:24:07 -06:00
|
|
|
<< numberString;
|
2017-02-14 17:45:24 -06:00
|
|
|
;
|
|
|
|
std::string str = oss.str();
|
|
|
|
_outBuffer.insert(_outBuffer.end(), str.begin(), str.end());
|
|
|
|
_inBuffer.clear();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-02-16 05:52:22 -06:00
|
|
|
// FIXME: use Poco Thread instead (?)
|
|
|
|
|
2017-02-12 14:54:49 -06:00
|
|
|
/// Generic thread class.
|
|
|
|
class Thread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Thread(const std::function<void(std::atomic<bool>&)>& cb) :
|
|
|
|
_cb(cb),
|
|
|
|
_stop(false)
|
|
|
|
{
|
|
|
|
_thread = std::thread([this]() { _cb(_stop); });
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread(Thread&& other) = delete;
|
|
|
|
const Thread& operator=(Thread&& other) = delete;
|
|
|
|
|
|
|
|
~Thread()
|
|
|
|
{
|
|
|
|
stop();
|
|
|
|
if (_thread.joinable())
|
|
|
|
{
|
|
|
|
_thread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
_stop = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const std::function<void(std::atomic<bool>&)> _cb;
|
|
|
|
std::atomic<bool> _stop;
|
|
|
|
std::thread _thread;
|
|
|
|
};
|
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
Poco::Net::SocketAddress addr("127.0.0.1", PortNumber);
|
2017-02-13 19:55:01 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
/// A non-blocking, streaming socket.
|
|
|
|
class ServerSocket : public Socket
|
|
|
|
{
|
|
|
|
SocketPoll& _clientPoller;
|
|
|
|
public:
|
|
|
|
ServerSocket(SocketPoll& clientPoller)
|
|
|
|
: _clientPoller(clientPoller)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Binds to a local address (Servers only).
|
|
|
|
/// Does not retry on error.
|
|
|
|
/// Returns true on success only.
|
|
|
|
bool bind(const Poco::Net::SocketAddress& address)
|
|
|
|
{
|
|
|
|
// Enable address reuse to avoid stalling after
|
|
|
|
// recycling, when previous socket is TIME_WAIT.
|
|
|
|
//TODO: Might be worth refactoring out.
|
|
|
|
const int reuseAddress = 1;
|
|
|
|
constexpr unsigned int len = sizeof(reuseAddress);
|
|
|
|
::setsockopt(getFD(), SOL_SOCKET, SO_REUSEADDR, &reuseAddress, len);
|
|
|
|
|
|
|
|
const int rc = ::bind(getFD(), address.addr(), address.length());
|
|
|
|
return (rc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Listen to incoming connections (Servers only).
|
|
|
|
/// Does not retry on error.
|
|
|
|
/// Returns true on success only.
|
|
|
|
bool listen(const int backlog = 64)
|
|
|
|
{
|
|
|
|
const int rc = ::listen(getFD(), backlog);
|
|
|
|
return (rc == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Accepts an incoming connection (Servers only).
|
|
|
|
/// Does not retry on error.
|
|
|
|
/// Returns a valid Socket shared_ptr on success only.
|
|
|
|
template <typename T>
|
|
|
|
std::shared_ptr<T> accept()
|
|
|
|
{
|
|
|
|
// Accept a connection (if any) and set it to non-blocking.
|
|
|
|
// We don't care about the client's address, so ignored.
|
|
|
|
const int rc = ::accept4(getFD(), nullptr, nullptr, SOCK_NONBLOCK);
|
|
|
|
return std::shared_ptr<T>(rc != -1 ? new T(rc) : nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
int getPollEvents() override
|
|
|
|
{
|
|
|
|
return POLLIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
HandleResult handlePoll( int /* events */ ) override
|
|
|
|
{
|
|
|
|
std::shared_ptr<SimpleResponseClient> clientSocket = accept<SimpleResponseClient>();
|
|
|
|
if (!clientSocket)
|
|
|
|
{
|
|
|
|
const std::string msg = "Failed to accept. (errno: ";
|
|
|
|
throw std::runtime_error(msg + std::strerror(errno) + ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Accepted client #" << clientSocket->getFD() << std::endl;
|
|
|
|
_clientPoller.insertNewSocket(clientSocket);
|
|
|
|
|
|
|
|
return Socket::HandleResult::CONTINUE;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void server(SocketPoll& clientPoller)
|
2017-02-13 19:55:01 -06:00
|
|
|
{
|
|
|
|
// Start server.
|
2017-02-15 08:48:48 -06:00
|
|
|
auto server = std::make_shared<ServerSocket>(clientPoller);
|
2017-02-13 19:55:01 -06:00
|
|
|
if (!server->bind(addr))
|
|
|
|
{
|
|
|
|
const std::string msg = "Failed to bind. (errno: ";
|
|
|
|
throw std::runtime_error(msg + std::strerror(errno) + ")");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!server->listen())
|
|
|
|
{
|
|
|
|
const std::string msg = "Failed to listen. (errno: ";
|
|
|
|
throw std::runtime_error(msg + std::strerror(errno) + ")");
|
|
|
|
}
|
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
SocketPoll serverPoll;
|
|
|
|
|
|
|
|
serverPoll.insertNewSocket(server);
|
|
|
|
|
2017-02-13 19:55:01 -06:00
|
|
|
std::cout << "Listening." << std::endl;
|
|
|
|
for (;;)
|
|
|
|
{
|
2017-02-15 08:48:48 -06:00
|
|
|
serverPoll.poll(30000);
|
2017-02-13 19:55:01 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 20:25:54 -06:00
|
|
|
/// Poll client sockets and do IO.
|
2017-02-15 08:48:48 -06:00
|
|
|
void pollAndComm(SocketPoll& poller, std::atomic<bool>& stop)
|
2017-02-11 22:47:41 -06:00
|
|
|
{
|
2017-02-13 20:25:54 -06:00
|
|
|
while (!stop)
|
2017-02-11 22:47:41 -06:00
|
|
|
{
|
2017-02-15 08:48:48 -06:00
|
|
|
poller.poll(5000);
|
2017-02-13 20:25:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-14 17:45:24 -06:00
|
|
|
int main(int, const char**)
|
2017-02-13 20:25:54 -06:00
|
|
|
{
|
2017-02-12 14:54:49 -06:00
|
|
|
// Used to poll client sockets.
|
2017-02-15 08:48:48 -06:00
|
|
|
SocketPoll poller;
|
2017-02-12 14:54:49 -06:00
|
|
|
|
|
|
|
// Start the client polling thread.
|
|
|
|
Thread threadPoll([&poller](std::atomic<bool>& stop)
|
|
|
|
{
|
2017-02-13 20:25:54 -06:00
|
|
|
pollAndComm(poller, stop);
|
2017-02-12 14:54:49 -06:00
|
|
|
});
|
|
|
|
|
2017-02-13 20:25:54 -06:00
|
|
|
// Start the server.
|
2017-02-13 19:55:01 -06:00
|
|
|
server(poller);
|
2017-02-11 17:21:57 -06:00
|
|
|
|
2017-02-12 14:54:49 -06:00
|
|
|
std::cout << "Shutting down server." << std::endl;
|
|
|
|
|
|
|
|
threadPoll.stop();
|
2017-02-12 13:44:14 -06:00
|
|
|
|
2017-02-11 11:13:31 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|