diff options
Diffstat (limited to 'server')
| -rw-r--r-- | server/src/Makefile.am | 2 | ||||
| -rw-r--r-- | server/src/connection.cc | 6 | ||||
| -rw-r--r-- | server/src/connection.h | 2 | ||||
| -rw-r--r-- | server/src/httpd.cc | 369 | ||||
| -rw-r--r-- | server/src/httpd.h | 89 | ||||
| -rw-r--r-- | server/src/server.cc | 188 | 
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> | 
