pion
5.0.6
|
00001 // --------------------------------------------------------------------- 00002 // pion: a Boost C++ framework for building lightweight HTTP interfaces 00003 // --------------------------------------------------------------------- 00004 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion) 00005 // 00006 // Distributed under the Boost Software License, Version 1.0. 00007 // See http://www.boost.org/LICENSE_1_0.txt 00008 // 00009 00010 #include <boost/exception/diagnostic_information.hpp> 00011 #include <pion/algorithm.hpp> 00012 #include <pion/http/server.hpp> 00013 #include <pion/http/request.hpp> 00014 #include <pion/http/request_reader.hpp> 00015 #include <pion/http/response_writer.hpp> 00016 00017 00018 namespace pion { // begin namespace pion 00019 namespace http { // begin namespace http 00020 00021 00022 // static members of server 00023 00024 const unsigned int server::MAX_REDIRECTS = 10; 00025 00026 00027 // server member functions 00028 00029 void server::handle_connection(tcp::connection_ptr& tcp_conn) 00030 { 00031 request_reader_ptr my_reader_ptr; 00032 my_reader_ptr = request_reader::create(tcp_conn, boost::bind(&server::handle_request, 00033 this, _1, _2, _3)); 00034 my_reader_ptr->set_max_content_length(m_max_content_length); 00035 my_reader_ptr->receive(); 00036 } 00037 00038 void server::handle_request(http::request_ptr& http_request_ptr, 00039 tcp::connection_ptr& tcp_conn, const boost::system::error_code& ec) 00040 { 00041 if (ec || ! http_request_ptr->is_valid()) { 00042 tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE); // make sure it will get closed 00043 if (tcp_conn->is_open() && (ec.category() == http::parser::get_error_category())) { 00044 // HTTP parser error 00045 PION_LOG_INFO(m_logger, "Invalid HTTP request (" << ec.message() << ")"); 00046 m_bad_request_handler(http_request_ptr, tcp_conn); 00047 } else { 00048 static const boost::system::error_condition 00049 ERRCOND_CANCELED(boost::system::errc::operation_canceled, boost::system::system_category()), 00050 ERRCOND_EOF(boost::asio::error::eof, boost::asio::error::misc_category); 00051 00052 if (ec == ERRCOND_CANCELED || ec == ERRCOND_EOF) { 00053 // don't spam the log with common (non-)errors that happen during normal operation 00054 PION_LOG_DEBUG(m_logger, "Lost connection on port " << get_port() << " (" << ec.message() << ")"); 00055 } else { 00056 PION_LOG_INFO(m_logger, "Lost connection on port " << get_port() << " (" << ec.message() << ")"); 00057 } 00058 00059 tcp_conn->finish(); 00060 } 00061 return; 00062 } 00063 00064 PION_LOG_DEBUG(m_logger, "Received a valid HTTP request"); 00065 00066 // strip off trailing slash if the request has one 00067 std::string resource_requested(strip_trailing_slash(http_request_ptr->get_resource())); 00068 00069 // apply any redirection 00070 redirect_map_t::const_iterator it = m_redirects.find(resource_requested); 00071 unsigned int num_redirects = 0; 00072 while (it != m_redirects.end()) { 00073 if (++num_redirects > MAX_REDIRECTS) { 00074 PION_LOG_ERROR(m_logger, "Maximum number of redirects (server::MAX_REDIRECTS) exceeded for requested resource: " << http_request_ptr->get_original_resource()); 00075 m_server_error_handler(http_request_ptr, tcp_conn, "Maximum number of redirects (server::MAX_REDIRECTS) exceeded for requested resource"); 00076 return; 00077 } 00078 resource_requested = it->second; 00079 http_request_ptr->change_resource(resource_requested); 00080 it = m_redirects.find(resource_requested); 00081 } 00082 00083 // if authentication activated, check current request 00084 if (m_auth_ptr) { 00085 // try to verify authentication 00086 if (! m_auth_ptr->handle_request(http_request_ptr, tcp_conn)) { 00087 // the HTTP 401 message has already been sent by the authentication object 00088 PION_LOG_DEBUG(m_logger, "Authentication required for HTTP resource: " 00089 << resource_requested); 00090 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) { 00091 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource()); 00092 } 00093 return; 00094 } 00095 } 00096 00097 // search for a handler matching the resource requested 00098 request_handler_t request_handler; 00099 if (find_request_handler(resource_requested, request_handler)) { 00100 00101 // try to handle the request 00102 try { 00103 request_handler(http_request_ptr, tcp_conn); 00104 PION_LOG_DEBUG(m_logger, "Found request handler for HTTP resource: " 00105 << resource_requested); 00106 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) { 00107 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource()); 00108 } 00109 } catch (std::bad_alloc&) { 00110 // propagate memory errors (FATAL) 00111 throw; 00112 } catch (std::exception& e) { 00113 // recover gracefully from other exceptions thrown by request handlers 00114 PION_LOG_ERROR(m_logger, "HTTP request handler: " << pion::diagnostic_information(e)); 00115 m_server_error_handler(http_request_ptr, tcp_conn, e.what()); 00116 } catch (boost::exception& e) { 00117 // recover gracefully from boost exceptions thrown by request handlers 00118 PION_LOG_ERROR(m_logger, "HTTP request handler: " << pion::diagnostic_information(e)); 00119 m_server_error_handler(http_request_ptr, tcp_conn, pion::diagnostic_information(e)); 00120 } 00121 00122 } else { 00123 00124 // no web services found that could handle the request 00125 PION_LOG_INFO(m_logger, "No HTTP request handlers found for resource: " 00126 << resource_requested); 00127 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) { 00128 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource()); 00129 } 00130 m_not_found_handler(http_request_ptr, tcp_conn); 00131 } 00132 } 00133 00134 bool server::find_request_handler(const std::string& resource, 00135 request_handler_t& request_handler) const 00136 { 00137 // first make sure that HTTP resources are registered 00138 boost::mutex::scoped_lock resource_lock(m_resource_mutex); 00139 if (m_resources.empty()) 00140 return false; 00141 00142 // iterate through each resource entry that may match the resource 00143 resource_map_t::const_iterator i = m_resources.upper_bound(resource); 00144 while (i != m_resources.begin()) { 00145 --i; 00146 // check for a match if the first part of the strings match 00147 if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) { 00148 // only if the resource matches the plug-in's identifier 00149 // or if resource is followed first with a '/' character 00150 if (resource.size() == i->first.size() || resource[i->first.size()]=='/') { 00151 request_handler = i->second; 00152 return true; 00153 } 00154 } 00155 } 00156 00157 return false; 00158 } 00159 00160 void server::add_resource(const std::string& resource, 00161 request_handler_t request_handler) 00162 { 00163 boost::mutex::scoped_lock resource_lock(m_resource_mutex); 00164 const std::string clean_resource(strip_trailing_slash(resource)); 00165 m_resources.insert(std::make_pair(clean_resource, request_handler)); 00166 PION_LOG_INFO(m_logger, "Added request handler for HTTP resource: " << clean_resource); 00167 } 00168 00169 void server::remove_resource(const std::string& resource) 00170 { 00171 boost::mutex::scoped_lock resource_lock(m_resource_mutex); 00172 const std::string clean_resource(strip_trailing_slash(resource)); 00173 m_resources.erase(clean_resource); 00174 PION_LOG_INFO(m_logger, "Removed request handler for HTTP resource: " << clean_resource); 00175 } 00176 00177 void server::add_redirect(const std::string& requested_resource, 00178 const std::string& new_resource) 00179 { 00180 boost::mutex::scoped_lock resource_lock(m_resource_mutex); 00181 const std::string clean_requested_resource(strip_trailing_slash(requested_resource)); 00182 const std::string clean_new_resource(strip_trailing_slash(new_resource)); 00183 m_redirects.insert(std::make_pair(clean_requested_resource, clean_new_resource)); 00184 PION_LOG_INFO(m_logger, "Added redirection for HTTP resource " << clean_requested_resource << " to resource " << clean_new_resource); 00185 } 00186 00187 void server::handle_bad_request(http::request_ptr& http_request_ptr, 00188 tcp::connection_ptr& tcp_conn) 00189 { 00190 static const std::string BAD_REQUEST_HTML = 00191 "<html><head>\n" 00192 "<title>400 Bad Request</title>\n" 00193 "</head><body>\n" 00194 "<h1>Bad Request</h1>\n" 00195 "<p>Your browser sent a request that this server could not understand.</p>\n" 00196 "</body></html>\n"; 00197 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00198 boost::bind(&tcp::connection::finish, tcp_conn))); 00199 writer->get_response().set_status_code(http::types::RESPONSE_CODE_BAD_REQUEST); 00200 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_BAD_REQUEST); 00201 writer->write_no_copy(BAD_REQUEST_HTML); 00202 writer->send(); 00203 } 00204 00205 void server::handle_not_found_request(http::request_ptr& http_request_ptr, 00206 tcp::connection_ptr& tcp_conn) 00207 { 00208 static const std::string NOT_FOUND_HTML_START = 00209 "<html><head>\n" 00210 "<title>404 Not Found</title>\n" 00211 "</head><body>\n" 00212 "<h1>Not Found</h1>\n" 00213 "<p>The requested URL "; 00214 static const std::string NOT_FOUND_HTML_FINISH = 00215 " was not found on this server.</p>\n" 00216 "</body></html>\n"; 00217 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00218 boost::bind(&tcp::connection::finish, tcp_conn))); 00219 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_FOUND); 00220 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND); 00221 writer->write_no_copy(NOT_FOUND_HTML_START); 00222 writer << algorithm::xml_encode(http_request_ptr->get_resource()); 00223 writer->write_no_copy(NOT_FOUND_HTML_FINISH); 00224 writer->send(); 00225 } 00226 00227 void server::handle_server_error(http::request_ptr& http_request_ptr, 00228 tcp::connection_ptr& tcp_conn, 00229 const std::string& error_msg) 00230 { 00231 static const std::string SERVER_ERROR_HTML_START = 00232 "<html><head>\n" 00233 "<title>500 Server Error</title>\n" 00234 "</head><body>\n" 00235 "<h1>Internal Server Error</h1>\n" 00236 "<p>The server encountered an internal error: <strong>"; 00237 static const std::string SERVER_ERROR_HTML_FINISH = 00238 "</strong></p>\n" 00239 "</body></html>\n"; 00240 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00241 boost::bind(&tcp::connection::finish, tcp_conn))); 00242 writer->get_response().set_status_code(http::types::RESPONSE_CODE_SERVER_ERROR); 00243 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_SERVER_ERROR); 00244 writer->write_no_copy(SERVER_ERROR_HTML_START); 00245 writer << algorithm::xml_encode(error_msg); 00246 writer->write_no_copy(SERVER_ERROR_HTML_FINISH); 00247 writer->send(); 00248 } 00249 00250 void server::handle_forbidden_request(http::request_ptr& http_request_ptr, 00251 tcp::connection_ptr& tcp_conn, 00252 const std::string& error_msg) 00253 { 00254 static const std::string FORBIDDEN_HTML_START = 00255 "<html><head>\n" 00256 "<title>403 Forbidden</title>\n" 00257 "</head><body>\n" 00258 "<h1>Forbidden</h1>\n" 00259 "<p>User not authorized to access the requested URL "; 00260 static const std::string FORBIDDEN_HTML_MIDDLE = 00261 "</p><p><strong>\n"; 00262 static const std::string FORBIDDEN_HTML_FINISH = 00263 "</strong></p>\n" 00264 "</body></html>\n"; 00265 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00266 boost::bind(&tcp::connection::finish, tcp_conn))); 00267 writer->get_response().set_status_code(http::types::RESPONSE_CODE_FORBIDDEN); 00268 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_FORBIDDEN); 00269 writer->write_no_copy(FORBIDDEN_HTML_START); 00270 writer << algorithm::xml_encode(http_request_ptr->get_resource()); 00271 writer->write_no_copy(FORBIDDEN_HTML_MIDDLE); 00272 writer << error_msg; 00273 writer->write_no_copy(FORBIDDEN_HTML_FINISH); 00274 writer->send(); 00275 } 00276 00277 void server::handle_method_not_allowed(http::request_ptr& http_request_ptr, 00278 tcp::connection_ptr& tcp_conn, 00279 const std::string& allowed_methods) 00280 { 00281 static const std::string NOT_ALLOWED_HTML_START = 00282 "<html><head>\n" 00283 "<title>405 Method Not Allowed</title>\n" 00284 "</head><body>\n" 00285 "<h1>Not Allowed</h1>\n" 00286 "<p>The requested method "; 00287 static const std::string NOT_ALLOWED_HTML_FINISH = 00288 " is not allowed on this server.</p>\n" 00289 "</body></html>\n"; 00290 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00291 boost::bind(&tcp::connection::finish, tcp_conn))); 00292 writer->get_response().set_status_code(http::types::RESPONSE_CODE_METHOD_NOT_ALLOWED); 00293 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED); 00294 if (! allowed_methods.empty()) 00295 writer->get_response().add_header("Allow", allowed_methods); 00296 writer->write_no_copy(NOT_ALLOWED_HTML_START); 00297 writer << algorithm::xml_encode(http_request_ptr->get_method()); 00298 writer->write_no_copy(NOT_ALLOWED_HTML_FINISH); 00299 writer->send(); 00300 } 00301 00302 } // end namespace http 00303 } // end namespace pion