Fawkes API Fawkes Development Version
|
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