From 8a2a19ac376c283fac9066770682bccd282478d4 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sun, 28 Apr 2019 00:17:32 +0000 Subject: [PATCH] Lots of file splits --- .gitignore | 1 + Makefile | 9 +++-- buffer.cpp | 66 ++++++++++++++++++++++++++++++++ buffer.h | 48 ++++++++++++++++++++++++ fastcgi.cpp | 47 +++++++++++++++++++++++ fastcgi.h | 10 +++++ fastcgi_conn.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ fastcgi_conn.h | 19 ++++++++++ mirall.cpp | 33 ++-------------- 9 files changed, 298 insertions(+), 32 deletions(-) create mode 100644 buffer.cpp create mode 100644 buffer.h create mode 100644 fastcgi.cpp create mode 100644 fastcgi.h create mode 100644 fastcgi_conn.cpp create mode 100644 fastcgi_conn.h diff --git a/.gitignore b/.gitignore index 4b4c904..a96a5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ mirall +*.o diff --git a/Makefile b/Makefile index c9dbbe7..3f2e5c9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ -mirall: Makefile mirall.cpp - g++ -std=gnu++2a -o mirall mirall.cpp -lgflags -lglog +mirall: Makefile mirall.o fastcgi.o fastcgi_conn.o buffer.o + g++ -std=gnu++2a -o mirall mirall.o fastcgi.o fastcgi_conn.o buffer.o -lgflags -lglog -lpthread clean: - rm --force mirall + rm --force mirall *.o + +.cpp.o: Makefile *.h + g++ -std=gnu++2a -o $@ -c $< diff --git a/buffer.cpp b/buffer.cpp new file mode 100644 index 0000000..05a1547 --- /dev/null +++ b/buffer.cpp @@ -0,0 +1,66 @@ +#include "buffer.h" + +ConstBuffer::ConstBuffer(const char *buf, size_t len) + : const_buf_(buf), + len_(len) {} + +size_t ConstBuffer::ReadMaxLen() const { + return len_ - start_; +} + +const char *ConstBuffer::Read(size_t len) { + CHECK_LE(len, ReadMaxLen()); + const auto *ret = &const_buf_[start_]; + start_ += len; + return ret; +} + +bool ConstBuffer::Discard(size_t len) { + if (len > ReadMaxLen()) { + return false; + } + Read(len); + return true; +} + +void ConstBuffer::ResetRead() { + start_ = commit_; +} + +void ConstBuffer::Commit() { + commit_ = start_; +} + + +Buffer::Buffer(char *buf, size_t size, size_t len) + : ConstBuffer(buf, size), + buf_(buf), + size_(size) { + len_ = len; +} + +Buffer::Buffer(size_t size) + : Buffer((own_buf_.reset(new char[size]), own_buf_.get()), size, 0) {} + +char *Buffer::WritePtr() { + return &buf_[len_]; +} + +size_t Buffer::WriteMaxLen() const { + return size_ - len_; +} + +void Buffer::Wrote(size_t len) { + CHECK_LE(len, WriteMaxLen()); + len_ += len; +} + +void Buffer::Consume() { + if (commit_ == 0) { + return; + } + memmove(buf_, &buf_[commit_], len_ - commit_); + len_ -= commit_; + start_ -= commit_; + commit_ = 0; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..334c8e3 --- /dev/null +++ b/buffer.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +class ConstBuffer { + public: + ConstBuffer(const char *buf, size_t len); + + size_t ReadMaxLen() const; + const char *Read(size_t len); + template const T *ReadObj(); + + bool Discard(size_t len); // like Read() but don't use the result + void ResetRead(); // next read from last commit + void Commit(); // commit read position + + protected: + const char *const_buf_; + size_t len_; + size_t start_ = 0; + size_t commit_ = 0; +}; + +class Buffer : public ConstBuffer { + public: + Buffer(const char *buf, size_t len) = delete; + Buffer(char *buf, size_t size, size_t len); + Buffer(size_t size); + + char *WritePtr(); + size_t WriteMaxLen() const; + void Wrote(size_t len); + + void Consume(); // discard up to last commit + + private: + std::unique_ptr own_buf_; + char *buf_; + const size_t size_; +}; + +template const T *ConstBuffer::ReadObj() { + if (ReadMaxLen() < sizeof(T)) { + return nullptr; + } + return reinterpret_cast(Read(sizeof(T))); +} diff --git a/fastcgi.cpp b/fastcgi.cpp new file mode 100644 index 0000000..d43258f --- /dev/null +++ b/fastcgi.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include + +#include "fastcgi.h" +#include "fastcgi_conn.h" + +FastCGIServer::FastCGIServer(int port) { + LOG(INFO) << "listening on [::1]:" << port; + + listen_sock_ = socket(AF_INET6, SOCK_STREAM, 0); + PCHECK(listen_sock_ >= 0) << "socket()"; + + { + int optval = 1; + PCHECK(setsockopt(listen_sock_, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == 0); + } + + { + sockaddr_in6 bind_addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = IN6ADDR_LOOPBACK_INIT, + }; + PCHECK(bind(listen_sock_, (sockaddr*) &bind_addr, sizeof(bind_addr)) == 0); + } + + PCHECK(listen(listen_sock_, 128) == 0); +} + +void FastCGIServer::Serve() { + while (true) { + sockaddr_in6 client_addr; + socklen_t client_addr_len = sizeof(client_addr); + + auto client_sock = accept(listen_sock_, (sockaddr*) &client_addr, &client_addr_len); + PCHECK(client_sock >= 0) << "accept()"; + CHECK_EQ(client_addr.sin6_family, AF_INET6); + + auto *conn = new FastCGIConn(client_sock, client_addr); + std::thread thread([conn]() { conn->Serve(); }); + thread.detach(); + } +} + + diff --git a/fastcgi.h b/fastcgi.h new file mode 100644 index 0000000..8757110 --- /dev/null +++ b/fastcgi.h @@ -0,0 +1,10 @@ +#pragma once + +class FastCGIServer { + public: + FastCGIServer(int port); + void Serve(); + + private: + int listen_sock_; +}; diff --git a/fastcgi_conn.cpp b/fastcgi_conn.cpp new file mode 100644 index 0000000..7cb5aac --- /dev/null +++ b/fastcgi_conn.cpp @@ -0,0 +1,97 @@ +#include +#include + +#include "fastcgi_conn.h" + +namespace { + +struct fcgi_header { + uint8_t version; + uint8_t type; + private: + uint16_t request_id_; // network byte order + uint16_t content_length_; // network byte order + public: + uint8_t padding_length; + uint8_t reserved; + + uint16_t ContentLength() const { return htons(content_length_); } +}; + +struct fcgi_begin_request { + uint16_t role; // network byte order + uint8_t flags; + uint8_t reserved[5]; +}; + +constexpr auto fcgi_max_content_len = 65535; +constexpr auto fcgi_max_padding_len = 255; +constexpr auto fcgi_max_record_len = sizeof(fcgi_header) + fcgi_max_content_len + fcgi_max_padding_len; + +} // namespace + +FastCGIConn::FastCGIConn(int sock, const sockaddr_in6& client_addr) + : sock_(sock), + buf_(fcgi_max_record_len) { + char client_addr_str[INET6_ADDRSTRLEN]; + PCHECK(inet_ntop(AF_INET6, &client_addr.sin6_addr, client_addr_str, sizeof(client_addr_str))); + + LOG(INFO) << "new connection: [" << client_addr_str << "]:" << ntohs(client_addr.sin6_port); +} + +FastCGIConn::~FastCGIConn() { + PCHECK(close(sock_) == 0); + LOG(INFO) << "connection closed"; +} + +void FastCGIConn::Serve() { + while (true) { + auto read_len = read(sock_, buf_.WritePtr(), buf_.WriteMaxLen()); + PCHECK(read_len >= 0); + if (read_len == 0) { + LOG(INFO) << "peer closed connection"; + } + buf_.Wrote(read_len); + + ParseBuf(); + buf_.Consume(); // free buffer tail space for next read + } +} + +void FastCGIConn::ParseBuf() { + buf_.ResetRead(); + + while (true) { + const auto *header = buf_.ReadObj(); + if (!header) { + return; + } + + CHECK_EQ(header->version, 1); + if (buf_.ReadMaxLen() < header->ContentLength() + header->padding_length) { + return; + } + + LOG(INFO) << "type: " << (int)header->type; + + switch (header->type) { + case 1: + { + CHECK_EQ(header->ContentLength(), sizeof(fcgi_begin_request)); + const auto *begin_request = CHECK_NOTNULL(buf_.ReadObj()); + CHECK_EQ(ntohs(begin_request->role), 1); + } + break; + + case 4: + { + const auto *params = buf_.Read(header->ContentLength()); + } + break; + } + + CHECK(buf_.Discard(header->padding_length)); + + buf_.Commit(); // we've acted on the bytes read so far + } +} diff --git a/fastcgi_conn.h b/fastcgi_conn.h new file mode 100644 index 0000000..d321132 --- /dev/null +++ b/fastcgi_conn.h @@ -0,0 +1,19 @@ +#pragma once + +#include "buffer.h" + +struct sockaddr_in6; + +class FastCGIConn { + public: + FastCGIConn(int sock, const sockaddr_in6& client_addr); + ~FastCGIConn(); + + void Serve(); + + private: + void ParseBuf(); + + const int sock_; + Buffer buf_; +}; diff --git a/mirall.cpp b/mirall.cpp index a47a33a..d019c2c 100644 --- a/mirall.cpp +++ b/mirall.cpp @@ -1,8 +1,7 @@ -#include #include #include -#include -#include + +#include "fastcgi.h" DEFINE_int32(port, 9000, "TCP port to bind"); @@ -10,30 +9,6 @@ int main(int argc, char *argv[]) { google::InitGoogleLogging(argv[0]); gflags::ParseCommandLineFlags(&argc, &argv, true); - auto listen_sock = socket(AF_INET6, SOCK_STREAM, 0); - PCHECK(listen_sock >= 0) << "socket()"; - - sockaddr_in6 bind_addr = { - .sin6_family = AF_INET6, - .sin6_port = htons(FLAGS_port), - .sin6_addr = IN6ADDR_LOOPBACK_INIT, - }; - PCHECK(bind(listen_sock, (sockaddr*) &bind_addr, sizeof(bind_addr)) == 0); - - PCHECK(listen(listen_sock, 128) == 0); - - while (true) { - sockaddr_in6 client_addr; - socklen_t client_addr_len = sizeof(client_addr); - - auto sock = accept(listen_sock, (sockaddr*) &client_addr, &client_addr_len); - PCHECK(sock >= 0) << "accept()"; - - CHECK_EQ(client_addr.sin6_family, AF_INET6); - - char client_addr_str[INET6_ADDRSTRLEN]; - PCHECK(inet_ntop(AF_INET6, &client_addr.sin6_addr, client_addr_str, sizeof(client_addr_str))); - - LOG(INFO) << "new connection: [" << client_addr_str << "]:" << ntohs(client_addr.sin6_port); - } + FastCGIServer server(FLAGS_port); + server.Serve(); }