request_dispatcher.cpp

00001 
00002 /***************************************************************************
00003  *  request_dispatcher.cpp - Web request dispatcher
00004  *
00005  *  Created: Mon Oct 13 22:48:04 2008
00006  *  Copyright  2006-2008  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/request_dispatcher.h>
00024 #include <webview/request_processor.h>
00025 #include <webview/page_reply.h>
00026 #include <webview/error_reply.h>
00027 
00028 #include <utils/misc/string_urlescape.h>
00029 
00030 #include <sys/types.h>
00031 #include <sys/socket.h>
00032 #include <cstdarg>
00033 #include <microhttpd.h>
00034 #include <cstring>
00035 #include <cstdlib>
00036 
00037 namespace fawkes {
00038 #if 0 /* just to make Emacs auto-indent happy */
00039 }
00040 #endif
00041 
00042 /** @class WebRequestDispatcher "request_dispatcher.h"
00043  * Web request dispatcher.
00044  * Takes web request received via a webserver run by libmicrohttpd and dispatches
00045  * pages to registered WebRequestProcessor instances or gives a 404 error if no
00046  * processor was registered for the given base url.
00047  * @author Tim Niemueller
00048  */
00049 
00050 /** Constructor.
00051  * @param headergen page header generator
00052  * @param footergen page footer generator
00053  */
00054 WebRequestDispatcher::WebRequestDispatcher(WebPageHeaderGenerator *headergen,
00055                                            WebPageFooterGenerator *footergen)
00056 {
00057   __page_header_generator = headergen;
00058   __page_footer_generator = footergen;
00059 }
00060 
00061 
00062 /** Process request callback for libmicrohttpd.
00063  * @param callback_data instance of WebRequestDispatcher to call
00064  * @param connection libmicrohttpd connection instance
00065  * @param url URL, may contain escape sequences
00066  * @param method HTTP method
00067  * @param version HTTP version
00068  * @param upload_data uploaded data
00069  * @param upload_data_size size of upload_data parameter
00070  * @param session_data session data pointer
00071  * @return appropriate return code for libmicrohttpd
00072  */
00073 int
00074 WebRequestDispatcher::process_request_cb(void *callback_data,
00075                                          struct MHD_Connection * connection,
00076                                          const char *url,
00077                                          const char *method,
00078                                          const char *version,
00079                                          const char *upload_data,
00080                                          size_t *upload_data_size,
00081                                          void **session_data)
00082 {
00083   WebRequestDispatcher *rd = static_cast<WebRequestDispatcher *>(callback_data);
00084   return rd->process_request(connection, url, method, version,
00085                              upload_data, upload_data_size, session_data);
00086 }
00087 
00088 
00089 /** Callback based chunk-wise data.
00090  * Supplies data chunk based.
00091  * @param reply instance of DynamicWebReply
00092  * @param pos position in stream
00093  * @param buf buffer to put data in
00094  * @param max maximum number of bytes that can be put in buf
00095  * @return suitable libmicrohttpd return code
00096  */
00097 #if MHD_VERSION >= 0x00090200
00098 static ssize_t
00099 dynamic_reply_data_cb(void *reply, uint64_t pos, char *buf, size_t max)
00100 #else
00101 static int
00102 dynamic_reply_data_cb(void *reply, uint64_t pos, char *buf, int max)
00103 #endif
00104 {
00105   DynamicWebReply *dreply = static_cast<DynamicWebReply *>(reply);
00106   return dreply->next_chunk(pos, buf, max);
00107 }
00108 
00109 
00110 /** Callback to free dynamic web reply.
00111  * @param reply Instance of DynamicWebReply to free.
00112  */
00113 static void
00114 dynamic_reply_free_cb(void *reply)
00115 {
00116   DynamicWebReply *dreply = static_cast<DynamicWebReply *>(reply);
00117   delete dreply;
00118 }
00119 
00120 
00121 /** Queue a static web reply.
00122  * @param connection libmicrohttpd connection to queue response to
00123  * @param sreply static web reply to queue
00124  * @return suitable libmicrohttpd return code
00125  */
00126 int
00127 WebRequestDispatcher::queue_static_reply(struct MHD_Connection * connection,
00128                                          StaticWebReply *sreply)
00129 {
00130   struct MHD_Response *response;
00131   WebPageReply *wpreply = dynamic_cast<WebPageReply *>(sreply);
00132   if (wpreply) {
00133     wpreply->pack(__active_baseurl,
00134                   __page_header_generator, __page_footer_generator);
00135   } else {
00136     sreply->pack();
00137   }
00138   if (sreply->body_length() > 0) {
00139     response = MHD_create_response_from_data(sreply->body_length(),
00140                                              (void*) sreply->body().c_str(),
00141                                              /* free */ MHD_YES,
00142                                              /* copy */ MHD_YES);
00143   } else {
00144     response = MHD_create_response_from_data(0, (void*) "",
00145                                              /* free */ MHD_NO,
00146                                              /* copy */ MHD_NO);
00147   }
00148 
00149   const WebReply::HeaderMap &headers = sreply->headers();
00150   for (WebReply::HeaderMap::const_iterator i = headers.begin(); i != headers.end(); ++i) {
00151     MHD_add_response_header(response, i->first.c_str(), i->second.c_str());
00152   }
00153 
00154   int rv = MHD_queue_response(connection, sreply->code(), response);
00155   MHD_destroy_response(response);
00156   return rv;
00157 }
00158 
00159 
00160 /** Process request callback for libmicrohttpd.
00161  * @param connection libmicrohttpd connection instance
00162  * @param url URL, may contain escape sequences
00163  * @param method HTTP method
00164  * @param version HTTP version
00165  * @param upload_data uploaded data
00166  * @param upload_data_size size of upload_data parameter
00167  * @param session_data session data pointer
00168  * @return appropriate return code for libmicrohttpd
00169  */
00170 int
00171 WebRequestDispatcher::process_request(struct MHD_Connection * connection,
00172                                       const char *url,
00173                                       const char *method,
00174                                       const char *version,
00175                                       const char *upload_data,
00176                                       size_t *upload_data_size,
00177                                       void **session_data)
00178 {
00179   std::string surl = url;
00180   static int dummy;
00181   int ret;
00182 
00183   if ((0 != strcmp(method, "GET")) && (0 != strcmp(method, "POST")))
00184     return MHD_NO; /* unexpected method */
00185 
00186   WebRequestProcessor *proc = NULL;
00187   std::map<std::string, WebRequestProcessor *>::iterator __pit;
00188   for (__pit = __processors.begin(); (proc == NULL) && (__pit != __processors.end()); ++__pit) {
00189     if (surl.find(__pit->first) == 0) {
00190       __active_baseurl = __pit->first;
00191       proc = __pit->second;
00192     }
00193   }
00194 
00195   if ( surl == "/" ) {
00196     if ( __startpage_processor ) {
00197       proc = __startpage_processor;
00198     } else {
00199       WebPageReply preply("Fawkes", "<h1>Welcome to Fawkes.</h1><hr />");
00200       ret = queue_static_reply(connection, &preply);
00201     }
00202   }
00203 
00204   if (proc) {
00205     char *urlc = strdup(url);
00206     fawkes::hex_unescape(urlc);
00207     std::string urls = urlc;
00208     free(urlc);
00209 
00210     if (! proc->handles_session_data()) {
00211       if ( *session_data == NULL) {
00212         // The first time only the headers are valid,
00213         // do not respond in the first round...
00214         *session_data = &dummy;
00215         return MHD_YES;
00216       }
00217       *session_data = NULL; /* clear context pointer */
00218     } else {
00219       if ( *session_data == NULL) {
00220         WebReply *reply = proc->process_request(urls.c_str(), method, version, upload_data, upload_data_size, session_data);
00221         if ((reply != NULL) || (*session_data == NULL)) {
00222           return MHD_NO;
00223         } else {
00224           return MHD_YES;
00225         }
00226       }
00227     }
00228 
00229     struct MHD_Response *response;
00230 
00231 
00232     WebReply *reply = proc->process_request(urls.c_str(), method, version, upload_data, upload_data_size, session_data);
00233 
00234     if ( reply ) {
00235       StaticWebReply  *sreply = dynamic_cast<StaticWebReply *>(reply);
00236       DynamicWebReply *dreply = dynamic_cast<DynamicWebReply *>(reply);
00237       if (sreply) {
00238         ret = queue_static_reply(connection, sreply);
00239         delete reply;
00240       } else if (dreply) {
00241         response = MHD_create_response_from_callback(dreply->size(),
00242                                                      dreply->chunk_size(),
00243                                                      dynamic_reply_data_cb,
00244                                                      dreply,
00245                                                      dynamic_reply_free_cb);
00246         ret = MHD_queue_response (connection, dreply->code(), response);
00247         MHD_destroy_response (response);
00248       } else {
00249         WebErrorPageReply ereply(WebReply::HTTP_INTERNAL_SERVER_ERROR);
00250         ret = queue_static_reply(connection, &ereply);
00251         delete reply;
00252       }
00253     } else {
00254       if (proc->handles_session_data()) {
00255         return MHD_YES;
00256       } else {
00257         WebErrorPageReply ereply(WebReply::HTTP_NOT_FOUND);
00258         ret = queue_static_reply(connection, &ereply);
00259       }
00260     }
00261   } else {
00262     WebErrorPageReply ereply(WebReply::HTTP_NOT_FOUND);
00263     ret = queue_static_reply(connection, &ereply);
00264   }
00265   return ret;
00266 }
00267 
00268 /** Add a request processor.
00269  * @param url_prefix baseurl this processor should handle
00270  * @param processor processor for baseurl
00271  */
00272 void
00273 WebRequestDispatcher::add_processor(const char *url_prefix,
00274                                     WebRequestProcessor *processor)
00275 {
00276   if (std::string(url_prefix) == "/") {
00277     __startpage_processor = processor;
00278   } else {
00279     __processors[url_prefix] = processor;
00280   }
00281 }
00282 
00283 
00284 /** Remove a request processor.
00285  * @param url_prefix baseurl the processor handled
00286  */
00287 void
00288 WebRequestDispatcher::remove_processor(const char *url_prefix)
00289 {
00290   if (std::string(url_prefix) == "/") {
00291     __startpage_processor = NULL;
00292   } else {
00293     __processors.erase(url_prefix);
00294   }
00295 }
00296 
00297 } // end namespace fawkes

Generated on Tue Feb 22 13:32:29 2011 for Fawkes API by  doxygen 1.4.7