2017-02-13 21:16:35 -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/.
|
|
|
|
*/
|
|
|
|
|
2017-02-21 19:54:13 -06:00
|
|
|
#ifndef INCLUDED_SOCKET_HPP
|
|
|
|
#define INCLUDED_SOCKET_HPP
|
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
#include <poll.h>
|
|
|
|
#include <unistd.h>
|
2017-02-27 00:08:22 -06:00
|
|
|
#include <sys/stat.h>
|
2017-03-16 12:43:33 -05:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/tcp.h>
|
2017-02-13 21:16:35 -06:00
|
|
|
|
|
|
|
#include <atomic>
|
2017-02-23 10:57:59 -06:00
|
|
|
#include <cassert>
|
2017-02-13 21:16:35 -06:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2017-02-27 00:08:22 -06:00
|
|
|
#include <fstream>
|
2017-02-23 10:57:59 -06:00
|
|
|
#include <iostream>
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
2017-02-13 21:16:35 -06:00
|
|
|
#include <sstream>
|
2017-03-06 22:09:09 -06:00
|
|
|
#include <thread>
|
2017-03-17 15:51:45 -05:00
|
|
|
#include <chrono>
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-28 18:34:21 -06:00
|
|
|
#include <Poco/Net/HTTPResponse.h>
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-28 18:34:21 -06:00
|
|
|
#include "Common.hpp"
|
2017-02-24 10:45:31 -06:00
|
|
|
#include "Log.hpp"
|
2017-02-26 20:32:16 -06:00
|
|
|
#include "Util.hpp"
|
2017-03-08 12:14:53 -06:00
|
|
|
#include "SigUtil.hpp"
|
2017-02-24 10:45:31 -06:00
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
/// A non-blocking, streaming socket.
|
|
|
|
class Socket
|
|
|
|
{
|
|
|
|
public:
|
2017-03-10 03:55:28 -06:00
|
|
|
static const int DefaultSendBufferSize = 16 * 1024;
|
|
|
|
static const int MaximumSendBufferSize = 128 * 1024;
|
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
Socket() :
|
2017-03-10 03:55:28 -06:00
|
|
|
_fd(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)),
|
2017-04-02 16:49:14 -05:00
|
|
|
_sendBufferSize(DefaultSendBufferSize),
|
|
|
|
_owner(std::this_thread::get_id())
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-10 03:55:28 -06:00
|
|
|
init();
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
virtual ~Socket()
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << getFD() << " Socket dtor.");
|
2017-02-13 21:16:35 -06:00
|
|
|
|
|
|
|
// Doesn't block on sockets; no error handling needed.
|
|
|
|
close(_fd);
|
|
|
|
}
|
|
|
|
|
2017-02-24 16:56:20 -06:00
|
|
|
/// Returns the OS native socket fd.
|
2017-02-14 05:10:52 -06:00
|
|
|
int getFD() const { return _fd; }
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-27 07:55:42 -06:00
|
|
|
/// Shutdown the socket.
|
|
|
|
/// TODO: Support separate read/write shutdown.
|
|
|
|
virtual void shutdown()
|
|
|
|
{
|
2017-03-25 20:50:24 -05:00
|
|
|
LOG_TRC("#" << _fd << ": socket shutdown RDWR.");
|
2017-02-27 07:55:42 -06:00
|
|
|
::shutdown(_fd, SHUT_RDWR);
|
|
|
|
}
|
|
|
|
|
2017-03-17 16:59:09 -05:00
|
|
|
/// Prepare our poll record; adjust @timeoutMaxMs downwards
|
|
|
|
/// for timeouts, based on current time @now.
|
|
|
|
/// @returns POLLIN and POLLOUT if output is expected.
|
|
|
|
virtual int getPollEvents(std::chrono::steady_clock::time_point now,
|
|
|
|
int &timeoutMaxMs) = 0;
|
2017-02-24 16:56:20 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
/// Handle results of events returned from poll
|
2017-03-18 22:22:56 -05:00
|
|
|
enum class HandleResult { CONTINUE, SOCKET_CLOSED, MOVED };
|
2017-03-17 15:51:45 -05:00
|
|
|
virtual HandleResult handlePoll(std::chrono::steady_clock::time_point now, int events) = 0;
|
2017-02-15 08:48:48 -06:00
|
|
|
|
2017-02-17 20:05:07 -06:00
|
|
|
/// manage latency issues around packet aggregation
|
|
|
|
void setNoDelay(bool noDelay = true)
|
|
|
|
{
|
|
|
|
int val = noDelay ? 1 : 0;
|
|
|
|
setsockopt (_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
|
|
(char *) &val, sizeof(val));
|
|
|
|
}
|
|
|
|
|
2017-03-10 03:55:28 -06:00
|
|
|
/// Sets the kernel socket send buffer in size bytes.
|
2017-02-13 21:16:35 -06:00
|
|
|
/// Note: TCP will allocate twice this size for admin purposes,
|
|
|
|
/// so a subsequent call to getSendBufferSize will return
|
|
|
|
/// the larger (actual) buffer size, if this succeeds.
|
|
|
|
/// Note: the upper limit is set via /proc/sys/net/core/wmem_max,
|
|
|
|
/// and there is an unconfigurable lower limit as well.
|
|
|
|
/// Returns true on success only.
|
2017-03-10 03:55:28 -06:00
|
|
|
bool setSocketBufferSize(const int size)
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-10 03:55:28 -06:00
|
|
|
int rc = ::setsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
|
|
|
|
|
|
|
|
_sendBufferSize = getSocketBufferSize();
|
|
|
|
if (rc != 0 || _sendBufferSize < 0 )
|
|
|
|
{
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_ERR("#" << _fd << ": Error getting socket buffer size " << errno);
|
2017-03-10 03:55:28 -06:00
|
|
|
_sendBufferSize = DefaultSendBufferSize;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_sendBufferSize > MaximumSendBufferSize * 2)
|
|
|
|
{
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << _fd << ": Clamped send buffer size to " <<
|
|
|
|
MaximumSendBufferSize << " from " << _sendBufferSize);
|
2017-03-10 03:55:28 -06:00
|
|
|
_sendBufferSize = MaximumSendBufferSize;
|
|
|
|
}
|
|
|
|
else
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << _fd << ": Set socket buffer size to " << _sendBufferSize);
|
2017-03-10 03:55:28 -06:00
|
|
|
return true;
|
|
|
|
}
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the actual send buffer size in bytes, -1 for failure.
|
2017-03-10 03:55:28 -06:00
|
|
|
int getSocketBufferSize() const
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
|
|
|
int size;
|
|
|
|
unsigned int len = sizeof(size);
|
|
|
|
const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_SNDBUF, &size, &len);
|
2017-03-30 04:15:28 -05:00
|
|
|
return rc == 0 ? size : -1;
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-03-10 03:55:28 -06:00
|
|
|
/// Gets our fast cache of the socket buffer size
|
|
|
|
int getSendBufferSize() const
|
|
|
|
{
|
|
|
|
return _sendBufferSize;
|
|
|
|
}
|
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
/// Sets the receive buffer size in bytes.
|
|
|
|
/// Note: TCP will allocate twice this size for admin purposes,
|
2017-03-10 03:55:28 -06:00
|
|
|
/// so a subsequent call to getReceieveBufferSize will return
|
2017-02-13 21:16:35 -06:00
|
|
|
/// the larger (actual) buffer size, if this succeeds.
|
|
|
|
/// Note: the upper limit is set via /proc/sys/net/core/rmem_max,
|
|
|
|
/// and there is an unconfigurable lower limit as well.
|
|
|
|
/// Returns true on success only.
|
|
|
|
bool setReceiveBufferSize(const int size)
|
|
|
|
{
|
|
|
|
constexpr unsigned int len = sizeof(size);
|
|
|
|
const int rc = ::setsockopt(_fd, SOL_SOCKET, SO_RCVBUF, &size, len);
|
2017-03-30 04:15:28 -05:00
|
|
|
return rc == 0;
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the actual receive buffer size in bytes, -1 on error.
|
|
|
|
int getReceiveBufferSize() const
|
|
|
|
{
|
|
|
|
int size;
|
|
|
|
unsigned int len = sizeof(size);
|
|
|
|
const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_RCVBUF, &size, &len);
|
2017-03-30 04:15:28 -05:00
|
|
|
return rc == 0 ? size : -1;
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the error code.
|
|
|
|
/// Sets errno on success and returns it.
|
|
|
|
/// Returns -1 on failure to get the error code.
|
|
|
|
int getError() const
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
unsigned int len = sizeof(error);
|
|
|
|
const int rc = ::getsockopt(_fd, SOL_SOCKET, SO_ERROR, &error, &len);
|
|
|
|
if (rc == 0)
|
|
|
|
{
|
|
|
|
// Set errno so client can use strerror etc.
|
|
|
|
errno = error;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-03-12 18:03:45 -05:00
|
|
|
virtual void dumpState(std::ostream&) {}
|
2017-03-03 15:18:55 -06:00
|
|
|
|
2017-03-08 12:14:53 -06:00
|
|
|
/// Set the thread-id we're bound to
|
|
|
|
void setThreadOwner(const std::thread::id &id)
|
|
|
|
{
|
|
|
|
#if ENABLE_DEBUG
|
2017-04-02 16:38:05 -05:00
|
|
|
if (id != _owner)
|
|
|
|
{
|
|
|
|
LOG_DBG("#" << _fd << " Thread affinity set to 0x" << std::hex <<
|
|
|
|
id << " (was 0x" << _owner << ")." << std::dec);
|
|
|
|
_owner = id;
|
|
|
|
}
|
2017-03-08 12:14:53 -06:00
|
|
|
#else
|
|
|
|
(void)id;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-03-10 12:46:16 -06:00
|
|
|
virtual bool isCorrectThread(bool hard = false)
|
2017-03-08 12:14:53 -06:00
|
|
|
{
|
|
|
|
#if ENABLE_DEBUG
|
2017-03-18 21:19:41 -05:00
|
|
|
const bool sameThread = std::this_thread::get_id() == _owner;
|
|
|
|
if (!sameThread)
|
2017-04-02 16:38:05 -05:00
|
|
|
LOG_WRN("#" << _fd << " Invoked from foreign thread. Expected: 0x" << std::hex <<
|
|
|
|
_owner << " but called from 0x" << std::this_thread::get_id() << " (" <<
|
|
|
|
std::dec << Util::getThreadId() << ").");
|
|
|
|
|
2017-03-10 12:46:16 -06:00
|
|
|
if (hard)
|
|
|
|
return sameThread;
|
|
|
|
else
|
|
|
|
return !getenv("LOOL_CHECK_THREADS") || sameThread;
|
2017-03-08 12:14:53 -06:00
|
|
|
#else
|
2017-03-13 07:22:45 -05:00
|
|
|
(void)hard;
|
2017-03-08 12:14:53 -06:00
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
protected:
|
|
|
|
|
|
|
|
/// Construct based on an existing socket fd.
|
|
|
|
/// Used by accept() only.
|
|
|
|
Socket(const int fd) :
|
|
|
|
_fd(fd)
|
2017-03-08 12:14:53 -06:00
|
|
|
{
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void init()
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-02-17 20:05:07 -06:00
|
|
|
setNoDelay();
|
2017-03-10 06:11:46 -06:00
|
|
|
_sendBufferSize = DefaultSendBufferSize;
|
2017-03-08 12:14:53 -06:00
|
|
|
#if ENABLE_DEBUG
|
|
|
|
_owner = std::this_thread::get_id();
|
2017-04-02 16:49:14 -05:00
|
|
|
LOG_DBG("#" << _fd << " Thread affinity set to 0x" << std::hex <<
|
|
|
|
_owner << "." << std::dec);
|
2017-03-09 00:32:28 -06:00
|
|
|
|
2017-03-10 03:55:28 -06:00
|
|
|
const int oldSize = getSocketBufferSize();
|
|
|
|
setSocketBufferSize(0);
|
2017-04-02 16:49:14 -05:00
|
|
|
LOG_TRC("#" << _fd << ": Buffer size: " << getSendBufferSize() <<
|
|
|
|
" (was " << oldSize << ")");
|
2017-03-08 12:14:53 -06:00
|
|
|
#endif
|
2017-02-15 08:48:48 -06:00
|
|
|
}
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
private:
|
|
|
|
const int _fd;
|
2017-03-10 03:55:28 -06:00
|
|
|
int _sendBufferSize;
|
2017-03-08 12:14:53 -06:00
|
|
|
// always enabled to avoid ABI change in debug mode ...
|
|
|
|
std::thread::id _owner;
|
2017-02-15 08:48:48 -06:00
|
|
|
};
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
|
|
|
|
/// Handles non-blocking socket event polling.
|
|
|
|
/// Only polls on N-Sockets and invokes callback and
|
|
|
|
/// doesn't manage buffers or client data.
|
|
|
|
/// Note: uses poll(2) since it has very good performance
|
|
|
|
/// compared to epoll up to a few hundred sockets and
|
|
|
|
/// doesn't suffer select(2)'s poor API. Since this will
|
|
|
|
/// be used per-document we don't expect to have several
|
|
|
|
/// hundred users on same document to suffer poll(2)'s
|
|
|
|
/// scalability limit. Meanwhile, epoll(2)'s high
|
|
|
|
/// overhead to adding/removing sockets is not helpful.
|
|
|
|
class SocketPoll
|
|
|
|
{
|
2017-03-02 12:12:52 -06:00
|
|
|
public:
|
|
|
|
/// Create a socket poll, called rather infrequently.
|
2017-03-09 13:00:04 -06:00
|
|
|
SocketPoll(const std::string& threadName);
|
2017-03-02 12:12:52 -06:00
|
|
|
~SocketPoll();
|
2017-02-15 08:48:48 -06:00
|
|
|
|
2017-03-09 14:35:04 -06:00
|
|
|
/// Default poll time - useful to increase for debugging.
|
|
|
|
static int DefaultPollTimeoutMs;
|
|
|
|
|
2017-03-06 22:09:09 -06:00
|
|
|
/// Stop the polling thread.
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
_stop = true;
|
2017-03-08 12:14:53 -06:00
|
|
|
wakeup();
|
2017-03-06 22:09:09 -06:00
|
|
|
}
|
|
|
|
|
2017-03-12 21:11:25 -05:00
|
|
|
bool isAlive() const { return _threadStarted && !_threadFinished; }
|
|
|
|
|
2017-03-07 11:08:54 -06:00
|
|
|
/// Check if we should continue polling
|
|
|
|
virtual bool continuePolling()
|
|
|
|
{
|
|
|
|
return !_stop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Executed inside the poll in case of a wakeup
|
|
|
|
virtual void wakeupHook() {}
|
|
|
|
|
2017-03-07 11:34:01 -06:00
|
|
|
/// The default implementation of our polling thread
|
|
|
|
virtual void pollingThread()
|
|
|
|
{
|
|
|
|
while (continuePolling())
|
|
|
|
{
|
2017-03-09 14:35:04 -06:00
|
|
|
poll(DefaultPollTimeoutMs);
|
2017-03-07 11:34:01 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-08 12:14:53 -06:00
|
|
|
/// Are we running in either shutdown, or the polling thread.
|
2017-03-26 22:08:10 -05:00
|
|
|
bool isCorrectThread() const
|
2017-03-08 12:14:53 -06:00
|
|
|
{
|
2017-03-29 20:01:01 -05:00
|
|
|
if (std::this_thread::get_id() != _owner)
|
2017-04-02 16:38:05 -05:00
|
|
|
LOG_WRN("Incorrect thread affinity for " << _name << ". Expected: 0x" << std::hex <<
|
|
|
|
_owner << " (" << std::dec << Util::getThreadId() << ") but called from 0x" <<
|
|
|
|
std::hex << std::this_thread::get_id() << std::dec << ", stop: " << _stop);
|
2017-03-29 20:01:01 -05:00
|
|
|
|
2017-03-09 12:19:53 -06:00
|
|
|
return _stop || std::this_thread::get_id() == _owner;
|
2017-03-08 12:14:53 -06:00
|
|
|
}
|
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
/// Poll the sockets for available data to read or buffer to write.
|
2017-03-17 15:51:45 -05:00
|
|
|
void poll(int timeoutMaxMs)
|
2017-02-15 08:48:48 -06:00
|
|
|
{
|
2017-03-08 12:14:53 -06:00
|
|
|
assert(isCorrectThread());
|
|
|
|
|
2017-03-17 15:51:45 -05:00
|
|
|
std::chrono::steady_clock::time_point now =
|
|
|
|
std::chrono::steady_clock::now();
|
2017-02-24 16:56:20 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
// The events to poll on change each spin of the loop.
|
2017-03-17 15:51:45 -05:00
|
|
|
setupPollFds(now, timeoutMaxMs);
|
2017-03-07 22:13:56 -06:00
|
|
|
const size_t size = _pollSockets.size();
|
2017-02-15 08:48:48 -06:00
|
|
|
|
|
|
|
int rc;
|
|
|
|
do
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-17 17:59:03 -05:00
|
|
|
rc = ::poll(&_pollFds[0], size + 1, std::max(timeoutMaxMs,0));
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
2017-02-15 08:48:48 -06:00
|
|
|
while (rc < 0 && errno == EINTR);
|
2017-04-02 16:49:14 -05:00
|
|
|
LOG_TRC("Poll completed with " << rc << " live polls max (" <<
|
|
|
|
timeoutMaxMs << "ms)" << ((rc==0) ? "(timedout)" : ""));
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
// Fire the callback and remove dead fds.
|
2017-03-17 15:51:45 -05:00
|
|
|
std::chrono::steady_clock::time_point newNow =
|
|
|
|
std::chrono::steady_clock::now();
|
2017-02-15 08:48:48 -06:00
|
|
|
for (int i = static_cast<int>(size) - 1; i >= 0; --i)
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-17 13:56:59 -05:00
|
|
|
Socket::HandleResult res = Socket::HandleResult::SOCKET_CLOSED;
|
|
|
|
try
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-17 13:56:59 -05:00
|
|
|
res = _pollSockets[i]->handlePoll(newNow, _pollFds[i].revents);
|
|
|
|
}
|
|
|
|
catch (const std::exception& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR("Error while handling poll for socket #" <<
|
|
|
|
_pollFds[i].fd << " in " << _name << ": " << exc.what());
|
|
|
|
}
|
|
|
|
|
2017-03-20 21:59:08 -05:00
|
|
|
if (res == Socket::HandleResult::SOCKET_CLOSED ||
|
|
|
|
res == Socket::HandleResult::MOVED)
|
2017-03-17 13:56:59 -05:00
|
|
|
{
|
|
|
|
LOG_DBG("Removing socket #" << _pollFds[i].fd << " (of " <<
|
|
|
|
_pollSockets.size() << ") from " << _name);
|
|
|
|
_pollSockets.erase(_pollSockets.begin() + i);
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
2017-02-15 08:48:48 -06:00
|
|
|
}
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
// Process the wakeup pipe (always the last entry).
|
|
|
|
if (_pollFds[size].revents)
|
|
|
|
{
|
2017-02-24 17:14:09 -06:00
|
|
|
std::vector<CallbackFn> invoke;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
|
|
|
|
// Clear the data.
|
|
|
|
int dump = ::read(_wakeup[0], &dump, sizeof(dump));
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-24 17:14:09 -06:00
|
|
|
// Copy the new sockets over and clear.
|
|
|
|
_pollSockets.insert(_pollSockets.end(),
|
|
|
|
_newSockets.begin(), _newSockets.end());
|
2017-03-10 12:20:51 -06:00
|
|
|
|
|
|
|
// Update thread ownership.
|
|
|
|
for (auto &i : _newSockets)
|
|
|
|
i->setThreadOwner(std::this_thread::get_id());
|
|
|
|
|
2017-02-24 17:14:09 -06:00
|
|
|
_newSockets.clear();
|
|
|
|
|
|
|
|
// Extract list of callbacks to process
|
|
|
|
std::swap(_newCallbacks, invoke);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < invoke.size(); ++i)
|
|
|
|
invoke[i]();
|
2017-03-07 11:08:54 -06:00
|
|
|
|
|
|
|
wakeupHook();
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 12:12:52 -06:00
|
|
|
/// Write to a wakeup descriptor
|
|
|
|
static void wakeup (int fd)
|
2017-02-24 17:14:09 -06:00
|
|
|
{
|
|
|
|
// wakeup the main-loop.
|
|
|
|
int rc;
|
|
|
|
do {
|
2017-03-02 12:12:52 -06:00
|
|
|
rc = ::write(fd, "w", 1);
|
2017-02-24 17:14:09 -06:00
|
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
|
2017-03-18 09:44:07 -05:00
|
|
|
if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
|
2017-03-17 13:56:59 -05:00
|
|
|
LOG_WRN("wakeup socket #" << fd << " is closd at wakeup? error: " << errno);
|
2017-02-24 17:14:09 -06:00
|
|
|
}
|
|
|
|
|
2017-03-02 12:12:52 -06:00
|
|
|
/// Wakeup the main polling loop in another thread
|
|
|
|
void wakeup()
|
|
|
|
{
|
|
|
|
wakeup(_wakeup[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Global wakeup - signal safe: wakeup all socket polls.
|
2017-03-03 15:18:27 -06:00
|
|
|
static void wakeupWorld();
|
2017-03-02 12:12:52 -06:00
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
/// Insert a new socket to be polled.
|
|
|
|
/// Sockets are removed only when the handler return false.
|
|
|
|
void insertNewSocket(const std::shared_ptr<Socket>& newSocket)
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-02-27 20:40:44 -06:00
|
|
|
if (newSocket)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
2017-03-10 12:20:51 -06:00
|
|
|
// Beware - _thread may not be created & started yet.
|
2017-03-08 12:14:53 -06:00
|
|
|
newSocket->setThreadOwner(_thread.get_id());
|
2017-03-07 01:00:51 -06:00
|
|
|
LOG_DBG("Inserting socket #" << newSocket->getFD() << " into " << _name);
|
2017-02-27 20:40:44 -06:00
|
|
|
_newSockets.emplace_back(newSocket);
|
|
|
|
wakeup();
|
|
|
|
}
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-02-24 17:14:09 -06:00
|
|
|
typedef std::function<void()> CallbackFn;
|
2017-02-15 08:48:48 -06:00
|
|
|
|
2017-02-24 17:14:09 -06:00
|
|
|
/// Add a callback to be invoked in the polling thread
|
|
|
|
void addCallback(CallbackFn fn)
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-02-15 08:48:48 -06:00
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
2017-02-24 17:14:09 -06:00
|
|
|
_newCallbacks.emplace_back(fn);
|
|
|
|
wakeup();
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-03-15 11:13:13 -05:00
|
|
|
virtual void dumpState(std::ostream& os);
|
2017-03-03 15:18:55 -06:00
|
|
|
|
2017-03-07 01:00:51 -06:00
|
|
|
/// Removes a socket from this poller.
|
2017-03-10 12:46:31 -06:00
|
|
|
/// NB. this must be called from the socket poll that
|
|
|
|
/// owns the socket.
|
2017-03-07 01:00:51 -06:00
|
|
|
void releaseSocket(const std::shared_ptr<Socket>& socket)
|
|
|
|
{
|
2017-03-10 12:46:31 -06:00
|
|
|
assert(socket);
|
|
|
|
assert(isCorrectThread());
|
|
|
|
assert(socket->isCorrectThread(true));
|
|
|
|
auto it = std::find(_pollSockets.begin(), _pollSockets.end(), socket);
|
|
|
|
assert(it != _pollSockets.end());
|
|
|
|
|
|
|
|
_pollSockets.erase(it);
|
2017-03-12 17:34:06 -05:00
|
|
|
LOG_TRC("Release socket #" << socket->getFD() << " from " << _name <<
|
|
|
|
" leaving " << _pollSockets.size());
|
2017-03-07 01:00:51 -06:00
|
|
|
}
|
|
|
|
|
2017-03-26 22:10:24 -05:00
|
|
|
size_t getSocketCount() const
|
|
|
|
{
|
|
|
|
assert(isCorrectThread());
|
|
|
|
return _pollSockets.size();
|
|
|
|
}
|
|
|
|
|
2017-03-07 01:00:51 -06:00
|
|
|
const std::string& name() const { return _name; }
|
|
|
|
|
2017-03-12 21:30:30 -05:00
|
|
|
/// Start the polling thread (if desired)
|
|
|
|
void startThread();
|
2017-02-24 17:14:09 -06:00
|
|
|
|
2017-03-31 11:28:20 -05:00
|
|
|
/// Stop and join the polling thread before returning (if active)
|
|
|
|
void joinThread();
|
|
|
|
|
2017-03-12 21:30:30 -05:00
|
|
|
private:
|
2017-02-15 08:48:48 -06:00
|
|
|
/// Initialize the poll fds array with the right events
|
2017-03-17 15:51:45 -05:00
|
|
|
void setupPollFds(std::chrono::steady_clock::time_point now,
|
|
|
|
int &timeoutMaxMs)
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-02-15 08:48:48 -06:00
|
|
|
const size_t size = _pollSockets.size();
|
|
|
|
|
|
|
|
_pollFds.resize(size + 1); // + wakeup pipe
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; ++i)
|
|
|
|
{
|
|
|
|
_pollFds[i].fd = _pollSockets[i]->getFD();
|
2017-03-17 16:59:09 -05:00
|
|
|
_pollFds[i].events = _pollSockets[i]->getPollEvents(now, timeoutMaxMs);
|
2017-02-15 08:48:48 -06:00
|
|
|
_pollFds[i].revents = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the read-end of the wake pipe.
|
|
|
|
_pollFds[size].fd = _wakeup[0];
|
|
|
|
_pollFds[size].events = POLLIN;
|
|
|
|
_pollFds[size].revents = 0;
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-03-14 21:15:01 -05:00
|
|
|
/// The polling thread entry.
|
|
|
|
/// Used to set the thread name and mark the thread as stopped when done.
|
|
|
|
void pollingThreadEntry()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Util::setThreadName(_name);
|
|
|
|
LOG_INF("Starting polling thread [" << _name << "].");
|
|
|
|
|
|
|
|
// Invoke the virtual implementation.
|
|
|
|
pollingThread();
|
|
|
|
}
|
|
|
|
catch (const std::exception& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR("Exception in polling thread [" << _name << "]: " << exc.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
_threadFinished = true;
|
|
|
|
}
|
|
|
|
|
2017-02-13 21:16:35 -06:00
|
|
|
private:
|
2017-03-06 22:09:09 -06:00
|
|
|
/// Debug name used for logging.
|
|
|
|
const std::string _name;
|
|
|
|
|
2017-02-15 08:48:48 -06:00
|
|
|
/// main-loop wakeup pipe
|
|
|
|
int _wakeup[2];
|
|
|
|
/// The sockets we're controlling
|
|
|
|
std::vector<std::shared_ptr<Socket>> _pollSockets;
|
|
|
|
/// Protects _newSockets
|
|
|
|
std::mutex _mutex;
|
|
|
|
std::vector<std::shared_ptr<Socket>> _newSockets;
|
2017-02-24 17:14:09 -06:00
|
|
|
std::vector<CallbackFn> _newCallbacks;
|
2017-02-15 08:48:48 -06:00
|
|
|
/// The fds to poll.
|
|
|
|
std::vector<pollfd> _pollFds;
|
2017-03-06 22:09:09 -06:00
|
|
|
|
2017-03-07 11:34:01 -06:00
|
|
|
protected:
|
2017-03-06 22:09:09 -06:00
|
|
|
/// Flag the thread to stop.
|
|
|
|
std::atomic<bool> _stop;
|
|
|
|
/// The polling thread.
|
|
|
|
std::thread _thread;
|
2017-03-09 13:23:21 -06:00
|
|
|
std::atomic<bool> _threadStarted;
|
2017-03-12 21:11:25 -05:00
|
|
|
std::atomic<bool> _threadFinished;
|
2017-03-09 12:19:53 -06:00
|
|
|
std::thread::id _owner;
|
2017-02-13 21:16:35 -06:00
|
|
|
};
|
|
|
|
|
2017-02-22 10:12:12 -06:00
|
|
|
class StreamSocket;
|
|
|
|
|
|
|
|
/// Interface that handles the actual incoming message.
|
2017-02-23 06:14:01 -06:00
|
|
|
class SocketHandlerInterface
|
2017-02-22 10:12:12 -06:00
|
|
|
{
|
|
|
|
public:
|
2017-02-25 12:38:51 -06:00
|
|
|
/// Called when the socket is newly created to
|
|
|
|
/// set the socket associated with this ResponseClient.
|
|
|
|
/// Will be called exactly once.
|
2017-03-26 22:06:44 -05:00
|
|
|
virtual void onConnect(const std::shared_ptr<StreamSocket>& socket) = 0;
|
2017-02-22 10:12:12 -06:00
|
|
|
|
2017-03-18 22:22:56 -05:00
|
|
|
enum class SocketOwnership
|
|
|
|
{
|
|
|
|
UNCHANGED, //< Same socket poll, business as usual.
|
|
|
|
MOVED //< The socket poll is now different.
|
|
|
|
};
|
|
|
|
|
2017-02-22 10:12:12 -06:00
|
|
|
/// Called after successful socket reads.
|
2017-03-18 22:22:56 -05:00
|
|
|
virtual SocketHandlerInterface::SocketOwnership handleIncomingMessage() = 0;
|
2017-02-25 12:38:51 -06:00
|
|
|
|
2017-03-17 16:59:09 -05:00
|
|
|
/// Prepare our poll record; adjust @timeoutMaxMs downwards
|
|
|
|
/// for timeouts, based on current time @now.
|
|
|
|
/// @returns POLLIN and POLLOUT if output is expected.
|
|
|
|
virtual int getPollEvents(std::chrono::steady_clock::time_point now,
|
|
|
|
int &timeoutMaxMs) = 0;
|
2017-03-06 10:20:03 -06:00
|
|
|
|
2017-03-17 17:59:03 -05:00
|
|
|
/// Do we need to handle a timeout ?
|
|
|
|
virtual void checkTimeout(std::chrono::steady_clock::time_point /* now */) {}
|
|
|
|
|
2017-03-06 10:20:03 -06:00
|
|
|
/// Do some of the queued writing.
|
2017-03-06 10:26:52 -06:00
|
|
|
virtual void performWrites() = 0;
|
2017-03-06 10:20:03 -06:00
|
|
|
|
2017-02-25 12:38:51 -06:00
|
|
|
/// Called when the is disconnected and will be destroyed.
|
|
|
|
/// Will be called exactly once.
|
2017-03-18 09:59:09 -05:00
|
|
|
virtual void onDisconnect() {}
|
|
|
|
|
|
|
|
/// Append pretty printed internal state to a line
|
|
|
|
virtual void dumpState(std::ostream& os) { os << "\n"; }
|
2017-02-22 10:12:12 -06:00
|
|
|
};
|
|
|
|
|
2017-02-22 08:45:41 -06:00
|
|
|
/// A plain, non-blocking, data streaming socket.
|
2017-03-13 20:59:50 -05:00
|
|
|
class StreamSocket : public Socket, public std::enable_shared_from_this<StreamSocket>
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
|
|
|
public:
|
2017-02-25 12:38:51 -06:00
|
|
|
/// Create a StreamSocket from native FD and take ownership of handler instance.
|
2017-03-14 19:49:17 -05:00
|
|
|
StreamSocket(const int fd, std::shared_ptr<SocketHandlerInterface> socketHandler) :
|
2017-02-22 10:12:12 -06:00
|
|
|
Socket(fd),
|
2017-02-25 13:08:03 -06:00
|
|
|
_socketHandler(std::move(socketHandler)),
|
2017-03-09 10:20:34 -06:00
|
|
|
_closed(false),
|
|
|
|
_shutdownSignalled(false)
|
2017-02-22 10:12:12 -06:00
|
|
|
{
|
2017-03-04 11:06:23 -06:00
|
|
|
LOG_DBG("StreamSocket ctor #" << fd);
|
|
|
|
|
|
|
|
// Without a handler we make no sense object.
|
2017-02-25 12:38:51 -06:00
|
|
|
if (!_socketHandler)
|
|
|
|
throw std::runtime_error("StreamSocket expects a valid SocketHandler instance.");
|
|
|
|
}
|
|
|
|
|
|
|
|
~StreamSocket()
|
|
|
|
{
|
2017-03-04 11:06:23 -06:00
|
|
|
LOG_DBG("StreamSocket dtor #" << getFD());
|
|
|
|
|
2017-02-25 12:38:51 -06:00
|
|
|
if (!_closed)
|
|
|
|
_socketHandler->onDisconnect();
|
2017-03-09 10:20:34 -06:00
|
|
|
|
|
|
|
if (!_shutdownSignalled)
|
|
|
|
{
|
|
|
|
_shutdownSignalled = true;
|
|
|
|
closeConnection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Just trigger the async shutdown.
|
2017-03-10 03:54:08 -06:00
|
|
|
virtual void shutdown() override
|
2017-03-09 10:20:34 -06:00
|
|
|
{
|
|
|
|
_shutdownSignalled = true;
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << getFD() << ": Async shutdown requested.");
|
2017-03-09 10:20:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform the real shutdown.
|
|
|
|
virtual void closeConnection()
|
|
|
|
{
|
|
|
|
Socket::shutdown();
|
2017-02-22 10:12:12 -06:00
|
|
|
}
|
2017-02-19 13:47:25 -06:00
|
|
|
|
2017-03-17 16:59:09 -05:00
|
|
|
int getPollEvents(std::chrono::steady_clock::time_point now,
|
|
|
|
int &timeoutMaxMs) override
|
2017-02-26 20:32:16 -06:00
|
|
|
{
|
2017-03-17 16:59:09 -05:00
|
|
|
// cf. SslSocket::getPollEvents
|
2017-03-12 18:04:52 -05:00
|
|
|
assert(isCorrectThread());
|
2017-03-17 16:59:09 -05:00
|
|
|
int events = _socketHandler->getPollEvents(now, timeoutMaxMs);
|
|
|
|
if (!_outBuffer.empty() || _shutdownSignalled)
|
|
|
|
events |= POLLOUT;
|
|
|
|
return events;
|
2017-02-26 20:32:16 -06:00
|
|
|
}
|
|
|
|
|
2017-02-26 23:41:22 -06:00
|
|
|
/// Send data to the socket peer.
|
|
|
|
void send(const char* data, const int len, const bool flush = true)
|
|
|
|
{
|
2017-03-31 06:25:21 -05:00
|
|
|
assert(isCorrectThread(true));
|
2017-02-26 23:41:22 -06:00
|
|
|
if (data != nullptr && len > 0)
|
|
|
|
{
|
|
|
|
_outBuffer.insert(_outBuffer.end(), data, data + len);
|
|
|
|
if (flush)
|
|
|
|
writeOutgoingData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Send a string to the socket peer.
|
|
|
|
void send(const std::string& str, const bool flush = true)
|
|
|
|
{
|
|
|
|
send(str.data(), str.size(), flush);
|
|
|
|
}
|
|
|
|
|
2017-03-09 10:20:34 -06:00
|
|
|
/// Sends HTTP response.
|
2017-03-13 18:38:19 -05:00
|
|
|
void send(Poco::Net::HTTPResponse& response)
|
2017-03-01 00:49:10 -06:00
|
|
|
{
|
|
|
|
response.set("User-Agent", HTTP_AGENT_STRING);
|
|
|
|
std::ostringstream oss;
|
|
|
|
response.write(oss);
|
2017-03-13 18:38:19 -05:00
|
|
|
send(oss.str());
|
2017-03-01 00:49:10 -06:00
|
|
|
}
|
|
|
|
|
2017-02-27 20:40:44 -06:00
|
|
|
/// Reads data by invoking readData() and buffering.
|
|
|
|
/// Return false iff the socket is closed.
|
|
|
|
virtual bool readIncomingData()
|
|
|
|
{
|
2017-03-08 12:14:53 -06:00
|
|
|
assert(isCorrectThread());
|
|
|
|
|
2017-02-27 20:40:44 -06:00
|
|
|
// SSL decodes blocks of 16Kb, so for efficiency we use the same.
|
|
|
|
char buf[16 * 1024];
|
|
|
|
ssize_t len;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// Drain the read buffer.
|
|
|
|
// TODO: Cap the buffer size, lest we grow beyond control.
|
|
|
|
do
|
|
|
|
{
|
|
|
|
len = readData(buf, sizeof(buf));
|
|
|
|
}
|
|
|
|
while (len < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
assert (len <= ssize_t(sizeof(buf)));
|
|
|
|
_inBuffer.insert(_inBuffer.end(), &buf[0], &buf[len]);
|
|
|
|
}
|
|
|
|
// else poll will handle errors.
|
|
|
|
}
|
|
|
|
while (len == (sizeof(buf)));
|
|
|
|
|
|
|
|
return len != 0; // zero is eof / clean socket close.
|
|
|
|
}
|
|
|
|
|
2017-03-13 20:59:50 -05:00
|
|
|
/// Replace the existing SocketHandler with a new one.
|
2017-03-14 19:49:17 -05:00
|
|
|
void setHandler(std::shared_ptr<SocketHandlerInterface> handler)
|
2017-03-13 20:59:50 -05:00
|
|
|
{
|
|
|
|
_socketHandler = std::move(handler);
|
|
|
|
_socketHandler->onConnect(shared_from_this());
|
|
|
|
}
|
|
|
|
|
2017-02-26 20:32:16 -06:00
|
|
|
/// Create a socket of type TSocket given an FD and a handler.
|
|
|
|
/// We need this helper since the handler needs a shared_ptr to the socket
|
|
|
|
/// but we can't have a shared_ptr in the ctor.
|
|
|
|
template <typename TSocket>
|
|
|
|
static
|
2017-03-14 19:49:17 -05:00
|
|
|
std::shared_ptr<TSocket> create(const int fd, std::shared_ptr<SocketHandlerInterface> handler)
|
2017-02-26 20:32:16 -06:00
|
|
|
{
|
|
|
|
SocketHandlerInterface* pHandler = handler.get();
|
|
|
|
auto socket = std::make_shared<TSocket>(fd, std::move(handler));
|
|
|
|
pHandler->onConnect(socket);
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
2017-02-19 13:47:25 -06:00
|
|
|
/// Called when a polling event is received.
|
|
|
|
/// @events is the mask of events that triggered the wake.
|
2017-03-17 17:59:03 -05:00
|
|
|
HandleResult handlePoll(std::chrono::steady_clock::time_point now,
|
2017-02-24 16:56:20 -06:00
|
|
|
const int events) override
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-31 06:25:21 -05:00
|
|
|
assert(isCorrectThread(true));
|
2017-03-08 12:14:53 -06:00
|
|
|
|
2017-03-17 17:59:03 -05:00
|
|
|
_socketHandler->checkTimeout(now);
|
|
|
|
|
2017-03-17 13:56:59 -05:00
|
|
|
if (!events)
|
|
|
|
return Socket::HandleResult::CONTINUE;
|
|
|
|
|
2017-02-19 13:47:25 -06:00
|
|
|
// FIXME: need to close input, but not output (?)
|
2017-02-26 14:02:20 -06:00
|
|
|
bool closed = (events & (POLLHUP | POLLERR | POLLNVAL));
|
|
|
|
|
2017-02-19 13:47:25 -06:00
|
|
|
// Always try to read.
|
2017-02-26 14:02:20 -06:00
|
|
|
closed = !readIncomingData() || closed;
|
2017-02-19 13:47:25 -06:00
|
|
|
|
2017-02-24 10:45:31 -06:00
|
|
|
auto& log = Log::logger();
|
|
|
|
if (log.trace()) {
|
2017-02-27 20:35:06 -06:00
|
|
|
LOG_TRC("#" << getFD() << ": Incoming data buffer " << _inBuffer.size() <<
|
|
|
|
" bytes, closeSocket? " << closed);
|
2017-03-01 20:24:17 -06:00
|
|
|
// log.dump("", &_inBuffer[0], _inBuffer.size());
|
2017-02-24 10:45:31 -06:00
|
|
|
}
|
|
|
|
|
2017-02-19 13:47:25 -06:00
|
|
|
// If we have data, allow the app to consume.
|
|
|
|
size_t oldSize = 0;
|
|
|
|
while (!_inBuffer.empty() && oldSize != _inBuffer.size())
|
2017-02-18 14:27:27 -06:00
|
|
|
{
|
2017-02-19 13:47:25 -06:00
|
|
|
oldSize = _inBuffer.size();
|
2017-03-18 22:22:56 -05:00
|
|
|
if (_socketHandler->handleIncomingMessage() == SocketHandlerInterface::SocketOwnership::MOVED)
|
|
|
|
return Socket::HandleResult::MOVED;
|
2017-02-18 14:27:27 -06:00
|
|
|
}
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-03-12 21:29:30 -05:00
|
|
|
do
|
|
|
|
{
|
|
|
|
// If we have space for writing and that was requested
|
|
|
|
if ((events & POLLOUT) && _outBuffer.empty())
|
|
|
|
_socketHandler->performWrites();
|
2017-03-06 10:20:03 -06:00
|
|
|
|
2017-03-12 21:29:30 -05:00
|
|
|
// perform the shutdown if we have sent everything.
|
|
|
|
if (_shutdownSignalled && _outBuffer.empty())
|
|
|
|
{
|
|
|
|
closeConnection();
|
|
|
|
closed = true;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-09 10:20:34 -06:00
|
|
|
|
2017-03-12 21:29:30 -05:00
|
|
|
oldSize = _outBuffer.size();
|
2017-02-26 20:32:16 -06:00
|
|
|
|
2017-03-12 21:29:30 -05:00
|
|
|
// Write if we can and have data to write.
|
2017-03-31 06:25:21 -05:00
|
|
|
if ((events & POLLOUT) && !_outBuffer.empty())
|
2017-03-12 21:29:30 -05:00
|
|
|
{
|
2017-03-31 06:25:21 -05:00
|
|
|
writeOutgoingData();
|
2017-03-12 21:29:30 -05:00
|
|
|
closed = closed || (errno == EPIPE);
|
|
|
|
}
|
2017-02-19 13:47:25 -06:00
|
|
|
}
|
2017-03-12 21:29:30 -05:00
|
|
|
while (oldSize != _outBuffer.size());
|
2017-02-13 21:16:35 -06:00
|
|
|
|
2017-02-26 14:02:20 -06:00
|
|
|
if (closed)
|
2017-02-25 12:38:51 -06:00
|
|
|
{
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << getFD() << ": Closed. Firing onDisconnect.");
|
2017-02-25 12:38:51 -06:00
|
|
|
_closed = true;
|
|
|
|
_socketHandler->onDisconnect();
|
|
|
|
}
|
2017-02-15 08:48:48 -06:00
|
|
|
|
2017-02-25 12:38:51 -06:00
|
|
|
return _closed ? HandleResult::SOCKET_CLOSED :
|
|
|
|
HandleResult::CONTINUE;
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
2017-02-15 08:48:48 -06:00
|
|
|
|
2017-02-18 14:35:29 -06:00
|
|
|
/// Override to write data out to socket.
|
|
|
|
virtual void writeOutgoingData()
|
2017-02-13 21:16:35 -06:00
|
|
|
{
|
2017-03-31 06:25:21 -05:00
|
|
|
assert(isCorrectThread(true));
|
2017-02-20 22:18:54 -06:00
|
|
|
assert(!_outBuffer.empty());
|
2017-02-21 19:34:37 -06:00
|
|
|
do
|
2017-02-18 14:35:29 -06:00
|
|
|
{
|
2017-02-19 13:47:05 -06:00
|
|
|
ssize_t len;
|
|
|
|
do
|
|
|
|
{
|
2017-03-10 03:55:28 -06:00
|
|
|
// Writing more than we can absorb in the kernel causes SSL wasteage.
|
|
|
|
len = writeData(&_outBuffer[0], std::min((int)_outBuffer.size(),
|
|
|
|
getSendBufferSize()));
|
2017-02-24 10:45:31 -06:00
|
|
|
|
|
|
|
auto& log = Log::logger();
|
2017-03-01 20:24:17 -06:00
|
|
|
if (log.trace() && len > 0) {
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_TRC("#" << getFD() << ": Wrote outgoing data " << len << " bytes.");
|
2017-03-01 20:24:17 -06:00
|
|
|
// log.dump("", &_outBuffer[0], len);
|
2017-02-24 10:45:31 -06:00
|
|
|
}
|
2017-03-01 20:24:17 -06:00
|
|
|
|
2017-03-30 04:21:10 -05:00
|
|
|
if (len <= 0 && errno != EAGAIN && errno != EWOULDBLOCK)
|
2017-03-14 21:14:20 -05:00
|
|
|
LOG_SYS("#" << getFD() << ": Wrote outgoing data " << len << " bytes.");
|
2017-02-19 13:47:05 -06:00
|
|
|
}
|
|
|
|
while (len < 0 && errno == EINTR);
|
2017-02-18 14:35:29 -06:00
|
|
|
|
2017-02-19 13:47:05 -06:00
|
|
|
if (len > 0)
|
|
|
|
{
|
2017-02-21 19:34:37 -06:00
|
|
|
_outBuffer.erase(_outBuffer.begin(), _outBuffer.begin() + len);
|
2017-02-19 13:47:05 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Poll will handle errors.
|
|
|
|
break;
|
|
|
|
}
|
2017-02-14 17:45:24 -06:00
|
|
|
}
|
2017-02-21 19:34:37 -06:00
|
|
|
while (!_outBuffer.empty());
|
2017-02-13 21:16:35 -06:00
|
|
|
}
|
|
|
|
|
2017-02-18 14:35:29 -06:00
|
|
|
/// Override to handle reading of socket data differently.
|
2017-02-22 08:45:41 -06:00
|
|
|
virtual int readData(char* buf, int len)
|
|
|
|
{
|
2017-03-08 12:14:53 -06:00
|
|
|
assert(isCorrectThread());
|
2017-02-22 08:45:41 -06:00
|
|
|
return ::read(getFD(), buf, len);
|
|
|
|
}
|
2017-02-18 14:35:29 -06:00
|
|
|
|
|
|
|
/// Override to handle writing data to socket differently.
|
2017-02-22 08:45:41 -06:00
|
|
|
virtual int writeData(const char* buf, const int len)
|
|
|
|
{
|
2017-03-08 12:14:53 -06:00
|
|
|
assert(isCorrectThread());
|
2017-02-22 08:45:41 -06:00
|
|
|
return ::write(getFD(), buf, len);
|
|
|
|
}
|
2017-02-18 14:35:29 -06:00
|
|
|
|
2017-03-12 18:03:45 -05:00
|
|
|
void dumpState(std::ostream& os) override;
|
2017-03-03 15:18:55 -06:00
|
|
|
|
2017-02-18 14:35:29 -06:00
|
|
|
protected:
|
2017-02-22 10:12:12 -06:00
|
|
|
/// Client handling the actual data.
|
2017-03-14 19:49:17 -05:00
|
|
|
std::shared_ptr<SocketHandlerInterface> _socketHandler;
|
2017-02-18 14:35:29 -06:00
|
|
|
|
2017-02-25 12:38:51 -06:00
|
|
|
/// True if we are already closed.
|
|
|
|
bool _closed;
|
|
|
|
|
2017-03-09 10:20:34 -06:00
|
|
|
/// True when shutdown was requested via shutdown().
|
|
|
|
bool _shutdownSignalled;
|
|
|
|
|
2017-02-18 14:35:29 -06:00
|
|
|
std::vector< char > _inBuffer;
|
|
|
|
std::vector< char > _outBuffer;
|
|
|
|
|
2017-02-23 06:14:01 -06:00
|
|
|
// To be able to access _inBuffer and _outBuffer.
|
2017-03-02 03:38:49 -06:00
|
|
|
// TODO we probably need accessors to the _inBuffer & _outBuffer
|
|
|
|
// instead of this many friends...
|
2017-02-23 06:14:01 -06:00
|
|
|
friend class WebSocketHandler;
|
2017-02-25 10:55:24 -06:00
|
|
|
friend class ClientRequestDispatcher;
|
2017-03-06 13:50:06 -06:00
|
|
|
friend class PrisonerRequestDispatcher;
|
2017-03-02 03:38:49 -06:00
|
|
|
friend class SimpleResponseClient;
|
2017-02-18 14:35:29 -06:00
|
|
|
};
|
|
|
|
|
2017-02-27 00:08:22 -06:00
|
|
|
namespace HttpHelper
|
|
|
|
{
|
2017-03-15 13:21:59 -05:00
|
|
|
void sendFile(const std::shared_ptr<StreamSocket>& socket, const std::string& path,
|
2017-03-19 10:45:29 -05:00
|
|
|
Poco::Net::HTTPResponse& response, bool noCache = false, bool deflate = false);
|
2017-02-28 18:34:21 -06:00
|
|
|
|
|
|
|
inline void sendFile(const std::shared_ptr<StreamSocket>& socket, const std::string& path,
|
2017-03-19 10:45:29 -05:00
|
|
|
const std::string& mediaType, bool noCache = false, bool deflate = false)
|
2017-02-28 18:34:21 -06:00
|
|
|
{
|
|
|
|
Poco::Net::HTTPResponse response;
|
|
|
|
response.setContentType(mediaType);
|
2017-03-19 10:45:29 -05:00
|
|
|
sendFile(socket, path, response, noCache, deflate);
|
2017-02-28 18:34:21 -06:00
|
|
|
}
|
2017-02-27 00:08:22 -06:00
|
|
|
};
|
|
|
|
|
2017-02-21 19:54:13 -06:00
|
|
|
#endif
|
2017-02-13 21:16:35 -06:00
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|