Static "Hello world" page

This commit is contained in:
Ian Gulliver
2019-04-28 06:47:30 +00:00
parent 8a2a19ac37
commit 9da4a994bd
11 changed files with 231 additions and 25 deletions

View File

@@ -1,8 +1,8 @@
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
example_simple: example_simple.o fastcgi.o fastcgi_conn.o fastcgi_request.o buffer.o
g++ -std=gnu++2a -o example_simple example_simple.o fastcgi.o fastcgi_conn.o fastcgi_request.o buffer.o -lgflags -lglog -lpthread
clean:
rm --force mirall *.o
rm --force exmaple_simple *.o
.cpp.o: Makefile *.h
.cpp.o:
g++ -std=gnu++2a -o $@ -c $<

View File

@@ -19,7 +19,7 @@ bool ConstBuffer::Discard(size_t len) {
if (len > ReadMaxLen()) {
return false;
}
Read(len);
static_cast<void>(Read(len));
return true;
}
@@ -40,7 +40,9 @@ Buffer::Buffer(char *buf, size_t size, size_t len)
}
Buffer::Buffer(size_t size)
: Buffer((own_buf_.reset(new char[size]), own_buf_.get()), size, 0) {}
: Buffer(new char[size], size, 0) {
own_buf_.reset(buf_);
}
char *Buffer::WritePtr() {
return &buf_[len_];

View File

@@ -7,9 +7,9 @@ class ConstBuffer {
public:
ConstBuffer(const char *buf, size_t len);
size_t ReadMaxLen() const;
const char *Read(size_t len);
template<class T> const T *ReadObj();
[[nodiscard]] size_t ReadMaxLen() const;
[[nodiscard]] const char *Read(size_t len);
template<class T> [[nodiscard]] const T *ReadObj();
bool Discard(size_t len); // like Read() but don't use the result
void ResetRead(); // next read from last commit
@@ -28,8 +28,8 @@ class Buffer : public ConstBuffer {
Buffer(char *buf, size_t size, size_t len);
Buffer(size_t size);
char *WritePtr();
size_t WriteMaxLen() const;
[[nodiscard]] char *WritePtr();
[[nodiscard]] size_t WriteMaxLen() const;
void Wrote(size_t len);
void Consume(); // discard up to last commit

BIN
example_simple Executable file

Binary file not shown.

View File

@@ -9,6 +9,10 @@ int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
FastCGIServer server(FLAGS_port);
FastCGIServer server(FLAGS_port, [](std::unique_ptr<FastCGIRequest> request) {
LOG(INFO) << "request from " << request->GetParam("REMOTE_ADDR");
request->Write({{"Content-Type", "text/plain"}}, {"Hello world"});
request->WriteEnd();
});
server.Serve();
}

View File

@@ -6,7 +6,8 @@
#include "fastcgi.h"
#include "fastcgi_conn.h"
FastCGIServer::FastCGIServer(int port) {
FastCGIServer::FastCGIServer(int port, const std::function<void(std::unique_ptr<FastCGIRequest>)>& callback)
: callback_(callback) {
LOG(INFO) << "listening on [::1]:" << port;
listen_sock_ = socket(AF_INET6, SOCK_STREAM, 0);
@@ -38,7 +39,7 @@ void FastCGIServer::Serve() {
PCHECK(client_sock >= 0) << "accept()";
CHECK_EQ(client_addr.sin6_family, AF_INET6);
auto *conn = new FastCGIConn(client_sock, client_addr);
auto *conn = new FastCGIConn(client_sock, client_addr, callback_);
std::thread thread([conn]() { conn->Serve(); });
thread.detach();
}

View File

@@ -1,10 +1,16 @@
#pragma once
#include <functional>
#include <memory>
#include "fastcgi_request.h"
class FastCGIServer {
public:
FastCGIServer(int port);
FastCGIServer(int port, const std::function<void(std::unique_ptr<FastCGIRequest>)>& callback);
void Serve();
private:
int listen_sock_;
std::function<void(std::unique_ptr<FastCGIRequest>)> callback_;
};

View File

@@ -1,8 +1,11 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include "fastcgi_conn.h"
#include "fastcgi_request.h"
namespace {
struct fcgi_header {
@@ -12,16 +15,39 @@ struct fcgi_header {
uint16_t request_id_; // network byte order
uint16_t content_length_; // network byte order
public:
uint8_t padding_length;
uint8_t reserved;
uint8_t padding_length = 0;
uint8_t reserved = 0;
uint16_t ContentLength() const { return htons(content_length_); }
uint16_t RequestId() const { return ntohs(request_id_); }
uint16_t ContentLength() const { return ntohs(content_length_); }
void SetRequestId(uint16_t request_id) { request_id_ = htons(request_id); }
void SetContentLength(uint16_t content_length) { content_length_ = htons(content_length); }
};
struct fcgi_begin_request {
uint16_t role; // network byte order
private:
uint16_t role_; // network byte order
public:
uint8_t flags;
uint8_t reserved[5];
uint16_t Role() const { return ntohs(role_); }
};
struct fcgi_end_request {
private:
uint32_t app_status_;
public:
uint8_t protocol_status;
uint8_t reserved[3] = {};
void SetAppStatus(uint32_t app_status) { app_status_ = htonl(app_status); }
};
struct fcgi_param_header {
uint8_t key_length;
uint8_t value_length;
};
constexpr auto fcgi_max_content_len = 65535;
@@ -30,8 +56,9 @@ constexpr auto fcgi_max_record_len = sizeof(fcgi_header) + fcgi_max_content_len
} // namespace
FastCGIConn::FastCGIConn(int sock, const sockaddr_in6& client_addr)
FastCGIConn::FastCGIConn(int sock, const sockaddr_in6& client_addr, const std::function<void(std::unique_ptr<FastCGIRequest>)>& callback)
: sock_(sock),
callback_(callback),
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)));
@@ -50,6 +77,8 @@ void FastCGIConn::Serve() {
PCHECK(read_len >= 0);
if (read_len == 0) {
LOG(INFO) << "peer closed connection";
delete this;
return;
}
buf_.Wrote(read_len);
@@ -58,6 +87,47 @@ void FastCGIConn::Serve() {
}
}
void FastCGIConn::WriteBlock(uint8_t type, uint16_t request_id, const std::vector<iovec>& vecs) {
std::vector<iovec> out_vecs;
out_vecs.reserve(vecs.size() + 1);
fcgi_header header;
header.version = 1;
header.type = type;
header.SetRequestId(request_id);
uint16_t content_length = 0;
for (auto& vec : vecs) {
content_length += vec.iov_len;
}
header.SetContentLength(content_length);
out_vecs.push_back(std::move(iovec{
.iov_base = &header,
.iov_len = sizeof(header),
}));
for (auto& vec : vecs) {
out_vecs.push_back(vec);
}
CHECK_EQ(writev(sock_, out_vecs.data(), out_vecs.size()), content_length + sizeof(header));
}
void FastCGIConn::WriteOutput(uint16_t request_id, const std::vector<iovec>& vecs) {
WriteBlock(6, request_id, vecs);
}
void FastCGIConn::WriteEnd(uint16_t request_id, uint8_t status) {
fcgi_end_request end;
end.SetAppStatus(status);
std::vector<iovec> vecs;
vecs.push_back(std::move(iovec{
.iov_base = &end,
.iov_len = sizeof(end),
}));
WriteBlock(3, request_id, vecs);
}
void FastCGIConn::ParseBuf() {
buf_.ResetRead();
@@ -72,22 +142,39 @@ void FastCGIConn::ParseBuf() {
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<fcgi_begin_request>());
CHECK_EQ(ntohs(begin_request->role), 1);
CHECK_EQ(begin_request->Role(), 1);
request_.reset(new FastCGIRequest(header->RequestId(), this));
}
break;
case 4:
{
const auto *params = buf_.Read(header->ContentLength());
ConstBuffer param_buf(buf_.Read(header->ContentLength()), header->ContentLength());
while (param_buf.ReadMaxLen() > 0) {
const auto *param_header = param_buf.ReadObj<fcgi_param_header>();
std::string_view key(param_buf.Read(param_header->key_length), param_header->key_length);
std::string_view value(param_buf.Read(param_header->value_length), param_header->value_length);
request_->AddParam(key, value);
}
}
break;
case 5:
{
if (header->ContentLength() == 0) {
// Magic signal for completed request (mirrors the HTTP/1.1 protocol)
callback_(std::move(request_));
} else {
std::string_view in(buf_.Read(header->ContentLength()), header->ContentLength());
request_->AddIn(in);
}
}
}
CHECK(buf_.Discard(header->padding_length));

View File

@@ -1,19 +1,31 @@
#pragma once
#include <functional>
#include <unordered_map>
#include "buffer.h"
struct sockaddr_in6;
class FastCGIRequest;
class FastCGIConn {
public:
FastCGIConn(int sock, const sockaddr_in6& client_addr);
FastCGIConn(int sock, const sockaddr_in6& client_addr, const std::function<void(std::unique_ptr<FastCGIRequest>)>& callback);
~FastCGIConn();
void Serve();
void WriteBlock(uint8_t type, uint16_t request_id, const std::vector<iovec>& vecs);
void WriteOutput(uint16_t request_id, const std::vector<iovec>& vecs);
void WriteEnd(uint16_t request_id, uint8_t status);
private:
void ParseBuf();
const int sock_;
std::function<void(std::unique_ptr<FastCGIRequest>)> callback_;
Buffer buf_;
std::unique_ptr<FastCGIRequest> request_;
};

68
fastcgi_request.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include <sys/uio.h>
#include "fastcgi_conn.h"
#include "fastcgi_request.h"
FastCGIRequest::FastCGIRequest(uint16_t request_id, FastCGIConn* conn)
: request_id_(request_id),
conn_(conn) {}
void FastCGIRequest::AddParam(const std::string_view& key, const std::string_view& value) {
params_.try_emplace(std::move(std::string(key)), std::move(std::string(value)));
}
void FastCGIRequest::AddIn(const std::string_view& in) {
in_.append(in);
}
const std::string& FastCGIRequest::GetParam(const std::string& key) {
return params_.at(key);
}
void FastCGIRequest::Write(const std::vector<std::pair<std::string_view, std::string_view>>& headers, const std::vector<std::string_view>& body) {
std::vector<iovec> vecs;
vecs.reserve((headers.size() * 4) + 1 + body.size());
CHECK(headers.empty() || !body_sent_);
for (const auto& header : headers) {
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>(header.first.data()),
.iov_len = header.first.size(),
}));
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>(": "),
.iov_len = 2,
}));
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>(header.second.data()),
.iov_len = header.second.size(),
}));
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>("\n"),
.iov_len = 1,
}));
}
if (!body.empty() && !body_sent_) {
body_sent_ = true;
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>("\n"),
.iov_len = 1,
}));
}
for (const auto& chunk : body) {
vecs.push_back(std::move(iovec{
.iov_base = const_cast<char*>(chunk.data()),
.iov_len = chunk.size(),
}));
}
conn_->WriteOutput(request_id_, vecs);
}
void FastCGIRequest::WriteEnd() {
conn_->WriteEnd(request_id_, 0);
}

26
fastcgi_request.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <unordered_map>
class FastCGIConn;
class FastCGIRequest {
public:
FastCGIRequest(uint16_t request_id, FastCGIConn *conn);
void AddParam(const std::string_view& key, const std::string_view& value);
void AddIn(const std::string_view& in);
const std::string& GetParam(const std::string& key);
void Write(const std::vector<std::pair<std::string_view, std::string_view>>& headers, const std::vector<std::string_view>& body);
void WriteEnd();
private:
const uint16_t request_id_;
FastCGIConn *conn_;
std::unordered_map<std::string, std::string> params_;
std::string in_;
bool body_sent_ = false;
};