* httpd/server.cxx (server::access_handler): Save regexp matches.
(server::queue_response): Add response headers.
* httpd/main.cxx: Add initial support for build info.
* httpd/Makefile.am: Link with the json-c library.
* httpd/server.h (struct request): Add 'matches' variable.
* configure.ac: Add the json-c library as a requirement of the httpd
server.
* config.in: Regenerated.
* configure: Ditto.
* httpd/Makefile.in: Ditto.
/* Define to 1 if libxml2 development libraries are installed */
#undef HAVE_LIBXML2
-/* Define to 1 if you have the <linux/bpf.h> header file. */
-#undef HAVE_LINUX_BPF_H
-
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
have_libuuid=true
fi
- if test "x$have_libmicrohttpd" == "xtrue" -a "x$have_libuuid" == "xtrue"; then :
+ if test "x$have_libmicrohttpd" == "xtrue" -a "x$have_libuuid" == "xtrue" -a "x$have_jsonc" == "xyes"; then :
have_http_support=yes
fi
fi
dnl Do we have the uuid library?
PKG_CHECK_MODULES([uuid], [uuid >= 2.17.0], [have_libuuid=true], [have_libuuid=false])
- dnl If we have both libraries, we could build the httpd web
- dnl complilation service.
- AS_IF([test "x$have_libmicrohttpd" == "xtrue" -a "x$have_libuuid" == "xtrue"], [have_http_support=yes])])
+ dnl If we have both libraries (and the json-c library), we could
+ dnl build the httpd web complilation service.
+ AS_IF([test "x$have_libmicrohttpd" == "xtrue" -a "x$have_libuuid" == "xtrue" -a "x$have_jsonc" == "xyes"], [have_http_support=yes])])
AS_IF([test "x$have_http_support" = "xyes"],
[AC_DEFINE([HAVE_HTTP_SUPPORT], [1],
stap_httpd_CFLAGS = $(AM_CFLAGS)
stap_httpd_CXXFLAGS = $(AM_CXXFLAGS)
stap_httpd_CPPFLAGS = $(AM_CPPFLAGS)
-stap_httpd_LDADD = -lpthread -lmicrohttpd -luuid
+stap_httpd_LDADD = -lpthread -lmicrohttpd -luuid -ljson-c
stap_httpd_LDFLAGS = $(AM_LDFLAGS)
endif
@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_CFLAGS = $(AM_CFLAGS)
@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_CXXFLAGS = $(AM_CXXFLAGS)
@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_CPPFLAGS = $(AM_CPPFLAGS)
-@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_LDADD = -lpthread -lmicrohttpd -luuid
+@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_LDADD = -lpthread -lmicrohttpd -luuid -ljson-c
@HAVE_HTTP_SUPPORT_TRUE@stap_httpd_LDFLAGS = $(AM_LDFLAGS)
# Arrange for the top-level git_version.h to be regenerated at every "make".
// later version.
#include "server.h"
+#include "iostream"
+#include "iomanip"
+#include <sstream>
extern "C" {
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#endif
+#include <uuid/uuid.h>
+#include <json-c/json_object.h>
}
-//using namespace std;
-#define PAGE "<html><head><title>Error</title></head><body>Bad data</body></html>"
+string get_uuid_representation(const uuid_t uuid)
+{
+ ostringstream os;
+
+ os << hex << setfill('0');
+ for (const unsigned char *ptr = uuid; ptr < uuid + sizeof(uuid_t); ptr++)
+ os << setw(2) << (unsigned int)*ptr;
+ return os.str();
+}
+
+struct build_info
+{
+ uuid_t uuid;
+ string uuid_str;
+ string uri;
+
+ string kver;
+ string arch;
+ string cmdline;
+
+ build_info() {
+ uuid_generate(uuid);
+ uuid_str = get_uuid_representation(uuid);
+ uri = "/builds/" + uuid_str;
+ }
+
+ string content();
+};
+
+string build_info::content()
+{
+ ostringstream os;
+ os << "{" << endl;
+ os << " \"uuid\": \"" << uuid_str << "\"" << endl;
+ os << " \"kver\": \"" << kver << "\"" << endl;
+ os << " \"arch\": \"" << arch << "\"" << endl;
+
+ struct json_object *j = json_object_new_string(cmdline.c_str());
+ if (j) {
+ os << " \"cmdline\": "
+ << json_object_to_json_string_ext(j, JSON_C_TO_STRING_PLAIN) << endl;
+ }
+
+ os << "}" << endl;
+ return os.str();
+}
+
+mutex builds_mutex;
+vector<build_info *> build_infos;
+
+class build_collection : public request_handler
+{
+public:
+ response POST(const request &req);
+
+ build_collection(string n) : request_handler(n) {}
+};
+
+response build_collection::POST(const request &req)
+{
+ // Create a build with the information we've gathered.
+ build_info *b = new build_info;
+ for (auto it = req.params.begin(); it != req.params.end(); it++) {
+ if (it->first == "kver") {
+ b->kver = it->second;
+ }
+ else if (it->first == "arch") {
+ b->arch = it->second;
+ }
+ else if (it->first == "cmdline") {
+ b->cmdline = it->second;
+ }
+ }
+
+ // Make sure we've got everything we need.
+ if (b->kver.empty() || b->arch.empty() || b->cmdline.empty()) {
+ // Return an error.
+ clog << "400 - bad request" << endl;
+ response error400(400);
+ error400.content = "<h1>Bad request</h1>";
+ return error400;
+ }
+
+ {
+ // Use a lock_guard to ensure the mutex gets released even if an
+ // exception is thrown.
+ lock_guard<mutex> lock(builds_mutex);
+ build_infos.push_back(b);
+ }
+
+ clog << "Returning a 202" << endl;
+ response resp(202);
+ resp.headers["Location"] = b->uri;
+ resp.headers["Retry-After"] = "20";
+ return resp;
+}
+
+class individual_build : public request_handler
+{
+public:
+ response GET(const request &req);
+
+ individual_build(string n) : request_handler(n) {}
+};
+
+response individual_build::GET(const request &req)
+{
+ clog << "individual_build::GET" << endl;
+
+ // matches[0] is the entire string '/builds/XXXX'. matches[1] is
+ // just the buildid 'XXXX'.
+ string buildid = req.matches[1];
+ build_info *b = NULL;
+ {
+ // Use a lock_guard to ensure the mutex gets released even if an
+ // exception is thrown.
+ lock_guard<mutex> lock(builds_mutex);
+ for (auto it = build_infos.begin(); it != build_infos.end(); it++) {
+ clog << "Comparing '" << buildid << "'to '" << (*it)->uuid_str << "'" << endl;
+ if (buildid == (*it)->uuid_str) {
+ b = *it;
+ break;
+ }
+ }
+ }
+
+ if (b == NULL) {
+ clog << "Couldn't find build '" << buildid << "'" << endl;
+ return get_404_response();
+ }
+
+ response rsp(200, "application/json");
+ rsp.content = b->content();
+ return rsp;
+}
-class request_handler build_collection("build collection");
-class request_handler build("individual build");
+build_collection builds("build collection");
+individual_build build("individual build");
int
main(int /*argc*/, char *const /*argv*/[])
{
server httpd(1234);
- httpd.add_request_handler("/builds$", build_collection);
- httpd.add_request_handler("/builds/[0-9]+$", build);
+ httpd.add_request_handler("/builds$", builds);
+ httpd.add_request_handler("/builds/([0-9a-f]+)$", build);
// FIXME: Should this be pthread_cond_wait()/pthread_cond_timedwait()?
while (1) {
sleep(1);
return MHD_YES;
}
-static response
+response
get_404_response()
{
response error404(404);
return queue_response(get_404_response(), connection);
}
+ struct request rq_info;
request_handler *rh = NULL;
clog << "Looking for a matching request handler match with '"
<< url_str << "'..." << endl;
for (auto it = request_handlers.begin();
it != request_handlers.end(); it++) {
string url_path_re = get<0>(*it);
- vector<string> matches;
- if (regexp_match(url_str, url_path_re, matches) == 0) {
+ if (regexp_match(url_str, url_path_re, rq_info.matches) == 0) {
rh = get<1>(*it);
clog << "Found a match with '" << rh->name << "'" << endl;
break;
// Prepare to call the appropriate request handler method by
// gathering up all the request info.
- struct request rq_info;
enum MHD_ValueKind kind = ((rq_method == request_method::POST)
? MHD_POSTDATA_KIND
: MHD_GET_ARGUMENT_KIND);
return MHD_NO;
}
-#if 0
- for (const auto &header : response.headers) {
- MHD_add_response_header(mhd_response, header.first.c_str(),
- header.second.c_str());
+ for (auto it = response.headers.begin(); it != response.headers.end();
+ it++) {
+ MHD_add_response_header(mhd_response, it->first.c_str(),
+ it->second.c_str());
}
-#endif
- MHD_add_response_header(mhd_response, MHD_HTTP_HEADER_CONTENT_TYPE, response.content_type.c_str());
+ MHD_add_response_header(mhd_response, MHD_HTTP_HEADER_CONTENT_TYPE,
+ response.content_type.c_str());
// MHD_add_response_header(mhd_response, MHD_HTTP_HEADER_SERVER, server_identifier_.c_str());
int ret = MHD_queue_response(connection, response.status_code,
struct response
{
unsigned int status_code;
+ map<string, string> headers;
string content;
string content_type;
}
};
+extern response get_404_response();
+
struct request
{
-#if 0
- endpoint_matches matches;
-#endif
map<string, string> params;
+ vector<string> matches;
#if 0
request_headers headers;
std::string body;