Commit 0d0d82fd authored by octal's avatar octal

Refactored a little bit the API. Peer is not a reference anymore but

instead is a shared_ptr
parent 2603f15f
...@@ -8,28 +8,28 @@ ...@@ -8,28 +8,28 @@
using namespace std; using namespace std;
class MyHandler : public Net::Http::Handler { class MyHandler : public Net::Http::Handler {
void onRequest(const Net::Http::Request& req, Net::Tcp::Peer& peer) { void onRequest(const Net::Http::Request& req, Net::Http::Response response) {
if (req.resource == "/ping") { if (req.resource() == "/ping") {
if (req.method == Net::Http::Method::Get) { if (req.method() == Net::Http::Method::Get) {
using namespace Net::Http; using namespace Net::Http;
Net::Http::Response response(Net::Http::Code::Ok, "PONG"); response.headers()
response.headers
.add(std::make_shared<Header::Server>("lys")) .add(std::make_shared<Header::Server>("lys"))
.add(std::make_shared<Header::ContentType>(MIME(Text, Plain))); .add(std::make_shared<Header::ContentType>(MIME(Text, Plain)));
response.writeTo(peer); response.send(Net::Http::Code::Ok, "PONG");
} }
} }
else if (req.resource == "/echo") { else if (req.resource() == "/echo") {
if (req.method == Net::Http::Method::Post) { if (req.method() == Net::Http::Method::Post) {
Net::Http::Response response(Net::Http::Code::Ok, req.body); response.send(Net::Http::Code::Ok, req.body());
response.writeTo(peer);
} }
} }
else if (req.resource() == "/exception") {
throw std::runtime_error("Exception thrown in the handler");
}
} }
}; };
......
...@@ -32,12 +32,12 @@ static constexpr const char* ParserData = "__Parser"; ...@@ -32,12 +32,12 @@ static constexpr const char* ParserData = "__Parser";
namespace Private { namespace Private {
void void
Parser::Step::raise(const char* msg, Code code /* = Code::Bad_Request */) { Step::raise(const char* msg, Code code /* = Code::Bad_Request */) {
throw HttpError(code, msg); throw HttpError(code, msg);
} }
Parser::State State
Parser::RequestLineStep::apply(StreamCursor& cursor) { RequestLineStep::apply(StreamCursor& cursor) {
StreamCursor::Revert revert(cursor); StreamCursor::Revert revert(cursor);
// Method // Method
...@@ -59,7 +59,7 @@ namespace Private { ...@@ -59,7 +59,7 @@ namespace Private {
bool found = false; bool found = false;
for (const auto& method: Methods) { for (const auto& method: Methods) {
if (match_raw(method.str, method.len, cursor)) { if (match_raw(method.str, method.len, cursor)) {
request->method = method.repr; request->method_ = method.repr;
found = true; found = true;
break; break;
} }
...@@ -83,7 +83,7 @@ namespace Private { ...@@ -83,7 +83,7 @@ namespace Private {
if (!cursor.advance(1)) return State::Again; if (!cursor.advance(1)) return State::Again;
} }
request->resource = std::string(cursor.offset(start), cursor.diff(start) + 1); request->resource_ = std::string(cursor.offset(start), cursor.diff(start) + 1);
if ((n = cursor.next()) == StreamCursor::Eof) return State::Again; if ((n = cursor.next()) == StreamCursor::Eof) return State::Again;
if (n != ' ') if (n != ' ')
...@@ -100,10 +100,10 @@ namespace Private { ...@@ -100,10 +100,10 @@ namespace Private {
const size_t diff = cursor.diff(start); const size_t diff = cursor.diff(start);
if (strncmp(cursor.offset(start), "HTTP/1.0", diff) == 0) { if (strncmp(cursor.offset(start), "HTTP/1.0", diff) == 0) {
request->version = Version::Http10; request->version_ = Version::Http10;
} }
else if (strncmp(cursor.offset(start), "HTTP/1.1", diff) == 0) { else if (strncmp(cursor.offset(start), "HTTP/1.1", diff) == 0) {
request->version = Version::Http11; request->version_ = Version::Http11;
} }
else { else {
raise("Encountered invalid HTTP version"); raise("Encountered invalid HTTP version");
...@@ -116,8 +116,8 @@ namespace Private { ...@@ -116,8 +116,8 @@ namespace Private {
} }
Parser::State State
Parser::HeadersStep::apply(StreamCursor& cursor) { HeadersStep::apply(StreamCursor& cursor) {
StreamCursor::Revert revert(cursor); StreamCursor::Revert revert(cursor);
while (!cursor.eol()) { while (!cursor.eol()) {
...@@ -147,11 +147,11 @@ namespace Private { ...@@ -147,11 +147,11 @@ namespace Private {
if (Header::Registry::isRegistered(name)) { if (Header::Registry::isRegistered(name)) {
std::shared_ptr<Header::Header> header = Header::Registry::makeHeader(name); std::shared_ptr<Header::Header> header = Header::Registry::makeHeader(name);
header->parseRaw(cursor.offset(start), cursor.diff(start)); header->parseRaw(cursor.offset(start), cursor.diff(start));
request->headers.add(header); request->headers_.add(header);
} }
else { else {
std::string value(cursor.offset(start), cursor.diff(start)); std::string value(cursor.offset(start), cursor.diff(start));
request->headers.addRaw(Header::Raw(std::move(name), std::move(value))); request->headers_.addRaw(Header::Raw(std::move(name), std::move(value)));
} }
// CRLF // CRLF
...@@ -161,13 +161,13 @@ namespace Private { ...@@ -161,13 +161,13 @@ namespace Private {
} }
revert.ignore(); revert.ignore();
return Parser::State::Next; return State::Next;
} }
Parser::State State
Parser::BodyStep::apply(StreamCursor& cursor) { BodyStep::apply(StreamCursor& cursor) {
auto cl = request->headers.tryGet<Header::ContentLength>(); auto cl = request->headers_.tryGet<Header::ContentLength>();
if (!cl) return Parser::State::Done; if (!cl) return State::Done;
auto contentLength = cl->value(); auto contentLength = cl->value();
// We already started to read some bytes but we got an incomplete payload // We already started to read some bytes but we got an incomplete payload
...@@ -181,14 +181,14 @@ namespace Private { ...@@ -181,14 +181,14 @@ namespace Private {
if (!cursor.advance(remaining)) { if (!cursor.advance(remaining)) {
const size_t available = cursor.remaining(); const size_t available = cursor.remaining();
request->body.append(cursor.offset(start), available); request->body_.append(cursor.offset(start), available);
bytesRead += available; bytesRead += available;
cursor.advance(available); cursor.advance(available);
return State::Again; return State::Again;
} }
else { else {
request->body.append(cursor.offset(), remaining); request->body_.append(cursor.offset(), remaining);
} }
} }
...@@ -196,7 +196,7 @@ namespace Private { ...@@ -196,7 +196,7 @@ namespace Private {
else { else {
if (!cursor.advance(2)) return State::Again; if (!cursor.advance(2)) return State::Again;
request->body.reserve(contentLength); request->body_.reserve(contentLength);
auto start = cursor; auto start = cursor;
...@@ -204,21 +204,21 @@ namespace Private { ...@@ -204,21 +204,21 @@ namespace Private {
if (!cursor.advance(contentLength)) { if (!cursor.advance(contentLength)) {
const size_t available = cursor.remaining(); const size_t available = cursor.remaining();
request->body.append(cursor.offset(start), available); request->body_.append(cursor.offset(start), available);
bytesRead += available; bytesRead += available;
cursor.advance(available); cursor.advance(available);
return State::Again; return State::Again;
} }
request->body.append(cursor.offset(start), contentLength); request->body_.append(cursor.offset(start), contentLength);
} }
bytesRead = 0; bytesRead = 0;
return Parser::State::Done; return State::Done;
} }
Parser::State State
Parser::parse() { Parser::parse() {
State state = State::Again; State state = State::Again;
do { do {
...@@ -245,88 +245,147 @@ namespace Private { ...@@ -245,88 +245,147 @@ namespace Private {
currentStep = 0; currentStep = 0;
request.headers.clear(); request.headers_.clear();
request.body.clear(); request.body_.clear();
request.resource.clear(); request.resource_.clear();
} }
} // namespace Private } // namespace Private
Message::Message() Message::Message()
: version(Version::Http11) : version_(Version::Http11)
{ } { }
Request::Request() Request::Request()
: Message() : Message()
{ } { }
Response::Response(int code, std::string body) Version
{ Request::version() const {
this->body = std::move(body); return version_;
code_ = code; }
Method
Request::method() const {
return method_;
}
std::string
Request::resource() const {
return resource_;
}
std::string
Request::body() const {
return body_;
} }
Response::Response(Code code, std::string body) const Header::Collection&
Request::headers() const {
return headers_;
}
Response::Response()
: Message() : Message()
{ }
void
Response::associatePeer(const std::shared_ptr<Tcp::Peer>& peer)
{ {
this->body = std::move(body); if (peer_.use_count() > 0)
code_ = static_cast<int>(code); throw std::runtime_error("A peer was already associated to the response");
peer_ = peer;
} }
void ssize_t
Response::writeTo(Tcp::Peer& peer) Response::send(Code code) {
return send(code, "");
}
ssize_t
Response::send(Code code, const std::string& body, const Mime::MediaType& mime)
{ {
int fd = peer.fd(); int fd = peer()->fd();
char buffer[Const::MaxBuffer]; char buffer[Const::MaxBuffer];
Io::OutArrayBuf obuf(buffer, Io::Init::ZeroOut); Io::OutArrayBuf obuf(buffer, Io::Init::ZeroOut);
std::ostream stream(&obuf); std::ostream stream(&obuf);
stream << "HTTP/1.1 "; stream << "HTTP/1.1 ";
stream << code_; stream << static_cast<int>(code);
stream << ' '; stream << ' ';
stream << static_cast<Code>(code_); stream << code;
stream << crlf; stream << crlf;
for (const auto& header: headers.list()) { for (const auto& header: headers_.list()) {
stream << header->name() << ": "; stream << header->name() << ": ";
header->write(stream); header->write(stream);
stream << crlf; stream << crlf;
} }
stream << "Content-Length: " << body.size() << crlf; if (!body.empty()) {
stream << crlf; stream << "Content-Length: " << body.size() << crlf;
stream << crlf;
stream << body;
}
ssize_t bytes = ::send(fd, buffer, obuf.len(), 0);
return bytes;
}
void
Response::setMime(const Mime::MediaType& mime)
{
headers_.add(std::make_shared<Header::ContentType>(mime));
}
Header::Collection&
Response::headers() {
return headers_;
}
const Header::Collection&
Response::headers() const {
return headers_;
}
stream << body; std::shared_ptr<Tcp::Peer>
Response::peer() const {
if (peer_.expired()) {
throw std::runtime_error("Broken pipe");
}
ssize_t bytes = send(fd, buffer, obuf.len(), 0); return peer_.lock();
} }
void void
Handler::onInput(const char* buffer, size_t len, Tcp::Peer& peer) { Handler::onInput(const char* buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer) {
try { try {
auto& parser = getParser(peer); auto& parser = getParser(peer);
// scope (failure), {
// parser.reset();
// };
if (!parser.feed(buffer, len)) { if (!parser.feed(buffer, len)) {
parser.reset(); parser.reset();
throw HttpError(Code::Request_Entity_Too_Large, "Request exceeded maximum buffer size"); throw HttpError(Code::Request_Entity_Too_Large, "Request exceeded maximum buffer size");
} }
auto state = parser.parse(); auto state = parser.parse();
if (state == Private::Parser::State::Done) { if (state == Private::State::Done) {
onRequest(parser.request, peer); Response response;
response.associatePeer(peer);
onRequest(parser.request, std::move(response));
parser.reset(); parser.reset();
} }
} catch (const HttpError &err) { } catch (const HttpError &err) {
Response response(err.code(), err.reason()); Response response;
response.writeTo(peer); response.associatePeer(peer);
response.send(static_cast<Code>(err.code()), err.reason());
getParser(peer).reset(); getParser(peer).reset();
} }
catch (const std::exception& e) { catch (const std::exception& e) {
Response response(Code::Internal_Server_Error, e.what()); Response response;
response.writeTo(peer); response.associatePeer(peer);
response.send(Code::Internal_Server_Error, e.what());
getParser(peer).reset(); getParser(peer).reset();
} }
} }
...@@ -336,17 +395,17 @@ Handler::onOutput() { ...@@ -336,17 +395,17 @@ Handler::onOutput() {
} }
void void
Handler::onConnection(Tcp::Peer& peer) { Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer) {
peer.putData(ParserData, std::make_shared<Private::Parser>()); peer->putData(ParserData, std::make_shared<Private::Parser>());
} }
void void
Handler::onDisconnection(Tcp::Peer& peer) { Handler::onDisconnection(const shared_ptr<Tcp::Peer>& peer) {
} }
Private::Parser& Private::Parser&
Handler::getParser(Tcp::Peer& peer) const { Handler::getParser(const std::shared_ptr<Tcp::Peer>& peer) const {
return *peer.getData<Private::Parser>(ParserData); return *peer->getData<Private::Parser>(ParserData);
} }
Endpoint::Endpoint() Endpoint::Endpoint()
......
...@@ -14,88 +14,127 @@ ...@@ -14,88 +14,127 @@
#include "http_headers.h" #include "http_headers.h"
#include "http_defs.h" #include "http_defs.h"
#include "stream.h" #include "stream.h"
#include "mime.h"
namespace Net { namespace Net {
namespace Http { namespace Http {
namespace Private {
class Parser;
class RequestLineStep;
class HeadersStep;
class BodyStep;
}
// 4. HTTP Message // 4. HTTP Message
class Message { class Message {
public: public:
Message(); Message();
Version version; Version version_;
Header::Collection headers; Header::Collection headers_;
std::string body; std::string body_;
}; };
// 5. Request // 5. Request
class Request : public Message { class Request : private Message {
public: public:
friend class Private::RequestLineStep;
friend class Private::HeadersStep;
friend class Private::BodyStep;
friend class Private::Parser;
Request(); Request();
Method method; Version version() const;
std::string resource; Method method() const;
std::string resource() const;
std::string body() const;
const Header::Collection& headers() const;
private:
Method method_;
std::string resource_;
}; };
class Handler;
// 6. Response // 6. Response
class Response : public Message { class Response : private Message {
public: public:
Response(int code, std::string body); friend class Handler;
Response(Code code, std::string body);
Response(const Response& other) = delete;
Response& operator=(const Response& other) = delete;
Response(Response&& other) = default;
Response& operator=(Response&& other) = default;
void writeTo(Tcp::Peer& peer); Header::Collection& headers();
std::string mimeType; const Header::Collection& headers() const;
void setMime(const Mime::MediaType& mime);
ssize_t send(Code code);
ssize_t send(Code code, const std::string& body, const Mime::MediaType &mime = Mime::MediaType());
private: private:
int code_; Response();
std::shared_ptr<Tcp::Peer> peer() const;
void associatePeer(const std::shared_ptr<Tcp::Peer>& peer);
std::weak_ptr<Tcp::Peer> peer_;
}; };
namespace Private { namespace Private {
struct Parser { enum class State { Again, Next, Done };
enum class State { Again, Next, Done }; struct Step {
Step(Request* request)
: request(request)
{ }
struct Step { virtual State apply(StreamCursor& cursor) = 0;
Step(Request* request)
: request(request)
{ }
virtual State apply(StreamCursor& cursor) = 0; void raise(const char* msg, Code code = Code::Bad_Request);
void raise(const char* msg, Code code = Code::Bad_Request); Request *request;
};
Request *request; struct RequestLineStep : public Step {
}; RequestLineStep(Request* request)
: Step(request)
{ }
struct RequestLineStep : public Step { State apply(StreamCursor& cursor);
RequestLineStep(Request* request) };
: Step(request)
{ }
State apply(StreamCursor& cursor); struct HeadersStep : public Step {
}; HeadersStep(Request* request)
: Step(request)
{ }
struct HeadersStep : public Step { State apply(StreamCursor& cursor);
HeadersStep(Request* request) };
: Step(request)
{ }
State apply(StreamCursor& cursor); struct BodyStep : public Step {
}; BodyStep(Request* request)
: Step(request)
, bytesRead(0)
{ }
struct BodyStep : public Step { State apply(StreamCursor& cursor);
BodyStep(Request* request)
: Step(request)
, bytesRead(0)
{ }
State apply(StreamCursor& cursor); private:
size_t bytesRead;
};
private: struct Parser {
size_t bytesRead;
};
Parser() Parser()
: contentLength(-1) : contentLength(-1)
...@@ -127,8 +166,6 @@ namespace Private { ...@@ -127,8 +166,6 @@ namespace Private {
State parse(); State parse();
// Buffer buffer;
// Cursor cursor;
ArrayStreamBuf<Const::MaxBuffer> buffer; ArrayStreamBuf<Const::MaxBuffer> buffer;
StreamCursor cursor; StreamCursor cursor;
...@@ -147,16 +184,16 @@ namespace Private { ...@@ -147,16 +184,16 @@ namespace Private {
class Handler : public Net::Tcp::Handler { class Handler : public Net::Tcp::Handler {
public: public:
void onInput(const char* buffer, size_t len, Tcp::Peer& peer); void onInput(const char* buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer);
void onOutput(); void onOutput();
void onConnection(Tcp::Peer& peer); void onConnection(const std::shared_ptr<Tcp::Peer>& peer);
void onDisconnection(Tcp::Peer& peer); void onDisconnection(const std::shared_ptr<Tcp::Peer>& peer);
virtual void onRequest(const Request& request, Tcp::Peer& peer) = 0; virtual void onRequest(const Request& request, Response response) = 0;
private: private:
Private::Parser& getParser(Tcp::Peer& peer) const; Private::Parser& getParser(const std::shared_ptr<Tcp::Peer>& peer) const;
}; };
class Endpoint { class Endpoint {
......
...@@ -118,8 +118,8 @@ IoWorker::pin(const CpuSet& set) { ...@@ -118,8 +118,8 @@ IoWorker::pin(const CpuSet& set) {
} }
} }
std::shared_ptr<Peer> std::shared_ptr<Peer>&
IoWorker::getPeer(Fd fd) const IoWorker::getPeer(Fd fd)
{ {
std::unique_lock<std::mutex> guard(peersMutex); std::unique_lock<std::mutex> guard(peersMutex);
auto it = peers.find(fd); auto it = peers.find(fd);
...@@ -130,8 +130,8 @@ IoWorker::getPeer(Fd fd) const ...@@ -130,8 +130,8 @@ IoWorker::getPeer(Fd fd) const
return it->second; return it->second;
} }
std::shared_ptr<Peer> std::shared_ptr<Peer>&
IoWorker::getPeer(Polling::Tag tag) const IoWorker::getPeer(Polling::Tag tag)
{ {
return getPeer(tag.value()); return getPeer(tag.value());
} }
...@@ -153,11 +153,11 @@ IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) { ...@@ -153,11 +153,11 @@ IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) {
if (bytes == -1) { if (bytes == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
if (totalBytes > 0) { if (totalBytes > 0) {
handler_->onInput(buffer, totalBytes, *peer); handler_->onInput(buffer, totalBytes, peer);
} }
} else { } else {
if (errno == ECONNRESET) { if (errno == ECONNRESET) {
handler_->onDisconnection(*peer); handler_->onDisconnection(peer);
close(fd); close(fd);
} }
else { else {
...@@ -167,7 +167,7 @@ IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) { ...@@ -167,7 +167,7 @@ IoWorker::handleIncoming(const std::shared_ptr<Peer>& peer) {
break; break;
} }
else if (bytes == 0) { else if (bytes == 0) {
handler_->onDisconnection(*peer); handler_->onDisconnection(peer);
close(fd); close(fd);
break; break;
} }
...@@ -192,7 +192,7 @@ IoWorker::handleNewPeer(const std::shared_ptr<Peer>& peer) ...@@ -192,7 +192,7 @@ IoWorker::handleNewPeer(const std::shared_ptr<Peer>& peer)
peers.insert(std::make_pair(fd, peer)); peers.insert(std::make_pair(fd, peer));
} }
handler_->onConnection(*peer); handler_->onConnection(peer);
poller.addFd(fd, NotifyOn::Read, Polling::Tag(fd), Polling::Mode::Edge); poller.addFd(fd, NotifyOn::Read, Polling::Tag(fd), Polling::Mode::Edge);
} }
...@@ -226,7 +226,8 @@ IoWorker::run() { ...@@ -226,7 +226,8 @@ IoWorker::run() {
} }
} else { } else {
if (event.flags.hasFlag(NotifyOn::Read)) { if (event.flags.hasFlag(NotifyOn::Read)) {
handleIncoming(getPeer(event.tag)); auto& peer = getPeer(event.tag);
handleIncoming(peer);
} }
} }
} }
...@@ -247,11 +248,11 @@ Handler::~Handler() ...@@ -247,11 +248,11 @@ Handler::~Handler()
{ } { }
void void
Handler::onConnection(Tcp::Peer& peer) { Handler::onConnection(const std::shared_ptr<Tcp::Peer>& peer) {
} }
void void
Handler::onDisconnection(Tcp::Peer& peer) { Handler::onDisconnection(const std::shared_ptr<Tcp::Peer>& peer) {
} }
Listener::Listener() Listener::Listener()
......
...@@ -61,8 +61,8 @@ private: ...@@ -61,8 +61,8 @@ private:
CpuSet pins; CpuSet pins;
std::shared_ptr<Peer> getPeer(Fd fd) const; std::shared_ptr<Peer>& getPeer(Fd fd);
std::shared_ptr<Peer> getPeer(Polling::Tag tag) const; std::shared_ptr<Peer>& getPeer(Polling::Tag tag);
void handleIncoming(const std::shared_ptr<Peer>& peer); void handleIncoming(const std::shared_ptr<Peer>& peer);
void handleMailbox(); void handleMailbox();
...@@ -108,11 +108,11 @@ public: ...@@ -108,11 +108,11 @@ public:
Handler(); Handler();
~Handler(); ~Handler();
virtual void onInput(const char *buffer, size_t len, Tcp::Peer& peer) = 0; virtual void onInput(const char *buffer, size_t len, const std::shared_ptr<Tcp::Peer>& peer) = 0;
virtual void onOutput() = 0; virtual void onOutput() = 0;
virtual void onConnection(Tcp::Peer& peer); virtual void onConnection(const std::shared_ptr<Tcp::Peer>& peer);
virtual void onDisconnection(Tcp::Peer& peer); virtual void onDisconnection(const std::shared_ptr<Tcp::Peer>& peer);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment