Unverified Commit a6c8da47 authored by Dennis Jenkins's avatar Dennis Jenkins Committed by GitHub

Merge pull request #695 from hyperxor/refactoring_in_http

Refactoring in http.h and http.cc
parents 9a0f8984 4099bf12
...@@ -70,6 +70,7 @@ class Message { ...@@ -70,6 +70,7 @@ class Message {
public: public:
friend class Private::HeadersStep; friend class Private::HeadersStep;
friend class Private::BodyStep; friend class Private::BodyStep;
friend class ResponseWriter;
Message() = default; Message() = default;
explicit Message(Version version); explicit Message(Version version);
...@@ -279,35 +280,18 @@ private: ...@@ -279,35 +280,18 @@ private:
std::weak_ptr<Tcp::Peer> peer; std::weak_ptr<Tcp::Peer> peer;
}; };
class ResponseStream : public Message { class ResponseStream final {
public: public:
friend class ResponseWriter; friend class ResponseWriter;
ResponseStream(ResponseStream &&other) ResponseStream(ResponseStream &&other);
: Message(std::move(other)), peer_(std::move(other.peer_)),
buf_(std::move(other.buf_)), transport_(other.transport_),
timeout_(std::move(other.timeout_)) {}
ResponseStream &operator=(ResponseStream &&other) { ResponseStream &operator=(ResponseStream &&other);
Message::operator=(std::move(other));
peer_ = std::move(other.peer_);
buf_ = std::move(other.buf_);
transport_ = other.transport_;
timeout_ = std::move(other.timeout_);
return *this;
}
template <typename T> template <typename T>
friend ResponseStream &operator<<(ResponseStream &stream, const T &val); friend ResponseStream &operator<<(ResponseStream &stream, const T &val);
std::streamsize write(const char *data, std::streamsize sz) { std::streamsize write(const char *data, std::streamsize sz);
std::ostream os(&buf_);
os << std::hex << sz << crlf;
os.write(data, sz);
os << crlf;
return sz;
}
void flush(); void flush();
void ends(); void ends();
...@@ -316,13 +300,9 @@ private: ...@@ -316,13 +300,9 @@ private:
ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer, ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer,
Tcp::Transport *transport, Timeout timeout, size_t streamSize); Tcp::Transport *transport, Timeout timeout, size_t streamSize);
std::shared_ptr<Tcp::Peer> peer() const { std::shared_ptr<Tcp::Peer> peer() const;
if (peer_.expired())
throw std::runtime_error("Write failed: Broken pipe");
return peer_.lock();
}
Message response_;
std::weak_ptr<Tcp::Peer> peer_; std::weak_ptr<Tcp::Peer> peer_;
DynamicStreamBuf buf_; DynamicStreamBuf buf_;
Tcp::Transport *transport_; Tcp::Transport *transport_;
...@@ -371,7 +351,7 @@ public: ...@@ -371,7 +351,7 @@ public:
Response &operator=(Response &&other) = default; Response &operator=(Response &&other) = default;
}; };
class ResponseWriter : public Response { class ResponseWriter final {
public: public:
static constexpr size_t DefaultStreamSize = 512; static constexpr size_t DefaultStreamSize = 512;
...@@ -389,26 +369,11 @@ public: ...@@ -389,26 +369,11 @@ public:
// C++11: std::weak_ptr move constructor is C++14 only so the default // C++11: std::weak_ptr move constructor is C++14 only so the default
// version of move constructor / assignement operator does not work and we // version of move constructor / assignement operator does not work and we
// have to define it ourself // have to define it ourself
ResponseWriter(ResponseWriter &&other) ResponseWriter(ResponseWriter &&other);
: Response(std::move(other)), peer_(other.peer_),
buf_(std::move(other.buf_)), transport_(other.transport_),
timeout_(std::move(other.timeout_)) {}
ResponseWriter &operator=(ResponseWriter &&other) {
Response::operator=(std::move(other));
peer_ = std::move(other.peer_);
transport_ = other.transport_;
buf_ = std::move(other.buf_);
timeout_ = std::move(other.timeout_);
return *this;
}
void setMime(const Mime::MediaType &mime) { ResponseWriter &operator=(ResponseWriter &&other);
auto ct = headers_.tryGet<Header::ContentType>();
if (ct) void setMime(const Mime::MediaType &mime);
ct->setMime(mime);
else
headers_.add(std::make_shared<Header::ContentType>(mime));
}
/* @Feature: add helper functions for common http return code: /* @Feature: add helper functions for common http return code:
* - halt() -> 404 * - halt() -> 404
...@@ -416,98 +381,53 @@ public: ...@@ -416,98 +381,53 @@ public:
* - moved() -> 302 * - moved() -> 302
*/ */
Async::Promise<ssize_t> Async::Promise<ssize_t>
sendMethodNotAllowed(const std::vector<Http::Method> &supportedMethods) { sendMethodNotAllowed(const std::vector<Http::Method> &supportedMethods);
code_ = Http::Code::Method_Not_Allowed;
headers_.add(std::make_shared<Http::Header::Allow>(supportedMethods));
std::string body = codeString(Pistache::Http::Code::Method_Not_Allowed);
return putOnWire(body.c_str(), body.size());
}
Async::Promise<ssize_t> send(Code code) {
code_ = code;
return putOnWire(nullptr, 0);
}
Async::Promise<ssize_t>
send(Code code, const std::string &body,
const Mime::MediaType &mime = Mime::MediaType()) {
code_ = code;
if (mime.isValid()) {
auto contentType = headers_.tryGet<Header::ContentType>();
if (contentType)
contentType->setMime(mime);
else
headers_.add(std::make_shared<Header::ContentType>(mime));
}
return putOnWire(body.c_str(), body.size()); Async::Promise<ssize_t> send(Code code, const std::string &body = "",
} const Mime::MediaType &mime = Mime::MediaType());
template <size_t N> template <size_t N>
Async::Promise<ssize_t> Async::Promise<ssize_t>
send(Code code, const char (&arr)[N], send(Code code, const char (&arr)[N],
const Mime::MediaType &mime = Mime::MediaType()) { const Mime::MediaType &mime = Mime::MediaType()) {
return send(code, arr, N - 1, std::forward<const Mime::MediaType &>(mime)); return sendImpl(code, arr, N - 1, mime);
} }
Async::Promise<ssize_t> Async::Promise<ssize_t> send(Code code, const char *data, const size_t size,
send(Code code, const char *data, const size_t size, const Mime::MediaType &mime = Mime::MediaType());
const Mime::MediaType &mime = Mime::MediaType()) {
/* @Refactor: code duplication */
code_ = code;
if (mime.isValid()) {
auto contentType = headers_.tryGet<Header::ContentType>();
if (contentType)
contentType->setMime(mime);
else
headers_.add(std::make_shared<Header::ContentType>(mime));
}
return putOnWire(data, size); ResponseStream stream(Code code, size_t streamSize = DefaultStreamSize);
}
ResponseStream stream(Code code, size_t streamSize = DefaultStreamSize) {
code_ = code;
return ResponseStream(std::move(*this), peer_, transport_,
std::move(timeout_), streamSize);
}
template <typename Duration> void timeoutAfter(Duration duration) { template <typename Duration> void timeoutAfter(Duration duration) {
timeout_.arm(duration); timeout_.arm(duration);
} }
Timeout &timeout() { return timeout_; } const CookieJar &cookies() const;
CookieJar &cookies();
std::shared_ptr<Tcp::Peer> peer() const { const Header::Collection &headers() const;
if (peer_.expired()) Header::Collection &headers();
throw std::runtime_error("Write failed: Broken pipe");
return peer_.lock(); Timeout &timeout();
}
std::shared_ptr<Tcp::Peer> peer() const;
// Unsafe API // Unsafe API
DynamicStreamBuf *rdbuf() { return &buf_; } DynamicStreamBuf *rdbuf();
DynamicStreamBuf *rdbuf(DynamicStreamBuf *other) { DynamicStreamBuf *rdbuf(DynamicStreamBuf *other);
UNUSED(other)
throw std::domain_error("Unimplemented");
}
ResponseWriter clone() const { return ResponseWriter(*this); } ResponseWriter clone() const;
private: private:
ResponseWriter(Tcp::Transport *transport, Request request, Handler *handler) ResponseWriter(Tcp::Transport *transport, Request request, Handler *handler);
: Response(request.version()), peer_(), buf_(DefaultStreamSize),
transport_(transport), ResponseWriter(const ResponseWriter &other);
timeout_(transport, handler, std::move(request)) {}
ResponseWriter(const ResponseWriter &other) Async::Promise<ssize_t> sendImpl(Code code, const char *data,
: Response(other), peer_(other.peer_), buf_(DefaultStreamSize), const size_t size,
transport_(other.transport_), timeout_(other.timeout_) {} const Mime::MediaType &mime);
template <typename Ptr> void associatePeer(const Ptr &peer) { template <typename Ptr> void associatePeer(const Ptr &peer) {
if (peer_.use_count() > 0) if (peer_.use_count() > 0)
...@@ -519,6 +439,7 @@ private: ...@@ -519,6 +439,7 @@ private:
Async::Promise<ssize_t> putOnWire(const char *data, size_t len); Async::Promise<ssize_t> putOnWire(const char *data, size_t len);
Response response_;
std::weak_ptr<Tcp::Peer> peer_; std::weak_ptr<Tcp::Peer> peer_;
DynamicStreamBuf buf_; DynamicStreamBuf buf_;
Tcp::Transport *transport_; Tcp::Transport *transport_;
...@@ -526,7 +447,7 @@ private: ...@@ -526,7 +447,7 @@ private:
}; };
Async::Promise<ssize_t> Async::Promise<ssize_t>
serveFile(ResponseWriter &response, const std::string &fileName, serveFile(ResponseWriter &writer, const std::string &fileName,
const Mime::MediaType &contentType = Mime::MediaType()); const Mime::MediaType &contentType = Mime::MediaType());
namespace Private { namespace Private {
......
...@@ -567,19 +567,24 @@ std::shared_ptr<Tcp::Peer> Request::peer() const { ...@@ -567,19 +567,24 @@ std::shared_ptr<Tcp::Peer> Request::peer() const {
} }
#endif #endif
ResponseStream::ResponseStream(ResponseStream &&other)
: response_(std::move(other.response_)), peer_(std::move(other.peer_)),
buf_(std::move(other.buf_)), transport_(other.transport_),
timeout_(std::move(other.timeout_)) {}
ResponseStream::ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer, ResponseStream::ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer,
Tcp::Transport *transport, Timeout timeout, Tcp::Transport *transport, Timeout timeout,
size_t streamSize) size_t streamSize)
: Message(std::move(other)), peer_(std::move(peer)), buf_(streamSize), : response_(std::move(other)), peer_(std::move(peer)), buf_(streamSize),
transport_(transport), timeout_(std::move(timeout)) { transport_(transport), timeout_(std::move(timeout)) {
if (!writeStatusLine(version_, code_, buf_)) if (!writeStatusLine(response_.version(), response_.code(), buf_))
throw Error("Response exceeded buffer size"); throw Error("Response exceeded buffer size");
if (!writeCookies(cookies_, buf_)) { if (!writeCookies(response_.cookies(), buf_)) {
throw Error("Response exceeded buffer size"); throw Error("Response exceeded buffer size");
} }
if (writeHeaders(headers_, buf_)) { if (writeHeaders(response_.headers(), buf_)) {
std::ostream os(&buf_); std::ostream os(&buf_);
/* @Todo @Major: /* @Todo @Major:
* Correctly handle non-keep alive requests * Correctly handle non-keep alive requests
...@@ -595,6 +600,32 @@ ResponseStream::ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer, ...@@ -595,6 +600,32 @@ ResponseStream::ResponseStream(Message &&other, std::weak_ptr<Tcp::Peer> peer,
} }
} }
ResponseStream &ResponseStream::operator=(ResponseStream &&other) {
response_ = std::move(other.response_);
peer_ = std::move(other.peer_);
buf_ = std::move(other.buf_);
transport_ = other.transport_;
timeout_ = std::move(other.timeout_);
return *this;
}
std::streamsize ResponseStream::write(const char *data, std::streamsize sz) {
std::ostream os(&buf_);
os << std::hex << sz << crlf;
os.write(data, sz);
os << crlf;
return sz;
}
std::shared_ptr<Tcp::Peer> ResponseStream::peer() const {
if (peer_.expired()) {
throw std::runtime_error("Write failed: Broken pipe");
}
return peer_.lock();
}
void ResponseStream::flush() { void ResponseStream::flush() {
timeout_.disarm(); timeout_.disarm();
auto buf = buf_.buffer(); auto buf = buf_.buffer();
...@@ -617,6 +648,114 @@ void ResponseStream::ends() { ...@@ -617,6 +648,114 @@ void ResponseStream::ends() {
flush(); flush();
} }
ResponseWriter::ResponseWriter(ResponseWriter &&other)
: response_(std::move(other.response_)), peer_(other.peer_),
buf_(std::move(other.buf_)), transport_(other.transport_),
timeout_(std::move(other.timeout_)) {}
ResponseWriter::ResponseWriter(Tcp::Transport *transport, Request request,
Handler *handler)
: response_(request.version()), peer_(), buf_(DefaultStreamSize),
transport_(transport), timeout_(transport, handler, std::move(request)) {}
ResponseWriter::ResponseWriter(const ResponseWriter &other)
: response_(other.response_), peer_(other.peer_), buf_(DefaultStreamSize),
transport_(other.transport_), timeout_(other.timeout_) {}
ResponseWriter &ResponseWriter::operator=(ResponseWriter &&other) {
response_ = std::move(other.response_);
peer_ = std::move(other.peer_);
transport_ = other.transport_;
buf_ = std::move(other.buf_);
timeout_ = std::move(other.timeout_);
return *this;
}
void ResponseWriter::setMime(const Mime::MediaType &mime) {
auto ct = response_.headers().tryGet<Header::ContentType>();
if (ct) {
ct->setMime(mime);
}
else {
response_.headers().add(std::make_shared<Header::ContentType>(mime));
}
}
Async::Promise<ssize_t> ResponseWriter::sendMethodNotAllowed(
const std::vector<Http::Method> &supportedMethods) {
response_.code_ = Http::Code::Method_Not_Allowed;
response_.headers().add(
std::make_shared<Http::Header::Allow>(supportedMethods));
const std::string &body =
codeString(Pistache::Http::Code::Method_Not_Allowed);
return putOnWire(body.c_str(), body.size());
}
Async::Promise<ssize_t> ResponseWriter::send(Code code, const std::string &body,
const Mime::MediaType &mime) {
return sendImpl(code, body.c_str(), body.size(), mime);
}
Async::Promise<ssize_t> ResponseWriter::send(Code code, const char *data,
const size_t size,
const Mime::MediaType &mime) {
return sendImpl(code, data, size, mime);
}
Async::Promise<ssize_t> ResponseWriter::sendImpl(Code code, const char *data,
const size_t size,
const Mime::MediaType &mime) {
response_.code_ = code;
if (mime.isValid()) {
auto contentType = headers().tryGet<Header::ContentType>();
if (contentType) {
contentType->setMime(mime);
}
else {
headers().add(std::make_shared<Header::ContentType>(mime));
}
}
return putOnWire(data, size);
}
ResponseStream ResponseWriter::stream(Code code, size_t streamSize) {
response_.code_ = code;
return ResponseStream(std::move(response_), peer_, transport_,
std::move(timeout_), streamSize);
}
const CookieJar &ResponseWriter::cookies() const { return response_.cookies(); }
CookieJar &ResponseWriter::cookies() { return response_.cookies(); }
const Header::Collection &ResponseWriter::headers() const {
return response_.headers();
}
Header::Collection &ResponseWriter::headers() { return response_.headers(); }
Timeout &ResponseWriter::timeout() { return timeout_; }
std::shared_ptr<Tcp::Peer> ResponseWriter::peer() const {
if (peer_.expired()) {
throw std::runtime_error("Write failed: Broken pipe");
}
return peer_.lock();
}
DynamicStreamBuf *ResponseWriter::rdbuf() { return &buf_; }
DynamicStreamBuf *ResponseWriter::rdbuf(DynamicStreamBuf *other) {
UNUSED(other)
throw std::domain_error("Unimplemented");
}
ResponseWriter ResponseWriter::clone() const { return ResponseWriter(*this); }
Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data, Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data,
size_t len) { size_t len) {
try { try {
...@@ -631,9 +770,9 @@ Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data, ...@@ -631,9 +770,9 @@ Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data,
} \ } \
} while (0); } while (0);
OUT(writeStatusLine(version_, code_, buf_)); OUT(writeStatusLine(response_.version(), response_.code(), buf_));
OUT(writeHeaders(headers_, buf_)); OUT(writeHeaders(response_.headers(), buf_));
OUT(writeCookies(cookies_, buf_)); OUT(writeCookies(response_.cookies(), buf_));
/* @Todo @Major: /* @Todo @Major:
* Correctly handle non-keep alive requests * Correctly handle non-keep alive requests
...@@ -676,7 +815,7 @@ Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data, ...@@ -676,7 +815,7 @@ Async::Promise<ssize_t> ResponseWriter::putOnWire(const char *data,
} }
} }
Async::Promise<ssize_t> serveFile(ResponseWriter &response, Async::Promise<ssize_t> serveFile(ResponseWriter &writer,
const std::string &fileName, const std::string &fileName,
const Mime::MediaType &contentType) { const Mime::MediaType &contentType) {
struct stat sb; struct stat sb;
...@@ -702,7 +841,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response, ...@@ -702,7 +841,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response,
throw HttpError(Code::Internal_Server_Error, ""); throw HttpError(Code::Internal_Server_Error, "");
} }
auto *buf = response.rdbuf(); auto *buf = writer.rdbuf();
std::ostream os(buf); std::ostream os(buf);
...@@ -716,7 +855,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response, ...@@ -716,7 +855,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response,
} while (0); } while (0);
auto setContentType = [&](const Mime::MediaType &contentType) { auto setContentType = [&](const Mime::MediaType &contentType) {
auto &headers = response.headers(); auto &headers = writer.headers();
auto ct = headers.tryGet<Header::ContentType>(); auto ct = headers.tryGet<Header::ContentType>();
if (ct) if (ct)
ct->setMime(contentType); ct->setMime(contentType);
...@@ -724,7 +863,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response, ...@@ -724,7 +863,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response,
headers.add<Header::ContentType>(contentType); headers.add<Header::ContentType>(contentType);
}; };
OUT(writeStatusLine(response.version(), Http::Code::Ok, *buf)); OUT(writeStatusLine(writer.response_.version(), Http::Code::Ok, *buf));
if (contentType.isValid()) { if (contentType.isValid()) {
setContentType(contentType); setContentType(contentType);
} else { } else {
...@@ -733,7 +872,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response, ...@@ -733,7 +872,7 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response,
setContentType(mime); setContentType(mime);
} }
OUT(writeHeaders(response.headers(), *buf)); OUT(writeHeaders(writer.headers(), *buf));
const size_t len = sb.st_size; const size_t len = sb.st_size;
...@@ -741,8 +880,8 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response, ...@@ -741,8 +880,8 @@ Async::Promise<ssize_t> serveFile(ResponseWriter &response,
OUT(os << crlf); OUT(os << crlf);
auto *transport = response.transport_; auto *transport = writer.transport_;
auto peer = response.peer(); auto peer = writer.peer();
auto sockFd = peer->fd(); auto sockFd = peer->fd();
auto buffer = buf->buffer(); auto buffer = buf->buffer();
......
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