/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * server.cc * * Wed Aug 22 12:16:03 CEST 2007 * Copyright 2007 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 "server.h" #include #include "tcpsocket.h" #include #include // For fork #include #include #include #include #include "configuration.h" #include "transaction.h" #include "transactionparser.h" #include "database.h" #include "log.h" #include "environment.h" #include "transactionhandler.h" #include "connectionpool.h" #include "session.h" #include "xml_encode_decode.h" static std::string error_box(std::string message) { std::string errorbox = "\n" "\n" " " + message + "\n" "\n"; return errorbox; } static std::string handleConnection(const std::string &data, Environment &env, std::string &sessionid, bool sessioncommit) { std::string res; Session *session = NULL; if(sessionid == "") { session = env.sessions.newSession(); } else { session = env.sessions.session(sessionid); if(session == NULL) session = env.sessions.newSession(); } if(session == NULL) { PRACRO_ERR(server, "New session could not be created."); return error_box(xml_encode("New session could not be created.")); } sessionid = session->id(); { SessionAutolock lock(*session); if(data.length()) { Transaction transaction; TransactionParser parser(&transaction); if(!parser.parse(data.c_str(), data.length())) { PRACRO_ERR(server, "Failed to parse data!\n"); PRACRO_ERR(server, "DATA:[[%s]]\n", data.c_str()); res = error_box(xml_encode("XML Parse error.")); } else { res = handleTransaction(transaction, env, *session); } } } if(sessioncommit) { session->commit(); env.sessions.deleteSession(session->id()); } return res; } struct condata { std::map headers; std::string data; size_t data_size; std::string url; std::string method; std::string version; }; //static std::map condata; 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) { PRACRO_DEBUG(httpd, "handle_request_callback con:%p condata:%p\n", con, *con_cls); // Test for new connection if(*con_cls == NULL) { PRACRO_DEBUG(httpd, "handle_request(url=\"%s\", method=\"%s\"," " version=\"%s\", data_size=\"%d\")\n", url, method, version, *data_size); struct condata *cd = new struct condata; cd->url = url; cd->method = method; cd->version = version; cd->data_size = 0; cd->data = ""; const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionID"); if(sid) cd->headers["SessionID"] = sid; const char *scm = MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionCommit"); if(scm) cd->headers["SessionCommit"] = scm; const char *csz = MHD_lookup_connection_value(con, MHD_HEADER_KIND, "Content-Length"); if(csz) { cd->headers["Content-Length"] = csz; cd->data_size = atol(csz); PRACRO_DEBUG(httpd, "Content-Length: %s (%d)\n", csz, cd->data_size); } *con_cls = cd; } struct condata *cd = (struct condata*)*con_cls; cd->data.append(data, *data_size); int ret = MHD_YES; PRACRO_DEBUG(httpd, "Waiting for %d bytes of data. Got %d (total %d)\n", cd->data_size, *data_size, cd->data.length()); if(cd->data.length() >= cd->data_size) { Environment *env = (Environment *)cls; const char *sid = MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionID"); std::string sessionid; if(sid) sessionid = sid; const char *sessioncommit = MHD_lookup_connection_value(con, MHD_HEADER_KIND, "SessionCommit"); PRACRO_DEBUG(httpd, "Starting to handle SessionID: %s%s\n", sessionid.c_str(), sessioncommit != NULL?" COMMIT":""); PRACRO_DEBUG(httpd, "Data: [%s]\n", cd->data.c_str()); std::string reply = handleConnection(cd->data, *env, sessionid, sessioncommit != NULL); struct MHD_Response *rsp = MHD_create_response_from_data(reply.length(), (char*)reply.c_str(), MHD_NO, MHD_YES); MHD_add_response_header(rsp, MHD_HTTP_HEADER_CONTENT_TYPE, "text/plain; charset=UTF-8"); if(sessionid != "") { MHD_add_response_header(rsp, "SessionID", sessionid.c_str()); } ret = MHD_queue_response(con, MHD_HTTP_OK, rsp); MHD_destroy_response(rsp); PRACRO_DEBUG(httpd, "Finished handling SessionID: %s%s\n", sessionid.c_str(), sessioncommit != NULL?" COMMIT":""); delete cd; *con_cls = NULL; } *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) { struct condata *cd = (struct condata*)*con_cls; delete cd; *con_cls = NULL; } } static void httpderr(void *arg, const char *fmt, va_list ap) { PRACRO_ERR_VA(server, fmt, ap); } 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; 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(), #endif MHD_OPTION_CONNECTION_TIMEOUT, Conf::connection_timeout, MHD_OPTION_EXTERNAL_LOGGER, httpderr, NULL, MHD_OPTION_END); 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; } MHD_stop_daemon(d); PRACRO_DEBUG(server, "Server gracefully shut down.\n"); } #ifdef TEST_SERVER #include #include bool pracro_is_running = true; char request[] = "\n" "\n" " \n" "\n"; int main() { Conf::xml_basedir = "../xml/"; // Make sure wo don't interrupt an already running server. Conf::server_port = 32100; Conf::database_backend = "testdb"; pid_t pid = fork(); switch(pid) { case -1: // error perror("fork() failed!\n"); return 1; case 0: // child try { server(); } catch(Exception &e) { printf(e.what()); return 1; } return 0; default: // parent try { // sleep(1); // Make sure the server is started. TCPSocket socket; socket.connect("localhost", Conf::server_port); socket.write(request); // sleep(1); // Make sure the server has handled the request. char buf[32]; memset(buf, 0, sizeof(buf)); // while(socket.read(buf, 31, 1000)) { while(socket.read(buf, 31, 1000000)) { printf(buf); fflush(stdout); memset(buf, 0, sizeof(buf)); } } catch(Exception &e) { printf(e.what()); kill(pid, SIGKILL); // Kill the server again. return 1; } kill(pid, SIGKILL); // Kill the server again. return 0; } return 1; } #endif/*TEST_SERVER*/