summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordeva <deva>2010-06-14 12:25:23 +0000
committerdeva <deva>2010-06-14 12:25:23 +0000
commit653eb23b01c2066daccfe9f29ae1044802ef7481 (patch)
treeacf25066c13b090d4d44500c9bd0ec4ba4ae1274
parent198b0d886817f2c5bc97cfd11857d4b314dffae3 (diff)
Isolated all microhttpd code in Httpd class.
-rw-r--r--server/src/Makefile.am2
-rw-r--r--server/src/connection.cc6
-rw-r--r--server/src/connection.h2
-rw-r--r--server/src/httpd.cc369
-rw-r--r--server/src/httpd.h89
-rw-r--r--server/src/server.cc188
6 files changed, 525 insertions, 131 deletions
diff --git a/server/src/Makefile.am b/server/src/Makefile.am
index 942ced5..fdfeb42 100644
--- a/server/src/Makefile.am
+++ b/server/src/Makefile.am
@@ -22,6 +22,7 @@ pracrod_SOURCES = \
entitylist.cc \
environment.cc \
exception.cc \
+ httpd.cc \
inotify.cc \
journal_commit.cc \
journalwriter.cc \
@@ -101,6 +102,7 @@ EXTRA_DIST = \
environment.h \
entitylist.h \
exception.h \
+ httpd.h \
inotify.h \
journal_commit.h \
journalwriter.h \
diff --git a/server/src/connection.cc b/server/src/connection.cc
index ed5a7a9..43d1ea9 100644
--- a/server/src/connection.cc
+++ b/server/src/connection.cc
@@ -57,6 +57,7 @@ Connection::Connection(Environment &e, std::string sid, bool c, bool d)
did_commit = false;
#endif
+ parser_complete = false;
}
Connection::~Connection()
@@ -116,6 +117,8 @@ bool Connection::handle(const char *data, size_t size)
}
if(parser.parse(data, size)) {
+ parser_complete = true;
+
{
SessionAutolock lock(*session);
response = handleTransaction(transaction, env, *session);
@@ -123,6 +126,7 @@ bool Connection::handle(const char *data, size_t size)
commit(session);
discard(session);
+
return true;
}
} catch(...) {
@@ -136,6 +140,8 @@ bool Connection::handle(const char *data, size_t size)
std::string Connection::getResponse()
{
+ if(parser_complete == false)
+ return error_box(xml_encode("XML Parser need more data."));
return response;
}
diff --git a/server/src/connection.h b/server/src/connection.h
index 61997da..f1735a8 100644
--- a/server/src/connection.h
+++ b/server/src/connection.h
@@ -58,6 +58,8 @@ private:
TransactionParser parser;
std::string response;
+
+ bool parser_complete;
};
#endif/*__PRACRO_CONNECTION_H__*/
diff --git a/server/src/httpd.cc b/server/src/httpd.cc
new file mode 100644
index 0000000..9150520
--- /dev/null
+++ b/server/src/httpd.cc
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ * httpd.cc
+ *
+ * Thu Jun 10 09:05:10 CEST 2010
+ * Copyright 2010 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of Pracro.
+ *
+ * Pracro is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Pracro is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pracro; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "httpd.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+// For fork
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <microhttpd.h>
+
+typedef struct {
+ void *ptr;
+ bool firstrun;
+ size_t total;
+ size_t acc;
+} connection_t;
+
+int hdit(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
+{
+ headers_t *headers = (headers_t*)cls;
+
+ (*headers)[key] = value;
+
+ return MHD_YES;
+}
+
+static int request_handler(void *cls,
+ struct MHD_Connection *con,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *data,
+ unsigned int *data_size,
+ void **con_cls)
+{
+ int ret = MHD_YES;
+
+ Httpd *httpd = (Httpd*)cls;
+
+ if(*con_cls == NULL) {
+ headers_t headers;
+
+ MHD_get_connection_values(con, MHD_HEADER_KIND, hdit, &headers);
+
+ connection_t* c = new connection_t;
+
+ c->firstrun = true;
+ c->total = 0;
+ if(headers.find("Content-Length") != headers.end()) {
+ c->total = atoi(headers["Content-Length"].c_str());
+ }
+ c->acc = 0;
+ c->ptr = httpd->begin(url, method, version, headers);
+
+ *con_cls = c;
+ }
+
+ connection_t* c = (connection_t*)*con_cls;
+
+ if(c == NULL) return MHD_NO;
+
+ if(*data_size && data) {
+ if(!httpd->data(c->ptr, data, *data_size)) return MHD_NO;
+ }
+
+ if(c->total == c->acc && c->firstrun == false) {
+ Httpd::Reply reply;
+ if(!httpd->complete(c->ptr, reply)) return MHD_NO;
+
+ struct MHD_Response *rsp;
+ rsp = MHD_create_response_from_data(reply.data.length(),
+ (void*)reply.data.data(),
+ MHD_NO, // must free
+ MHD_YES); // must copy
+
+ headers_t::iterator i = reply.headers.begin();
+ while(i != reply.headers.end()) {
+ MHD_add_response_header(rsp, i->first.c_str(), i->second.c_str());
+ i++;
+ }
+
+ ret = MHD_queue_response(con, reply.status, rsp);
+ MHD_destroy_response(rsp);
+ }
+
+ c->firstrun = false;
+ c->acc += *data_size;
+
+ *data_size = 0;
+ return ret;
+}
+
+static void completed_handler(void *cls,
+ struct MHD_Connection *con,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ Httpd *httpd = (Httpd*)cls;
+
+ connection_t* c = (connection_t*)*con_cls;
+
+ if(c) {
+ httpd->cleanup(c->ptr);
+ c->ptr = NULL;
+ delete c;
+ }
+
+ *con_cls = NULL;
+}
+
+static void error_handler(void *cls, const char *fmt, va_list ap)
+{
+ Httpd *httpd = (Httpd*)cls;
+ char *cmsg;
+ int sz = vasprintf(&cmsg, fmt, ap);
+ std::string msg;
+ msg.append(cmsg, sz);
+ httpd->error(msg);
+ free(cmsg);
+}
+
+Httpd::Httpd()
+{
+ d = NULL;
+ d_ssl = NULL;
+}
+
+Httpd::~Httpd()
+{
+ stop();
+ stop_ssl();
+}
+
+void Httpd::listen(unsigned short int port,
+ unsigned int cn_limit, unsigned int cn_timeout)
+{
+ int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY;
+
+ d = MHD_start_daemon(flags, port, NULL, NULL,
+ request_handler, this,
+ MHD_OPTION_NOTIFY_COMPLETED, completed_handler, this,
+ MHD_OPTION_EXTERNAL_LOGGER, error_handler, this,
+ MHD_OPTION_CONNECTION_LIMIT, cn_limit,
+ MHD_OPTION_CONNECTION_TIMEOUT, cn_timeout,
+ MHD_OPTION_END);
+
+ if(!d) {
+ //PRACRO_ERR(server, "Failed to initialise MHD_start_daemon!\n");
+ return;
+ }
+}
+
+void Httpd::listen_ssl(unsigned short int port,
+ std::string key, std::string cert,
+ unsigned int cn_limit, unsigned int cn_timeout)
+{
+ int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL;
+
+ d_ssl = MHD_start_daemon(flags, port, NULL, NULL,
+ request_handler, this,
+ MHD_OPTION_NOTIFY_COMPLETED, completed_handler, this,
+ MHD_OPTION_EXTERNAL_LOGGER, error_handler, this,
+ MHD_OPTION_CONNECTION_LIMIT, cn_limit,
+ MHD_OPTION_CONNECTION_TIMEOUT, cn_timeout,
+ MHD_OPTION_HTTPS_MEM_KEY, key.c_str(),
+ MHD_OPTION_HTTPS_MEM_CERT, cert.c_str(),
+ MHD_OPTION_END);
+
+ if(!d_ssl) {
+ // PRACRO_ERR(server, "Failed to initialise MHD_start_daemon!\n");
+ return;
+ }
+}
+
+void Httpd::stop()
+{
+ if(is_running()) {
+ MHD_stop_daemon(d);
+ d = NULL;
+ }
+}
+
+void Httpd::stop_ssl()
+{
+ if(is_running_ssl()) {
+ MHD_stop_daemon(d_ssl);
+ d_ssl = NULL;
+ }
+}
+
+bool Httpd::is_running()
+{
+ return d != NULL;
+}
+
+bool Httpd::is_running_ssl()
+{
+ return d_ssl != NULL;
+}
+
+#ifdef TEST_HTTPD
+//deps:
+//cflags: $(HTTPD_CFLAGS)
+//libs: $(HTTPD_LIBS) -lcurl
+#include "test.h"
+
+#include <curl/curl.h>
+
+#define PORT 10008
+
+#define LONG_LEN 20000
+
+class TestHttpd : public Httpd {
+public:
+ TestHttpd() { fprintf(stderr, "TestHttpd()\n"); fflush(stderr); }
+ ~TestHttpd() { fprintf(stderr, "~TestHttpd()\n"); fflush(stderr); }
+ void error(const std::string &err)
+ {
+ fprintf(stderr, "ERROR: %s\n", err.c_str());
+ fflush(stderr);
+ }
+
+ void *begin(const std::string &url,
+ const std::string &method,
+ const std::string &version,
+ headers_t &headers)
+ {
+ fprintf(stderr, "begin(...)\n"); fflush(stderr);
+ std::string *s = new std::string;
+
+ headers_t::iterator i = headers.begin();
+ while(i != headers.end()) {
+ fprintf(stderr, "%s = \"%s\"\n", i->first.c_str(), i->second.c_str());
+ fflush(stderr);
+ i++;
+ }
+ /*
+ if(headers.find("foo") != headers.end() && headers["foo"] == "bar")
+ *s = "hdrok";
+ */
+ return s;
+ }
+
+ bool data(void *ptr, const char *data, unsigned int data_size)
+ {
+ std::string *s = (std::string*)ptr;
+ if(data && data_size) s->append(data, data_size);
+
+ fprintf(stderr, "data(...) (%p +%d %d)\n", ptr, data_size, s->length());
+ fflush(stderr);
+
+ return true;
+ }
+
+ bool complete(void *ptr, Httpd::Reply &reply)
+ {
+ std::string *s = (std::string*)ptr;
+ fprintf(stderr, "complete(...) (%p %d)\n", ptr, s->length());
+ fflush(stderr);
+ reply.data = *s;
+ reply.status = MHD_HTTP_OK;
+ //reply.headers[MHD_HTTP_HEADER_CONTENT_TYPE] = "text/plain; charset=UTF-8";
+ //reply.headers[MHD_HTTP_HEADER_CONTENT_TYPE] = "application/octet-stream";
+
+ return true;
+ }
+
+ void cleanup(void *ptr)
+ {
+ fprintf(stderr, "cleanup(...)\n"); fflush(stderr);
+
+ std::string *s = (std::string*)ptr;
+ delete s;
+ }
+};
+
+static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
+{
+ std::string *str = (std::string*)userp;
+ str->append((char*)buffer, size * nmemb);
+ return size * nmemb;
+}
+
+static std::string send(const std::string &msg, std::string name,
+ std::string value, CURLcode *ret)
+{
+ CURL *c = curl_easy_init();
+ curl_easy_setopt(c, CURLOPT_URL, "localhost");
+ curl_easy_setopt(c, CURLOPT_PORT, PORT);
+ curl_easy_setopt(c, CURLOPT_FAILONERROR, 1L);
+ curl_easy_setopt(c, CURLOPT_TIMEOUT, 2L);
+ curl_easy_setopt(c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+ curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, 2L);
+ curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(c, CURLOPT_USERAGENT, "TEST_HTTPD");
+
+ curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, (long)msg.length());
+ curl_easy_setopt(c, CURLOPT_POSTFIELDS, msg.data());
+ curl_easy_setopt(c, CURLOPT_POST, 1L);
+
+ std::string response;
+ curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(c, CURLOPT_WRITEDATA, &response);
+
+ struct curl_slist *slist=NULL;
+ slist = curl_slist_append(slist, (name + ": " + value).c_str());
+ slist = curl_slist_append(slist, "Content-Type: application/octet-stream");
+
+ curl_easy_setopt(c, CURLOPT_HTTPHEADER, slist);
+
+ *ret = curl_easy_perform(c);
+
+ curl_slist_free_all(slist);
+ curl_easy_cleanup(c);
+
+ return response;
+}
+
+TEST_BEGIN;
+
+TestHttpd httpd;
+httpd.listen(PORT);
+TEST_TRUE(httpd.is_running(), "Is the server running?");
+
+std::string r;
+
+CURLcode errornum;
+r = send("hello world", "foo", "bar", &errornum);
+TEST_EQUAL_INT(errornum, CURLE_OK, "Did perfom go well?");
+TEST_EQUAL_STR(r, "hello world", "Did we receive the correct answer?");
+
+std::string msg;
+msg.append(LONG_LEN, 0x00);
+r = send(msg, "foo", "bar", &errornum);
+TEST_EQUAL_INT(errornum, CURLE_OK, "Did perfom go well?");
+TEST_EQUAL(r, msg, "Did we receive the correct answer?");
+
+TEST_END;
+
+#endif/*TEST_HTTPD*/
diff --git a/server/src/httpd.h b/server/src/httpd.h
new file mode 100644
index 0000000..49d1517
--- /dev/null
+++ b/server/src/httpd.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ * httpd.h
+ *
+ * Thu Jun 10 09:05:10 CEST 2010
+ * Copyright 2010 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of Pracro.
+ *
+ * Pracro is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Pracro is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Pracro; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#ifndef __PRACRO_HTTPD_H__
+#define __PRACRO_HTTPD_H__
+
+#include <map>
+#include <string>
+
+class headers_t : public std::map< std::string, std::string > {
+public:
+ bool contains(std::string name) {
+ return find(name) != end();
+ }
+};
+
+struct MHD_Daemon;
+
+class Httpd {
+public:
+ class Reply {
+ public:
+ std::string data;
+ headers_t headers;
+ int status;
+ };
+
+ Httpd();
+ ~Httpd();
+
+ void listen(unsigned short int port,
+ unsigned int cn_limit = 1, unsigned int cn_timeout = 0);
+ void stop();
+ bool is_running();
+
+ void listen_ssl(unsigned short int port,
+ std::string key, std::string cert,
+ unsigned int cn_limit = 1, unsigned int cn_timeout = 0);
+ void stop_ssl();
+ bool is_running_ssl();
+
+ virtual void error(const std::string &err) {}
+
+ // The retruned void pointer will be given as an argument to data, complete
+ // and cleanup methods.
+ virtual void *begin(const std::string &url,
+ const std::string &method,
+ const std::string &version,
+ headers_t &headers) { return NULL; }
+
+ // Return false indicates error, and terminates connetion (no reply)
+ virtual bool data(void *ptr, const char *data,
+ unsigned int data_size) { return false; }
+
+ // Return false indicates error, and terminates connetion (no reply)
+ virtual bool complete(void *ptr, Httpd::Reply &reply) { return false; }
+
+ virtual void cleanup(void *ptr) {}
+
+private:
+ struct MHD_Daemon *d;
+ struct MHD_Daemon *d_ssl;
+};
+
+#endif/*__PRACRO_HTTPD_H__*/
diff --git a/server/src/server.cc b/server/src/server.cc
index 1f85479..f8f2d83 100644
--- a/server/src/server.cc
+++ b/server/src/server.cc
@@ -28,168 +28,94 @@
#include <config.h>
-#include "tcpsocket.h"
-#include <errno.h>
-
-#include <stdlib.h>
-
-// For fork
-#include <sys/types.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <microhttpd.h>
-
+#include "httpd.h"
#include "configuration.h"
+#include "environment.h"
#include "connection.h"
-#include "log.h"
-static int handle_request_callback(void *cls,
- struct MHD_Connection *con,
- const char *url,
- const char *method,
- const char *version,
- const char *data,
- unsigned int *data_size,
- void **con_cls)
-{
- int ret = MHD_YES;
-
- Connection *connection = (Connection*)*con_cls;
+class PracroHttpd : public Httpd {
+public:
+ PracroHttpd() {}
+ ~PracroHttpd()
+ {
+ env.sessions.store();
+ }
- PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n",
- con, *con_cls);
+ void error(const std::string &err)
+ {
+ fprintf(stderr, "ERROR: %s\n", err.c_str());
+ fflush(stderr);
+ }
- // Test for new connection
- if(connection == NULL) {
+ void *begin(const std::string &url,
+ const std::string &method,
+ const std::string &version,
+ headers_t &headers)
+ {
std::string sessionid;
- const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
- "SessionID");
- if(sid) sessionid = sid;
-
- const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
- "SessionCommit");
+ if(headers.contains("SessionID")) sessionid = headers["SessionID"];
+
+ bool commit = headers.contains("SessionCommit");
+ bool discard = headers.contains("Sessiondiscard");
- const char *sdc = MHD_lookup_connection_value(con, MHD_HEADER_KIND,
- "SessionDiscard");
+ Connection *connection = new Connection(env, sessionid, commit, discard);
- Environment *env = (Environment *)cls;
- connection = new Connection(*env, sessionid, scm != NULL, sdc != NULL);
- *con_cls = connection;
+ return connection;
}
- if(!connection) return MHD_NO;
-
- if(connection->handle(data, *data_size)) {
- std::string response = connection->getResponse();
-
- PRACRO_DEBUG(httpd, "Sending response: [[%s]]\n", response.c_str());
+ bool data(void *ptr, const char *data, unsigned int data_size)
+ {
+ Connection *connection = (Connection *)ptr;
+ connection->handle(data, data_size);
+ return true;
+ }
- struct MHD_Response *rsp =
- MHD_create_response_from_data(response.size(),
- (void*)response.data(),
- MHD_NO, // must free
- MHD_YES); // must copy
-
- MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE,
- "text/plain; charset=UTF-8");
+ bool complete(void *ptr, Httpd::Reply &reply)
+ {
+ Connection *connection = (Connection *)ptr;
- MHD_add_response_header(rsp, "SessionID",
- connection->getSessionID().c_str());
+ reply.data = connection->getResponse();
+ reply.headers["Content-Type"] = "text/plain; charset=UTF-8";
+ reply.headers["SessionID"] = connection->getSessionID();
+ reply.status = 200; // http 'OK'
- ret = MHD_queue_response(con, MHD_HTTP_OK, rsp);
- MHD_destroy_response(rsp);
-
- delete connection;
- *con_cls = NULL;
+ return true;
}
- *data_size = 0;
-
- return ret;
-}
-
-void requestCompletedCallback(void *cls,
- struct MHD_Connection *con,
- void **con_cls,
- enum MHD_RequestTerminationCode toe)
-{
- PRACRO_DEBUG(httpd, "requestCompletedCallback %p\n", con);
-
- // If connection was interrupted prematurely delete the content data here.
- if(*con_cls) {
- Connection *connection = (Connection*)*con_cls;
+ void cleanup(void *ptr)
+ {
+ Connection *connection = (Connection *)ptr;
delete connection;
- *con_cls = NULL;
}
-}
-static void httpderr(void *arg, const char *fmt, va_list ap)
-{
- PRACRO_ERR_VA(server, fmt, ap);
-}
+private:
+ Environment env;
+};
+
+
extern bool pracro_is_running;
void server()
{
- srand(time(NULL));
-
- // bool forceshutdown = false;
- port_t port = Conf::server_port;
-
- int flags = MHD_USE_DEBUG | MHD_USE_SELECT_INTERNALLY;
- // | MHD_USE_PEDANTIC_CHECKS
-#ifndef WITHOUT_SSL
- if(Conf::use_ssl) flags |= MHD_USE_SSL;
-#endif
-
- PRACRO_DEBUG(server, "Server running on port %d.\n", port);
-
- Environment env;
+ PracroHttpd httpd;
- struct MHD_Daemon *d;
- d = MHD_start_daemon(flags, port, NULL, NULL,
- handle_request_callback, &env,
- MHD_OPTION_NOTIFY_COMPLETED,
- requestCompletedCallback, NULL,
- MHD_OPTION_CONNECTION_LIMIT, Conf::connection_limit,
#ifndef WITHOUT_SSL
- MHD_OPTION_HTTPS_MEM_KEY, Conf::ssl_key.c_str(),
- MHD_OPTION_HTTPS_MEM_CERT, Conf::ssl_cert.c_str(),
+ if(Conf::use_ssl) httpd.listen_ssl(Conf::server_port,
+ Conf::ssl_key,
+ Conf::ssl_cert,
+ Conf::connection_limit,
+ Conf::connection_timeout);
+ else
#endif
- MHD_OPTION_CONNECTION_TIMEOUT, Conf::connection_timeout,
- MHD_OPTION_EXTERNAL_LOGGER, httpderr, NULL,
- MHD_OPTION_END);
+ httpd.listen(Conf::server_port,
+ Conf::connection_limit,
+ Conf::connection_timeout);
- if(!d) {
- PRACRO_ERR(server, "Failed to initialise MHD_start_daemon!\n");
- return;
- }
- // again:
while(pracro_is_running) sleep(1);
- /*
- if(!forceshutdown && env.sessions.size() != 0) {
- char *errbuf;
- if(asprintf(&errbuf, "There are %d live sessions."
- " Kill again to force shutdown.\n",
- env.sessions.size()) != -1) {
- PRACRO_ERR_LOG(server, "%s", errbuf);
- log(errbuf);
- free(errbuf);
- }
- pracro_is_running = true;
- forceshutdown = true;
- goto again;
- }
- */
- env.sessions.store();
-
- MHD_stop_daemon(d);
PRACRO_DEBUG(server, "Server gracefully shut down.\n");
}
-
#ifdef TEST_SERVER
#include <sys/types.h>