example_simple building and working
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
example_simple
|
||||||
|
connection_afl
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "firebuf"]
|
||||||
|
path = firebuf
|
||||||
|
url = git@github.com:firestuff/firebuf.git
|
||||||
37
Makefile
Normal file
37
Makefile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
FIRE_CXX ?= clang++
|
||||||
|
FIRE_CXXFLAGS ?= -O3 -std=gnu++2a -Wall -Werror
|
||||||
|
FIRE_LDLIBS ?= -lgflags -lglog -lpthread
|
||||||
|
|
||||||
|
all: firecgi.a example_simple
|
||||||
|
|
||||||
|
objects = firecgi.o connection.o request.o parse.o
|
||||||
|
|
||||||
|
firecgi.a: $(objects)
|
||||||
|
$(MAKE) --directory=firebuf
|
||||||
|
ar rcs $@ $^
|
||||||
|
|
||||||
|
example_simple: example_simple.o $(objects)
|
||||||
|
$(FIRE_CXX) $(FIRE_CXXFLAGS) -o $@ $+ firebuf/firebuf.a $(FIRE_LDLIBS)
|
||||||
|
|
||||||
|
%.o: %.cc *.h Makefile
|
||||||
|
$(FIRE_CXX) $(FIRE_CXXFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(MAKE) --directory=firebuf clean
|
||||||
|
rm --force example_simple connection_afl *.o
|
||||||
|
|
||||||
|
afl:
|
||||||
|
$(MAKE) clean
|
||||||
|
FIRE_CXX=afl-g++ $(MAKE) afl_int
|
||||||
|
|
||||||
|
afl_int: connection_afl
|
||||||
|
|
||||||
|
connection_afl: connection_afl.o $(objects)
|
||||||
|
$(FIRE_CXX) $(FIRE_CXXFLAGS) -o $@ $+ $(FIRE_LDLIBS)
|
||||||
|
|
||||||
|
test: test_connection
|
||||||
|
|
||||||
|
test_connection: connection_afl_afl
|
||||||
|
@echo "Running $$(ls afl_state/testcases | wc -l) tests"
|
||||||
|
for FILE in afl_state/testcases/*; do ./connection_afl < $$FILE; done
|
||||||
|
@printf '\033[0;32mALL TESTS PASSED\033[0m\n'
|
||||||
@@ -13,7 +13,7 @@ Connection::Connection(int sock, const sockaddr_in6& client_addr, const std::fun
|
|||||||
: sock_(sock),
|
: sock_(sock),
|
||||||
callback_(callback),
|
callback_(callback),
|
||||||
headers_(headers),
|
headers_(headers),
|
||||||
buf_(sock, fastcgi_max_record_len) {
|
buf_(sock, max_record_len) {
|
||||||
char client_addr_str[INET6_ADDRSTRLEN];
|
char client_addr_str[INET6_ADDRSTRLEN];
|
||||||
PCHECK(inet_ntop(AF_INET6, &client_addr.sin6_addr, client_addr_str, sizeof(client_addr_str)));
|
PCHECK(inet_ntop(AF_INET6, &client_addr.sin6_addr, client_addr_str, sizeof(client_addr_str)));
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ int Connection::Read() {
|
|||||||
return sock_;
|
return sock_;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstBuffer param_buf(buf_.Read(header->ContentLength()), header->ContentLength());
|
firebuf::ConstBuffer param_buf(buf_.Read(header->ContentLength()), header->ContentLength());
|
||||||
while (param_buf.ReadMaxLen() > 0) {
|
while (param_buf.ReadMaxLen() > 0) {
|
||||||
const auto *param_header = param_buf.ReadObj<ParamHeader>();
|
const auto *param_header = param_buf.ReadObj<ParamHeader>();
|
||||||
if (!param_header) {
|
if (!param_header) {
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "firebuf/stream_buffer.h"
|
||||||
|
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
#include "stream_buffer.h"
|
|
||||||
|
|
||||||
namespace firecgi {
|
namespace firecgi {
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ class Connection {
|
|||||||
|
|
||||||
uint64_t requests_ = 0;
|
uint64_t requests_ = 0;
|
||||||
|
|
||||||
StreamBuffer buf_;
|
firebuf::StreamBuffer buf_;
|
||||||
|
|
||||||
std::unique_ptr<Request> request_;
|
std::unique_ptr<Request> request_;
|
||||||
};
|
};
|
||||||
|
|||||||
19
example_simple.cc
Normal file
19
example_simple.cc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include <gflags/gflags.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "firecgi.h"
|
||||||
|
|
||||||
|
DEFINE_int32(port, 9000, "TCP port to bind");
|
||||||
|
DEFINE_int32(threads, 1, "Number of server threads");
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
|
||||||
|
firecgi::Server server(FLAGS_port, [](std::unique_ptr<firecgi::Request> request) {
|
||||||
|
request->WriteHeader("Content-Type", "text/plain");
|
||||||
|
request->WriteBody("Hello world");
|
||||||
|
request->End();
|
||||||
|
}, FLAGS_threads);
|
||||||
|
server.Serve();
|
||||||
|
}
|
||||||
1
firebuf
Submodule
1
firebuf
Submodule
Submodule firebuf added at dd9a0a974f
110
request.cc
Normal file
110
request.cc
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
namespace firecgi {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template<class T> void AppendVec(const T& obj, std::vector<iovec>* vec) {
|
||||||
|
vec->push_back(iovec{
|
||||||
|
.iov_base = (void*)(&obj),
|
||||||
|
.iov_len = sizeof(obj),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Request::Request(uint16_t request_id, Connection* conn)
|
||||||
|
: request_id_(request_id),
|
||||||
|
conn_(conn),
|
||||||
|
out_buf_(max_record_len) {}
|
||||||
|
|
||||||
|
uint16_t Request::RequestId() {
|
||||||
|
return request_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::AddParam(const std::string_view& key, const std::string_view& value) {
|
||||||
|
params_.try_emplace(std::string(key), std::string(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::AddIn(const std::string_view& in) {
|
||||||
|
in_.append(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Request::GetParam(const std::string& key) {
|
||||||
|
auto iter = params_.find(key);
|
||||||
|
if (iter == params_.end()) {
|
||||||
|
static const std::string none;
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::WriteHeader(const std::string_view& name, const std::string_view& value) {
|
||||||
|
CHECK(!body_written_);
|
||||||
|
CHECK(out_buf_.Write(name));
|
||||||
|
CHECK(out_buf_.Write(": "));
|
||||||
|
CHECK(out_buf_.Write(value));
|
||||||
|
CHECK(out_buf_.Write("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::WriteBody(const std::string_view& body) {
|
||||||
|
if (!body_written_) {
|
||||||
|
CHECK(out_buf_.Write("\n"));
|
||||||
|
body_written_ = true;
|
||||||
|
}
|
||||||
|
// TODO: make this able to span multiple packets
|
||||||
|
CHECK(out_buf_.Write(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::Flush() {
|
||||||
|
std::vector<iovec> vecs;
|
||||||
|
|
||||||
|
auto header = OutputHeader();
|
||||||
|
AppendVec(header, &vecs);
|
||||||
|
|
||||||
|
vecs.push_back(OutputVec());
|
||||||
|
|
||||||
|
if (!conn_->Write(vecs)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_buf_.Commit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::End() {
|
||||||
|
// Fully empty response not allowed
|
||||||
|
WriteBody("");
|
||||||
|
|
||||||
|
std::vector<iovec> vecs;
|
||||||
|
|
||||||
|
// Must be outside if block, so it lives through Write() below
|
||||||
|
auto output_header = OutputHeader();
|
||||||
|
if (output_header.ContentLength()) {
|
||||||
|
AppendVec(output_header, &vecs);
|
||||||
|
vecs.push_back(OutputVec());
|
||||||
|
}
|
||||||
|
|
||||||
|
EndRequest end;
|
||||||
|
Header end_header(3, request_id_, sizeof(end));
|
||||||
|
AppendVec(end_header, &vecs);
|
||||||
|
AppendVec(end, &vecs);
|
||||||
|
|
||||||
|
return conn_->Write(vecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
iovec Request::OutputVec() {
|
||||||
|
const auto output_len = out_buf_.ReadMaxLen();
|
||||||
|
return iovec{
|
||||||
|
.iov_base = (void *)(CHECK_NOTNULL(out_buf_.Read(output_len))),
|
||||||
|
.iov_len = output_len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Header Request::OutputHeader() {
|
||||||
|
return Header(6, request_id_, out_buf_.ReadMaxLen());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace firecgi
|
||||||
43
request.h
Normal file
43
request.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "firebuf/buffer.h"
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
namespace firecgi {
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
public:
|
||||||
|
Request(uint16_t request_id, Connection *conn);
|
||||||
|
|
||||||
|
uint16_t RequestId();
|
||||||
|
|
||||||
|
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 WriteHeader(const std::string_view& name, const std::string_view& value);
|
||||||
|
void WriteBody(const std::string_view& body);
|
||||||
|
[[nodiscard]] bool Flush();
|
||||||
|
bool End();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Header OutputHeader();
|
||||||
|
iovec OutputVec();
|
||||||
|
|
||||||
|
const uint16_t request_id_;
|
||||||
|
Connection *conn_;
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> params_;
|
||||||
|
std::string in_;
|
||||||
|
|
||||||
|
firebuf::Buffer out_buf_;
|
||||||
|
bool body_written_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace firecgi
|
||||||
Reference in New Issue
Block a user