Fawkes API  Fawkes Development Version
server.cpp
00001 
00002 /***************************************************************************
00003  *  server.cpp - Web server encapsulation around libmicrohttpd
00004  *
00005  *  Created: Sun Aug 30 17:40:54 2009
00006  *  Copyright  2006-2011  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022 
00023 #include <webview/server.h>
00024 #include <webview/request_dispatcher.h>
00025 #include <core/exception.h>
00026 #include <core/exceptions/system.h>
00027 #include <logging/logger.h>
00028 
00029 #include <sys/socket.h>
00030 #include <cstdlib>
00031 #include <cstdio>
00032 #include <cerrno>
00033 #include <microhttpd.h>
00034 
00035 namespace fawkes {
00036 #if 0 /* just to make Emacs auto-indent happy */
00037 }
00038 #endif
00039 
00040 /** @class WebServer <webview/server.h>
00041  * Encapsulation of the libmicrohttpd webserver.
00042  * This class opens a port serving websites and calls the supplied dispatcher
00043  * for requests.
00044  * @author Tim Niemueller
00045  */
00046 
00047 /** Constructor.
00048  * @param port TCP port to listen on
00049  * @param dispatcher dispatcher to call for requests
00050  * @param logger optional logger, used to output possible run-time problems
00051  */
00052 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
00053                      fawkes::Logger *logger)
00054 {
00055   __port         = port;
00056   __dispatcher   = dispatcher;
00057   __logger       = logger;
00058 
00059   __ssl_key_mem  = NULL;
00060   __ssl_cert_mem = NULL;
00061 
00062   __daemon = MHD_start_daemon(MHD_NO_FLAG,
00063                               __port,
00064                               NULL,
00065                               NULL,
00066                               WebRequestDispatcher::process_request_cb,
00067                               (void *)__dispatcher,
00068                               MHD_OPTION_END);
00069 
00070   if ( __daemon == NULL ) {
00071     throw fawkes::Exception("Could not start microhttpd");
00072   }
00073 
00074 }
00075 
00076 /** SSL constructor.
00077  * @param port TCP port to listen on
00078  * @param dispatcher dispatcher to call for requests
00079  * @param key_pem_filepath path to PEM formatted file containing the key
00080  * @param cert_pem_filepath path to PEM formatted file containing the certificate
00081  * @param logger optional logger, used to output possible run-time problems
00082  */
00083 WebServer::WebServer(unsigned short int port, WebRequestDispatcher *dispatcher,
00084                      const char *key_pem_filepath, const char *cert_pem_filepath,
00085                      fawkes::Logger *logger)
00086 {
00087   __port       = port;
00088   __dispatcher = dispatcher;
00089   __logger     = logger;
00090 
00091   __ssl_key_mem  = read_file(key_pem_filepath);
00092   __ssl_cert_mem = read_file(cert_pem_filepath);
00093 
00094   __daemon = MHD_start_daemon(MHD_USE_SSL,
00095                               __port,
00096                               NULL,
00097                               NULL,
00098                               WebRequestDispatcher::process_request_cb,
00099                               (void *)__dispatcher,
00100                               MHD_OPTION_HTTPS_MEM_KEY,  __ssl_key_mem,
00101                               MHD_OPTION_HTTPS_MEM_CERT, __ssl_cert_mem,
00102                               MHD_OPTION_END);
00103 
00104   if ( __daemon == NULL ) {
00105     throw fawkes::Exception("Could not start microhttpd (SSL)");
00106   }
00107 
00108 }
00109 
00110 
00111 /** Destructor. */
00112 WebServer::~WebServer()
00113 {
00114   MHD_stop_daemon(__daemon);
00115   __daemon = NULL;
00116   __dispatcher = NULL;
00117 
00118   if (__ssl_key_mem)   free(__ssl_key_mem);
00119   if (__ssl_cert_mem)  free(__ssl_cert_mem);
00120 }
00121 
00122 
00123 /** Read file into memory.
00124  * @param filename file path
00125  * @return memory location of file content, free after done
00126  */
00127 char *
00128 WebServer::read_file(const char *filename)
00129 {
00130   FILE *f = fopen(filename, "rb");
00131   if (! f) {
00132     throw CouldNotOpenFileException(filename, errno);
00133   }
00134 
00135   long size = 0;
00136   if ((fseek(f, 0, SEEK_END) != 0) || ((size = ftell(f)) == 1)) {
00137     fclose(f);
00138     throw Exception("Cannot determine file size of %s", filename);
00139   }
00140   fseek(f, 0, SEEK_SET);
00141 
00142   if ( size == 0 ) {
00143     fclose(f);
00144     throw Exception("File %s has zero length", filename);
00145   } else if (size > 1024 * 1024) {
00146     // keys or certs should not be that long...
00147     fclose(f);
00148     throw Exception("File %s is unexpectedly large", filename);
00149   }
00150 
00151   char *rv = (char *)malloc(size);
00152   if (fread(rv, size, 1, f) != 1) {
00153     int terrno = errno;
00154     fclose(f);
00155     free(rv);
00156     throw FileReadException(filename, terrno);
00157   }
00158 
00159   fclose(f);
00160 
00161   return rv;
00162 }
00163 
00164 
00165 /** Setup basic authentication.
00166  * @param realm authentication realm to display to the user
00167  * @param verifier verifier to use for checking credentials
00168  */
00169 void
00170 WebServer::setup_basic_auth(const char *realm, WebUserVerifier *verifier)
00171 {
00172   __dispatcher->setup_basic_auth(realm, verifier);
00173 }
00174 
00175 
00176 /** Process requests.
00177  * This method waits for new requests and processes them when received.
00178  */
00179 void
00180 WebServer::process()
00181 {
00182   fd_set read_fd, write_fd, except_fd;
00183   int max_fd = 0;
00184   FD_ZERO(&read_fd); FD_ZERO(&write_fd); FD_ZERO(&except_fd);
00185   if ( MHD_get_fdset(__daemon, &read_fd, &write_fd, &except_fd, &max_fd) != MHD_YES ) {
00186     if (__logger)
00187       __logger->log_warn("WebviewThread", "Could not get microhttpd fdsets");
00188     return;
00189   }
00190   select(max_fd + 1, &read_fd, &write_fd, &except_fd, NULL);
00191   MHD_run(__daemon);
00192 }
00193 
00194 } // end namespace fawkes