2015-09-13 20:25:26 +00:00
|
|
|
///
|
2019-02-15 17:05:44 +00:00
|
|
|
/// Copyright 2015-2019 Oliver Giles
|
2015-09-13 20:25:26 +00:00
|
|
|
///
|
|
|
|
/// This file is part of Laminar
|
|
|
|
///
|
|
|
|
/// Laminar is free software: you can redistribute it and/or modify
|
|
|
|
/// it under the terms of the GNU General Public License as published by
|
|
|
|
/// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
/// (at your option) any later version.
|
|
|
|
///
|
|
|
|
/// Laminar is distributed in the hope that it will be useful,
|
|
|
|
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
/// GNU General Public License for more details.
|
|
|
|
///
|
|
|
|
/// You should have received a copy of the GNU General Public License
|
|
|
|
/// along with Laminar. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
///
|
|
|
|
#include "server.h"
|
2015-12-06 11:36:12 +00:00
|
|
|
#include "log.h"
|
2019-09-27 17:50:46 +00:00
|
|
|
#include "rpc.h"
|
|
|
|
#include "http.h"
|
2019-10-05 17:06:35 +00:00
|
|
|
#include "laminar.h"
|
2015-09-13 20:25:26 +00:00
|
|
|
|
|
|
|
#include <kj/async-io.h>
|
2018-08-03 11:36:24 +00:00
|
|
|
#include <kj/async-unix.h>
|
2015-09-13 20:25:26 +00:00
|
|
|
#include <kj/threadlocal.h>
|
|
|
|
|
2019-07-04 09:28:33 +00:00
|
|
|
#include <signal.h>
|
2015-09-13 20:25:26 +00:00
|
|
|
#include <sys/eventfd.h>
|
2018-04-06 15:04:50 +00:00
|
|
|
#include <sys/inotify.h>
|
2018-02-24 16:53:11 +00:00
|
|
|
#include <sys/signalfd.h>
|
2015-09-13 20:25:26 +00:00
|
|
|
|
2016-07-25 11:59:45 +00:00
|
|
|
// Size of buffer used to read from file descriptors. Should be
|
|
|
|
// a multiple of sizeof(struct signalfd_siginfo) == 128
|
|
|
|
#define PROC_IO_BUFSIZE 4096
|
|
|
|
|
2019-10-05 17:06:35 +00:00
|
|
|
Server::Server(kj::AsyncIoContext& io) :
|
|
|
|
ioContext(io),
|
2018-02-24 16:53:11 +00:00
|
|
|
listeners(kj::heap<kj::TaskSet>(*this)),
|
2019-10-05 17:06:35 +00:00
|
|
|
childTasks(*this)
|
2015-09-13 20:25:26 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Server::~Server() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::start() {
|
2018-02-24 16:53:11 +00:00
|
|
|
// The eventfd is used to quit the server later since we need to trigger
|
|
|
|
// a reaction from the event loop
|
2017-12-04 18:31:23 +00:00
|
|
|
efd_quit = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
|
2018-02-24 16:53:11 +00:00
|
|
|
kj::evalLater([this](){
|
2015-09-13 20:25:26 +00:00
|
|
|
static uint64_t _;
|
2016-07-25 11:59:45 +00:00
|
|
|
auto wakeEvent = ioContext.lowLevelProvider->wrapInputFd(efd_quit);
|
2015-09-13 20:25:26 +00:00
|
|
|
return wakeEvent->read(&_, sizeof(uint64_t)).attach(std::move(wakeEvent));
|
2018-02-24 16:53:11 +00:00
|
|
|
}).wait(ioContext.waitScope);
|
|
|
|
// Execution arrives here when the eventfd is triggered (in stop())
|
|
|
|
|
|
|
|
// Shutdown sequence:
|
|
|
|
// 1. stop accepting new connections
|
|
|
|
listeners = nullptr;
|
2019-10-05 17:06:35 +00:00
|
|
|
// 2. wait for all children to close
|
2018-02-24 16:53:11 +00:00
|
|
|
childTasks.onEmpty().wait(ioContext.waitScope);
|
2019-10-05 17:06:35 +00:00
|
|
|
// TODO not sure the comments below are true
|
|
|
|
// 3. run the loop once more to send any pending output to http clients
|
2018-02-24 16:53:11 +00:00
|
|
|
ioContext.waitScope.poll();
|
2019-10-05 17:06:35 +00:00
|
|
|
// 4. return: http connections will be destructed when class is deleted
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Server::stop() {
|
2018-02-24 16:53:11 +00:00
|
|
|
// This method is expected to be called in signal context, so an eventfd
|
|
|
|
// is used to get the main loop to react. See run()
|
2016-07-25 11:59:45 +00:00
|
|
|
eventfd_write(efd_quit, 1);
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 11:15:59 +00:00
|
|
|
kj::Promise<void> Server::readDescriptor(int fd, std::function<void(const char*,size_t)> cb) {
|
2017-07-31 05:53:50 +00:00
|
|
|
auto event = this->ioContext.lowLevelProvider->wrapInputFd(fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP);
|
2017-12-07 16:28:12 +00:00
|
|
|
auto buffer = kj::heapArrayBuilder<char>(PROC_IO_BUFSIZE);
|
2018-07-20 11:15:59 +00:00
|
|
|
return handleFdRead(event, buffer.asPtr().begin(), cb).attach(std::move(event)).attach(std::move(buffer));
|
|
|
|
}
|
|
|
|
|
2018-08-03 11:36:24 +00:00
|
|
|
void Server::addTask(kj::Promise<void>&& task) {
|
2018-07-20 11:15:59 +00:00
|
|
|
childTasks.add(kj::mv(task));
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 10:25:19 +00:00
|
|
|
kj::Promise<void> Server::addTimeout(int seconds, std::function<void ()> cb) {
|
|
|
|
return ioContext.lowLevelProvider->getTimer().afterDelay(seconds * kj::SECONDS).then([cb](){
|
|
|
|
cb();
|
|
|
|
}).eagerlyEvaluate(nullptr);
|
|
|
|
}
|
|
|
|
|
2018-08-03 11:36:24 +00:00
|
|
|
kj::Promise<int> Server::onChildExit(kj::Maybe<pid_t> &pid) {
|
|
|
|
return ioContext.unixEventPort.onChildExit(pid);
|
|
|
|
}
|
|
|
|
|
2019-10-05 17:06:35 +00:00
|
|
|
Server::PathWatcher& Server::watchPaths(std::function<void()> fn)
|
|
|
|
{
|
2020-09-25 23:33:36 +00:00
|
|
|
struct PathWatcherImpl final : public PathWatcher {
|
2019-10-05 17:06:35 +00:00
|
|
|
PathWatcher& addPath(const char* path) override {
|
|
|
|
inotify_add_watch(fd, path, IN_ONLYDIR | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
int fd;
|
|
|
|
};
|
|
|
|
auto pwi = kj::heap<PathWatcherImpl>();
|
|
|
|
PathWatcher* pw = pwi.get();
|
|
|
|
|
|
|
|
pwi->fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
|
|
|
listeners->add(readDescriptor(pwi->fd, [fn](const char*, size_t){
|
|
|
|
fn();
|
|
|
|
}).attach(kj::mv(pwi)));
|
|
|
|
return *pw;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::listenRpc(Rpc &rpc, kj::StringPtr rpcBindAddress)
|
|
|
|
{
|
|
|
|
if(rpcBindAddress.startsWith("unix:"))
|
|
|
|
unlink(rpcBindAddress.slice(strlen("unix:")).cStr());
|
|
|
|
listeners->add(ioContext.provider->getNetwork().parseAddress(rpcBindAddress)
|
|
|
|
.then([this,&rpc](kj::Own<kj::NetworkAddress>&& addr) {
|
|
|
|
return acceptRpcClient(rpc, addr->listen());
|
|
|
|
}));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::listenHttp(Http &http, kj::StringPtr httpBindAddress)
|
|
|
|
{
|
|
|
|
if(httpBindAddress.startsWith("unix:"))
|
|
|
|
unlink(httpBindAddress.slice(strlen("unix:")).cStr());
|
|
|
|
listeners->add(ioContext.provider->getNetwork().parseAddress(httpBindAddress)
|
|
|
|
.then([this,&http](kj::Own<kj::NetworkAddress>&& addr) {
|
|
|
|
return http.startServer(ioContext.lowLevelProvider->getTimer(), addr->listen());
|
|
|
|
}));
|
2018-04-06 15:04:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 17:06:35 +00:00
|
|
|
kj::Promise<void> Server::acceptRpcClient(Rpc& rpc, kj::Own<kj::ConnectionReceiver>&& listener) {
|
2018-02-24 16:53:11 +00:00
|
|
|
kj::ConnectionReceiver& cr = *listener.get();
|
|
|
|
return cr.accept().then(kj::mvCapture(kj::mv(listener),
|
2019-10-05 17:06:35 +00:00
|
|
|
[this, &rpc](kj::Own<kj::ConnectionReceiver>&& listener, kj::Own<kj::AsyncIoStream>&& connection) {
|
|
|
|
addTask(rpc.accept(kj::mv(connection)));
|
|
|
|
return acceptRpcClient(rpc, kj::mv(listener));
|
2018-02-24 16:53:11 +00:00
|
|
|
}));
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 11:59:45 +00:00
|
|
|
// returns a promise which will read a chunk of data from the file descriptor
|
|
|
|
// wrapped by stream and invoke the provided callback with the read data.
|
|
|
|
// Repeats until ::read returns <= 0
|
2017-12-07 16:28:12 +00:00
|
|
|
kj::Promise<void> Server::handleFdRead(kj::AsyncInputStream* stream, char* buffer, std::function<void(const char*,size_t)> cb) {
|
|
|
|
return stream->tryRead(buffer, 1, PROC_IO_BUFSIZE).then([this,stream,buffer,cb](size_t sz) {
|
2015-09-13 20:25:26 +00:00
|
|
|
if(sz > 0) {
|
2017-12-07 16:28:12 +00:00
|
|
|
cb(buffer, sz);
|
2017-12-06 19:51:50 +00:00
|
|
|
return handleFdRead(stream, kj::mv(buffer), cb);
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
2017-12-06 19:59:22 +00:00
|
|
|
return kj::Promise<void>(kj::READY_NOW);
|
2017-12-07 16:28:12 +00:00
|
|
|
});
|
2015-09-13 20:25:26 +00:00
|
|
|
}
|
2018-01-27 11:11:40 +00:00
|
|
|
|
|
|
|
void Server::taskFailed(kj::Exception &&exception) {
|
|
|
|
//kj::throwFatalException(kj::mv(exception));
|
2018-12-14 13:23:57 +00:00
|
|
|
// prettier
|
|
|
|
fprintf(stderr, "fatal: %s\n", exception.getDescription().cStr());
|
|
|
|
exit(EXIT_FAILURE);
|
2018-01-27 11:11:40 +00:00
|
|
|
}
|