- Main Page
- Classes
- Files

- File List

# src/http_server.cpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.com)
    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