Archived
1
0

use epoll

This commit is contained in:
Jeff Becker 2018-05-03 11:47:20 -04:00
parent 6eba2d4653
commit 34fdc0a154
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05
9 changed files with 376 additions and 157 deletions

View File

@ -27,7 +27,7 @@ TEST = $(REPO)/test
DAEMON_SRC = $(REPO)/daemon DAEMON_SRC = $(REPO)/daemon
PKGS := libuv libsodium PKGS := libsodium
LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(HEADERS_PATH) INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(HEADERS_PATH)

View File

@ -10,6 +10,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
if (argc != 2) if (argc != 2)
@ -22,7 +23,7 @@ int main(int argc, char *argv[])
nntpchan::Mainloop loop; nntpchan::Mainloop loop;
nntpchan::NNTPServer nntp(loop); nntpchan::NNTPServer * nntp = new nntpchan::NNTPServer(loop);
std::string fname(argv[1]); std::string fname(argv[1]);
@ -54,7 +55,7 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
nntp.SetStoragePath(storeconf["store_path"]); nntp->SetStoragePath(storeconf["store_path"]);
auto &nntpconf = level.sections["nntp"].values; auto &nntpconf = level.sections["nntp"].values;
@ -70,11 +71,11 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
nntp.SetInstanceName(nntpconf["instance_name"]); nntp->SetInstanceName(nntpconf["instance_name"]);
if (nntpconf.find("authdb") != nntpconf.end()) if (nntpconf.find("authdb") != nntpconf.end())
{ {
nntp.SetLoginDB(nntpconf["authdb"]); nntp->SetLoginDB(nntpconf["authdb"]);
} }
if (level.sections.find("frontend") != level.sections.end()) if (level.sections.find("frontend") != level.sections.end())
@ -86,7 +87,7 @@ int main(int argc, char *argv[])
std::cerr << "frontend section provided but 'type' value not provided" << std::endl; std::cerr << "frontend section provided but 'type' value not provided" << std::endl;
return 1; return 1;
} }
auto ftype = frontconf["type"]; auto &ftype = frontconf["type"];
if (ftype == "exec") if (ftype == "exec")
{ {
if (frontconf.find("exec") == frontconf.end()) if (frontconf.find("exec") == frontconf.end())
@ -94,7 +95,7 @@ int main(int argc, char *argv[])
std::cerr << "exec frontend specified but no 'exec' value provided" << std::endl; std::cerr << "exec frontend specified but no 'exec' value provided" << std::endl;
return 1; return 1;
} }
nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"])); nntp->SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"]));
} }
else if (ftype == "staticfile") else if (ftype == "staticfile")
{ {
@ -113,7 +114,7 @@ int main(int argc, char *argv[])
std::cerr << "max_pages invalid value '" << frontconf["max_pages"] << "'" << std::endl; std::cerr << "max_pages invalid value '" << frontconf["max_pages"] << "'" << std::endl;
return 1; return 1;
} }
nntp.SetFrontend(new nntpchan::StaticFileFrontend(nntpchan::CreateTemplateEngine(frontconf["template_dialect"]), nntp->SetFrontend(new nntpchan::StaticFileFrontend(nntpchan::CreateTemplateEngine(frontconf["template_dialect"]),
frontconf["template_dir"], frontconf["out_dir"], maxPages)); frontconf["template_dir"], frontconf["out_dir"], maxPages));
} }
else else
@ -127,16 +128,23 @@ int main(int argc, char *argv[])
try try
{ {
nntp.Bind(a); if(nntp->Bind(a))
{
std::cerr << "nntpd for " << nntp->InstanceName() << " bound to " << a << std::endl;
}
else
{
std::cerr << "nntpd for " << nntp->InstanceName() << "failed to bind to " << a << ": "<< strerror(errno) << std::endl;
return 1;
}
} catch (std::exception &ex) } catch (std::exception &ex)
{ {
std::cerr << "failed to bind: " << ex.what() << std::endl; std::cerr << "failed to bind: " << ex.what() << std::endl;
return 1; return 1;
} }
std::cerr << "nntpd for " << nntp.InstanceName() << " bound to " << a << std::endl;
loop.Run(); loop.Run();
std::cerr << "Exiting" << std::endl;
} }
else else
{ {

View File

@ -1,22 +1,53 @@
#ifndef NNTPCHAN_EVENT_HPP #ifndef NNTPCHAN_EVENT_HPP
#define NNTPCHAN_EVENT_HPP #define NNTPCHAN_EVENT_HPP
#include <uv.h>
#include <unistd.h>
#include <cstdint>
#include <string>
#include <sys/socket.h>
namespace nntpchan namespace nntpchan
{ {
namespace ev
{
struct io
{
int fd;
io(int f) : fd(f) {};
virtual ~io() {};
virtual bool readable() const { return true; };
virtual int read(char * buf, size_t sz) = 0;
virtual bool writeable() const { return true; };
virtual int write() = 0;
virtual bool keepalive() = 0;
virtual void close()
{
if(fd!=-1)
{
::close(fd);
}
};
virtual bool acceptable() const { return false; };
virtual int accept() { return -1; };
};
}
class Mainloop class Mainloop
{ {
public: public:
Mainloop(); Mainloop();
~Mainloop(); ~Mainloop();
operator uv_loop_t *() const { return m_loop; } bool BindTCP(const sockaddr * addr, ev::io * handler);
bool TrackConn(ev::io * handler);
void Run(uv_run_mode mode = UV_RUN_DEFAULT); void UntrackConn(ev::io * handler);
void Stop(); void Run();
private: private:
uv_loop_t *m_loop; size_t conns;
int epollfd;
char readbuf[1024];
}; };
} }

View File

@ -4,7 +4,6 @@
#include "server.hpp" #include "server.hpp"
#include <deque> #include <deque>
#include <string> #include <string>
#include <uv.h>
namespace nntpchan namespace nntpchan
{ {
@ -12,7 +11,7 @@ namespace nntpchan
class NNTPServer : public Server class NNTPServer : public Server
{ {
public: public:
NNTPServer(uv_loop_t *loop); NNTPServer(Mainloop & loop);
virtual ~NNTPServer(); virtual ~NNTPServer();
@ -24,9 +23,7 @@ public:
std::string InstanceName() const; std::string InstanceName() const;
void Close(); virtual IServerConn *CreateConn(int fd);
virtual IServerConn *CreateConn(uv_stream_t *s);
virtual void OnAcceptError(int status); virtual void OnAcceptError(int status);
@ -43,13 +40,10 @@ private:
class NNTPServerConn : public IServerConn class NNTPServerConn : public IServerConn
{ {
public: public:
NNTPServerConn(uv_loop_t *l, uv_stream_t *s, Server *parent, IConnHandler *h) : IServerConn(l, s, parent, h) {} NNTPServerConn(int fd, Server *parent, IConnHandler *h) : IServerConn(fd, parent, h) {}
virtual bool IsTimedOut() { return false; }; virtual bool IsTimedOut() { return false; };
/** @brief send next queued reply */
virtual void SendNextReply();
virtual void Greet(); virtual void Greet();
}; };
} }

View File

@ -3,7 +3,7 @@
#include <deque> #include <deque>
#include <functional> #include <functional>
#include <string> #include <string>
#include <uv.h> #include <nntpchan/event.hpp>
namespace nntpchan namespace nntpchan
{ {
@ -37,38 +37,45 @@ private:
}; };
/** server connection handler interface */ /** server connection handler interface */
struct IServerConn struct IServerConn : public ev::io
{ {
IServerConn(uv_loop_t *l, uv_stream_t *s, Server *parent, IConnHandler *h); IServerConn(int fd, Server *parent, IConnHandler *h);
virtual ~IServerConn(); virtual ~IServerConn();
virtual void Close(); virtual int read(char * buf, size_t sz);
virtual int write();
virtual void close();
virtual void Greet() = 0; virtual void Greet() = 0;
virtual void SendNextReply() = 0;
virtual bool IsTimedOut() = 0; virtual bool IsTimedOut() = 0;
void SendString(const std::string &str); virtual bool keepalive() ;
Server *Parent() { return m_parent; }; Server *Parent() { return m_parent; };
IConnHandler *GetHandler() { return m_handler; }; IConnHandler *GetHandler() { return m_handler; };
uv_loop_t *GetLoop() { return m_loop; };
private: private:
uv_tcp_t m_conn;
uv_loop_t *m_loop;
Server *m_parent; Server *m_parent;
IConnHandler *m_handler; IConnHandler *m_handler;
std::string m_writeLeftover;
}; };
class Server class Server : public ev::io
{ {
public: public:
Server(uv_loop_t *loop); Server(Mainloop & loop);
/** called after socket close, NEVER call directly */ virtual ~Server() {};
virtual ~Server() {}
virtual bool acceptable() const { return true; };
virtual void close();
virtual bool readable() const { return false; };
virtual int read(char *,size_t) { return -1; };
virtual bool writeable() const { return false; };
virtual int write() {return -1; };
virtual int accept();
virtual bool keepalive() { return true; };
/** create connection handler from open stream */ /** create connection handler from open stream */
virtual IServerConn *CreateConn(uv_stream_t *s) = 0; virtual IServerConn *CreateConn(int fd) = 0;
/** close all sockets and stop */
void Close();
/** bind to address */ /** bind to address */
void Bind(const std::string &addr); bool Bind(const std::string &addr);
typedef std::function<void(IServerConn *)> ConnVisitor; typedef std::function<void(IServerConn *)> ConnVisitor;
@ -79,18 +86,13 @@ public:
void RemoveConn(IServerConn *conn); void RemoveConn(IServerConn *conn);
protected: protected:
uv_loop_t *GetLoop() { return m_loop; }
virtual void OnAcceptError(int status) = 0; virtual void OnAcceptError(int status) = 0;
private: private:
operator uv_handle_t *() { return (uv_handle_t *)&m_server; }
operator uv_tcp_t *() { return &m_server; }
operator uv_stream_t *() { return (uv_stream_t *)&m_server; }
void OnAccept(uv_stream_t *s, int status); void OnAccept(int fd, int status);
Mainloop & m_Loop;
std::deque<IServerConn *> m_conns; std::deque<IServerConn *> m_conns;
uv_tcp_t m_server;
uv_loop_t *m_loop;
}; };
} }

View File

@ -1,17 +1,197 @@
#include <cassert> #include <cassert>
#include <nntpchan/event.hpp> #include <nntpchan/event.hpp>
#include <sys/epoll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <iostream>
namespace nntpchan namespace nntpchan
{ {
Mainloop::Mainloop() Mainloop::Mainloop() : conns(0)
{ {
m_loop = uv_default_loop(); epollfd = epoll_create1(EPOLL_CLOEXEC);
assert(uv_loop_init(m_loop) == 0);
} }
Mainloop::~Mainloop() { uv_loop_close(m_loop); } Mainloop::~Mainloop() { close(epollfd); }
void Mainloop::Stop() { uv_stop(m_loop); }
void Mainloop::Run(uv_run_mode mode) { assert(uv_run(m_loop, mode) == 0); } bool Mainloop::BindTCP(const sockaddr * addr, ev::io * handler)
{
assert(handler->acceptable());
socklen_t slen;
switch(addr->sa_family)
{
case AF_INET:
slen = sizeof(sockaddr_in);
break;
case AF_INET6:
slen = sizeof(sockaddr_in6);
break;
case AF_UNIX:
slen = sizeof(sockaddr_un);
break;
default:
return false;
}
int fd = socket(addr->sa_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
if(fd == -1)
{
return false;
}
std::cout << "bind to " << fd << std::endl;
handler->fd = fd;
if(bind(fd, addr, slen) == -1)
return false;
if (listen(fd, 5) == -1)
return false;
return TrackConn(handler);
}
bool Mainloop::TrackConn(ev::io * handler)
{
epoll_event ev;
ev.data.ptr = handler;
ev.events = EPOLLET;
if(handler->readable() || handler->acceptable())
{
ev.events |= EPOLLIN;
}
if(handler->writeable())
{
ev.events |= EPOLLOUT;
}
if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, handler->fd, &ev) == -1)
{
return false;
}
++conns;
return true;
}
void Mainloop::UntrackConn(ev::io * handler)
{
if(epoll_ctl(epollfd, EPOLL_CTL_DEL, handler->fd, nullptr) != -1)
--conns;
}
void Mainloop::Run()
{
epoll_event evs[512];
epoll_event * ev;
ev::io * handler;
int res = -1;
int idx ;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGWINCH);
int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
epoll_event sig_ev;
sig_ev.data.fd = sfd;
sig_ev.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &sig_ev);
do
{
res = epoll_wait(epollfd, evs, 512, -1);
idx = 0;
while(idx < res)
{
ev = &evs[idx++];
if(ev->data.fd == sfd)
{
read(sfd, readbuf, sizeof(readbuf));
continue;
}
handler = static_cast<ev::io *>(ev->data.ptr);
if(ev->events & EPOLLERR || ev->events & EPOLLHUP)
{
handler->close();
delete handler;
continue;
}
if (handler->acceptable())
{
int acceptfd;
bool errored = false;
while(true)
{
acceptfd = handler->accept();
if(acceptfd == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
break;
}
perror("accept()");
errored = true;
break;
}
}
if(errored)
{
handler->close();
delete handler;
continue;
}
}
if(ev->events & EPOLLIN && handler->readable())
{
int readed = handler->read(readbuf, sizeof(readbuf));
if(readed == -1)
{
if(errno != EAGAIN)
{
perror("read()");
handler->close();
delete handler;
continue;
}
}
else if (readed == 0)
{
handler->close();
delete handler;
continue;
}
}
if(ev->events & EPOLLOUT && handler->writeable())
{
int written = handler->write();
if(written < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
// blocking
}
else
{
perror("write()");
handler->close();
delete handler;
}
}
}
if (!handler->keepalive())
{
handler->close();
delete handler;
}
}
}
while(res != -1 && conns);
}
} }

View File

@ -11,7 +11,7 @@ std::string NetAddr::to_string()
std::string str("invalid"); std::string str("invalid");
const size_t s = 128; const size_t s = 128;
char *buff = new char[s]; char *buff = new char[s];
if (uv_ip6_name(&addr, buff, s) == 0) if (inet_ntop(AF_INET6, &addr, buff, sizeof(sockaddr_in6)))
{ {
str = std::string(buff); str = std::string(buff);
delete[] buff; delete[] buff;
@ -38,7 +38,9 @@ NetAddr ParseAddr(const std::string &addr)
auto p = addr.substr(n + 2); auto p = addr.substr(n + 2);
int port = std::atoi(p.c_str()); int port = std::atoi(p.c_str());
auto a = addr.substr(0, n); auto a = addr.substr(0, n);
uv_ip6_addr(a.c_str(), port, &saddr.addr); saddr.addr.sin6_port = htons(port);
saddr.addr.sin6_family = AF_INET6;
inet_pton(AF_INET6, a.c_str(), &saddr.addr);
return saddr; return saddr;
} }
} }

View File

@ -1,5 +1,6 @@
#include <cassert> #include <cassert>
#include <cstring>
#include <iostream> #include <iostream>
#include <nntpchan/net.hpp> #include <nntpchan/net.hpp>
#include <nntpchan/nntp_auth.hpp> #include <nntpchan/nntp_auth.hpp>
@ -10,11 +11,11 @@
namespace nntpchan namespace nntpchan
{ {
NNTPServer::NNTPServer(uv_loop_t *loop) : Server(loop), m_frontend(nullptr) {} NNTPServer::NNTPServer(Mainloop & loop) : Server(loop), m_frontend(nullptr) {}
NNTPServer::~NNTPServer() {} NNTPServer::~NNTPServer() {}
IServerConn *NNTPServer::CreateConn(uv_stream_t *s) IServerConn *NNTPServer::CreateConn(int f)
{ {
CredDB_ptr creds; CredDB_ptr creds;
@ -27,8 +28,7 @@ IServerConn *NNTPServer::CreateConn(uv_stream_t *s)
if (creds) if (creds)
handler->SetAuth(creds); handler->SetAuth(creds);
NNTPServerConn *conn = new NNTPServerConn(GetLoop(), s, this, handler); return new NNTPServerConn(f, this, handler);
return conn;
} }
void NNTPServer::SetLoginDB(const std::string path) { m_logindbpath = path; } void NNTPServer::SetLoginDB(const std::string path) { m_logindbpath = path; }
@ -41,22 +41,12 @@ void NNTPServer::SetFrontend(Frontend *f) { m_frontend.reset(f); }
std::string NNTPServer::InstanceName() const { return m_servername; } std::string NNTPServer::InstanceName() const { return m_servername; }
void NNTPServer::OnAcceptError(int status) { std::cerr << "nntpserver::accept() " << uv_strerror(status) << std::endl; } void NNTPServer::OnAcceptError(int status) { std::cerr << "nntpserver::accept() " << strerror(status) << std::endl; }
void NNTPServerConn::SendNextReply()
{
IConnHandler *handler = GetHandler();
while (handler->HasNextLine())
{
auto line = handler->GetNextLine();
SendString(line + "\r\n");
}
}
void NNTPServerConn::Greet() void NNTPServerConn::Greet()
{ {
IConnHandler *handler = GetHandler(); IConnHandler *handler = GetHandler();
handler->Greet(); handler->Greet();
SendNextReply();
} }
} }

View File

@ -6,46 +6,56 @@
namespace nntpchan namespace nntpchan
{ {
Server::Server(uv_loop_t *loop)
Server::Server(Mainloop & loop) : ev::io(-1), m_Loop(loop)
{ {
m_loop = loop;
uv_tcp_init(m_loop, &m_server);
m_server.data = this;
} }
void Server::Close() void Server::close()
{ {
std::cout << "Close server" << std::endl; auto itr = m_conns.begin();
uv_close((uv_handle_t *)&m_server, [](uv_handle_t *s) { while(itr != m_conns.end())
Server *self = (Server *)s->data; {
if (self) itr = m_conns.erase(itr);
delete self;
s->data = nullptr;
});
} }
m_Loop.UntrackConn(this);
void Server::Bind(const std::string &addr) ev::io::close();
}
bool Server::Bind(const std::string &addr)
{ {
auto saddr = ParseAddr(addr); auto saddr = ParseAddr(addr);
assert(uv_tcp_bind(*this, saddr, 0) == 0); return m_Loop.BindTCP(saddr, this);
auto cb = [](uv_stream_t *s, int status) {
Server *self = (Server *)s->data;
self->OnAccept(s, status);
};
assert(uv_listen(*this, 5, cb) == 0);
} }
void Server::OnAccept(uv_stream_t *s, int status) void Server::OnAccept(int f, int status)
{ {
if (status < 0) if (status)
{ {
OnAcceptError(status); OnAcceptError(status);
return; return;
} }
IServerConn *conn = CreateConn(s); IServerConn *conn = CreateConn(f);
assert(conn);
if(m_Loop.TrackConn(conn))
{
m_conns.push_back(conn); m_conns.push_back(conn);
conn->Greet(); conn->Greet();
conn->write();
}
else
{
std::cout << "accept track conn failed" << std::endl;
conn->close();
delete conn;
}
}
int Server::accept()
{
int res = ::accept4(fd, nullptr, nullptr, SOCK_NONBLOCK);
if(res == -1) return res;
OnAccept(res, errno);
return res;
} }
void Server::RemoveConn(IServerConn *conn) void Server::RemoveConn(IServerConn *conn)
@ -58,9 +68,10 @@ void Server::RemoveConn(IServerConn *conn)
else else
++itr; ++itr;
} }
m_Loop.UntrackConn(conn);
} }
void IConnHandler::QueueLine(const std::string &line) { m_sendlines.push_back(line); } void IConnHandler::QueueLine(const std::string &line) { m_sendlines.push_back(line+"\r\n"); }
bool IConnHandler::HasNextLine() { return m_sendlines.size() > 0; } bool IConnHandler::HasNextLine() { return m_sendlines.size() > 0; }
@ -71,72 +82,73 @@ std::string IConnHandler::GetNextLine()
return line; return line;
} }
IServerConn::IServerConn(uv_loop_t *l, uv_stream_t *st, Server *parent, IConnHandler *h) IServerConn::IServerConn(int fd, Server *parent, IConnHandler *h) : ev::io(fd), m_parent(parent), m_handler(h)
{ {
m_loop = l;
m_parent = parent;
m_handler = h;
uv_tcp_init(l, &m_conn);
m_conn.data = this;
uv_accept(st, (uv_stream_t *)&m_conn);
uv_read_start((uv_stream_t *)&m_conn,
[](uv_handle_t *h, size_t s, uv_buf_t *b) {
IServerConn *self = (IServerConn *)h->data;
if (self == nullptr)
return;
b->base = new char[s];
},
[](uv_stream_t *s, ssize_t nread, const uv_buf_t *b) {
IServerConn *self = (IServerConn *)s->data;
if (self == nullptr)
{
if (b->base)
delete[] b->base;
return;
}
if (nread > 0)
{
self->m_handler->OnData(b->base, nread);
self->SendNextReply();
if (self->m_handler->ShouldClose())
self->Close();
delete[] b->base;
}
else
{
if (nread != UV_EOF)
{
std::cerr << "error in nntp server conn alloc: ";
std::cerr << uv_strerror(nread);
std::cerr << std::endl;
}
// got eof or error
self->Close();
}
});
} }
IServerConn::~IServerConn() { delete m_handler; } IServerConn::~IServerConn() { delete m_handler; }
void IServerConn::SendString(const std::string &str) int IServerConn::read(char * buf, size_t sz)
{ {
WriteBuffer *b = new WriteBuffer(str); ssize_t readsz = ::read(fd, buf, sz);
uv_write(&b->w, (uv_stream_t *)&m_conn, &b->b, 1, [](uv_write_t *w, int status) { if(readsz > 0)
(void)status; {
WriteBuffer *wb = (WriteBuffer *)w->data; m_handler->OnData(buf, readsz);
if (wb) }
delete wb; return readsz;
});
} }
void IServerConn::Close() bool IServerConn::keepalive()
{
return !m_handler->ShouldClose();
}
int IServerConn::write()
{
auto leftovers = m_writeLeftover.size();
ssize_t written;
if(leftovers)
{
if(leftovers > 1024)
{
leftovers = 1024;
}
written = ::write(fd, m_writeLeftover.c_str(), leftovers);
if(written > 0)
{
m_writeLeftover = m_writeLeftover.substr(written);
}
else
{
// too much leftovers
return -1;
}
}
do
{
if(!m_handler->HasNextLine())
{
return 0;
}
auto line = m_handler->GetNextLine();
written = ::write(fd, line.c_str(), line.size());
if(written > 0)
{
m_writeLeftover = line.substr(written);
}
else
{
m_writeLeftover = line;
return -1;
}
}
while(written > 0);
return 0;
}
void IServerConn::close()
{ {
m_parent->RemoveConn(this); m_parent->RemoveConn(this);
uv_close((uv_handle_t *)&m_conn, [](uv_handle_t *s) { ev::io::close();
IServerConn *self = (IServerConn *)s->data;
if (self)
delete self;
s->data = nullptr;
});
} }
} }