From aebe139d00b44684158edb3616da5c37b12db6d1 Mon Sep 17 00:00:00 2001
From: Roland Reichwein <mail@reichwein.it>
Date: Sun, 26 Apr 2020 13:01:19 +0200
Subject: Added stats output

---
 plugins/statistics/Makefile       | 124 ++++++++++++++++++++++++++++++++++++++
 plugins/statistics/statistics.cpp |  97 +++++++++++++++++++++++++++++
 plugins/statistics/statistics.h   |  21 +++++++
 3 files changed, 242 insertions(+)
 create mode 100644 plugins/statistics/Makefile
 create mode 100644 plugins/statistics/statistics.cpp
 create mode 100644 plugins/statistics/statistics.h

(limited to 'plugins/statistics')

diff --git a/plugins/statistics/Makefile b/plugins/statistics/Makefile
new file mode 100644
index 0000000..48c2e8c
--- /dev/null
+++ b/plugins/statistics/Makefile
@@ -0,0 +1,124 @@
+DISTROS=debian10
+VERSION=$(shell dpkg-parsechangelog --show-field Version)
+PROJECTNAME=statistics
+
+CXX=clang++-10
+
+ifeq ($(shell which $(CXX)),)
+CXX=clang++
+endif
+
+ifeq ($(shell which $(CXX)),)
+CXX=g++-9
+endif
+
+ifeq ($(CXXFLAGS),)
+#CXXFLAGS=-O2 -DNDEBUG
+CXXFLAGS=-O0 -g -D_DEBUG
+endif
+# -fprofile-instr-generate -fcoverage-mapping
+# gcc:--coverage
+
+CXXFLAGS+= -Wall -I.
+
+CXXFLAGS+= -pthread -fvisibility=hidden -fPIC
+ifeq ($(CXX),clang++-10)
+CXXFLAGS+=-std=c++20 #-stdlib=libc++
+else
+CXXFLAGS+=-std=c++17
+endif
+
+CXXTESTFLAGS=-Igoogletest/include -Igooglemock/include/ -Igoogletest -Igooglemock
+
+LIBS=\
+-lboost_context \
+-lboost_coroutine \
+-lboost_program_options \
+-lboost_system \
+-lboost_thread \
+-lboost_filesystem \
+-lboost_regex \
+-lpthread \
+-lssl -lcrypto \
+-ldl
+
+ifeq ($(CXX),clang++-10)
+LIBS+= \
+-fuse-ld=lld-10 \
+-lstdc++
+#-lc++ \
+#-lc++abi
+#-lc++fs
+#-lstdc++fs
+else
+LIBS+= \
+-lstdc++ \
+-lstdc++fs
+endif
+
+PROGSRC=\
+    statistics.cpp
+
+TESTSRC=\
+    test-webserver.cpp \
+    googlemock/src/gmock-all.cpp \
+    googletest/src/gtest-all.cpp \
+    $(PROGSRC)
+
+SRC=$(PROGSRC)
+
+all: $(PROJECTNAME).so
+
+# testsuite ----------------------------------------------
+test-$(PROJECTNAME): $(TESTSRC:.cpp=.o)
+	$(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+$(PROJECTNAME).so: $(SRC:.cpp=.o)
+	$(CXX) -shared $(CXXFLAGS) $^ $(LIBS) -o $@
+
+dep: $(TESTSRC:.cpp=.d)
+
+%.d: %.cpp
+	$(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -MM -MP -MF $@ -c $<
+
+%.o: %.cpp %.d
+	$(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@
+
+googletest/src/%.o: googletest/src/%.cc
+	$(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@
+
+# dependencies
+
+ADD_DEP=Makefile
+
+install:
+	mkdir -p $(DESTDIR)/usr/lib/webserver/plugins
+	cp $(PROJECTNAME).so $(DESTDIR)/usr/lib/webserver/plugins
+
+# misc ---------------------------------------------------
+deb:
+	# build binary deb package
+	dpkg-buildpackage -us -uc -rfakeroot
+
+deb-src:
+	dpkg-source -b .
+
+$(DISTROS): deb-src
+	sudo pbuilder build --basetgz /var/cache/pbuilder/$@.tgz --buildresult result/$@ ../webserver_$(VERSION).dsc ; \
+
+debs: $(DISTROS)
+
+clean:
+	-rm -f test-$(PROJECTNAME) $(PROJECTNAME)
+	-find . -name '*.o' -o -name '*.so' -o -name '*.d' -o -name '*.gcno' -o -name '*.gcda' | xargs rm -f
+
+zip: clean
+	-rm -f ../$(PROJECTNAME).zip
+	zip -r ../$(PROJECTNAME).zip *
+	ls -l ../$(PROJECTNAME).zip
+
+
+
+.PHONY: clean all zip install deb deb-src debs all $(DISTROS)
+
+-include $(wildcard $(SRC:.cpp=.d))
diff --git a/plugins/statistics/statistics.cpp b/plugins/statistics/statistics.cpp
new file mode 100644
index 0000000..03f4c94
--- /dev/null
+++ b/plugins/statistics/statistics.cpp
@@ -0,0 +1,97 @@
+#include "statistics.h"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/coroutine2/coroutine.hpp>
+#include <boost/process.hpp>
+
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <unordered_map>
+
+using namespace std::string_literals;
+namespace bp = boost::process;
+namespace fs = std::filesystem;
+
+namespace {
+
+ // Used to return errors by generating response page and HTTP status code
+ std::string HttpStatus(std::string status, std::string message, std::function<plugin_interface_setter_type>& SetResponseHeader)
+ {
+  SetResponseHeader("status", status);
+  SetResponseHeader("content_type", "text/html");
+  return status + " " + message;
+ }
+
+} // anonymous namespace
+
+std::string statistics_plugin::name()
+{
+ return "statistics";
+}
+
+statistics_plugin::statistics_plugin()
+{
+ //std::cout << "Plugin constructor" << std::endl;
+}
+
+statistics_plugin::~statistics_plugin()
+{
+ //std::cout << "Plugin destructor" << std::endl;
+}
+
+std::string statistics_plugin::generate_page(
+  std::function<std::string(const std::string& key)>& GetServerParam,
+  std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...)
+  std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string
+)
+{
+ try {
+  // Request path must not contain "..".
+  std::string rel_target{GetRequestParam("rel_target")};
+  size_t query_pos{rel_target.find("?")};
+  if (query_pos != rel_target.npos)
+   rel_target = rel_target.substr(0, query_pos);
+
+  std::string target{GetRequestParam("target")};
+  if (rel_target.find("..") != std::string::npos) {
+   return HttpStatus("400", "Illegal request: "s + target, SetResponseHeader);
+  }
+
+  // Build the path to the requested file
+  std::string doc_root{GetRequestParam("doc_root")};
+  fs::path path {fs::path{doc_root} / rel_target};
+  if (target.size() && target.back() != '/' && fs::is_directory(path)) {
+   std::string location{GetRequestParam("location") + "/"s};
+   SetResponseHeader("location", location);
+   return HttpStatus("301", "Correcting directory path", SetResponseHeader);
+  }
+
+  try {
+   SetResponseHeader("content_type", "text/html");
+
+   std::string header {"<!DOCTYPE html><html><head>"
+             "<meta charset=\"utf-8\"/>"
+             "<title>Webserver Statistics</title>"
+             "</head><body>"};
+   std::string footer{"<br/><br/><br/></body></html>"};
+
+   std::string result{header};
+
+   result += "<h1>Webserver Statistics</h1>";
+   result += "<pre>"s + GetServerParam("statistics") + "</pre>"s;
+
+   result += footer;
+   
+   return result;
+  } catch (const std::exception& ex) {
+   return HttpStatus("500", "Statistics error: "s + ex.what(), SetResponseHeader);
+  }
+
+ } catch (const std::exception& ex) {
+  return HttpStatus("500", "Unknown Error: "s + ex.what(), SetResponseHeader);
+ }
+}
+
diff --git a/plugins/statistics/statistics.h b/plugins/statistics/statistics.h
new file mode 100644
index 0000000..5db309b
--- /dev/null
+++ b/plugins/statistics/statistics.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "../../plugin_interface.h"
+
+class statistics_plugin: public webserver_plugin_interface 
+{
+public:
+ statistics_plugin();
+ ~statistics_plugin();
+ 
+ std::string name();
+ std::string generate_page(
+  std::function<std::string(const std::string& key)>& GetServerParam,
+  std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...)
+  std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string
+ );
+
+};
+
+extern "C" BOOST_SYMBOL_EXPORT statistics_plugin webserver_plugin;
+statistics_plugin webserver_plugin;
-- 
cgit v1.2.3