# net/src/HTTPServer.cpp

    00001 // ------------------------------------------------------------------
    00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
    00003 // ------------------------------------------------------------------
    00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.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 <pion/net/HTTPServer.hpp>
    00011 #include <pion/net/HTTPRequest.hpp>
    00012 #include <pion/net/HTTPRequestReader.hpp>
    00013 #include <pion/net/HTTPResponseWriter.hpp>
    00014
    00015
    00016 namespace pion {    // begin namespace pion
    00017 namespace net {     // begin namespace net (Pion Network Library)
    00018
    00019
    00020 // static members of HTTPServer
    00021
    00022 const unsigned int          HTTPServer::MAX_REDIRECTS = 10;
    00023
    00024
    00025 // HTTPServer member functions
    00026
    00027 void HTTPServer::handleConnection(TCPConnectionPtr& tcp_conn)
    00028 {
    00029     HTTPRequestReaderPtr reader_ptr;
    00030     reader_ptr = HTTPRequestReader::create(tcp_conn, boost::bind(&HTTPServer::handleRequest,
    00031                                            this, _1, _2, _3));
    00032     reader_ptr->setMaxContentLength(m_max_content_length);
    00033     reader_ptr->receive();
    00034 }
    00035
    00036 void HTTPServer::handleRequest(HTTPRequestPtr& http_request,
    00037     TCPConnectionPtr& tcp_conn, const boost::system::error_code& ec)
    00038 {
    00039     if (ec || ! http_request->isValid()) {
    00040         tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // make sure it will get closed
    00041         if (tcp_conn->is_open() && (&ec.category() == &HTTPParser::getErrorCategory())) {
    00042             // HTTP parser error
    00043             PION_LOG_INFO(m_logger, "Invalid HTTP request (" << ec.message() << ")");
    00044             m_bad_request_handler(http_request, tcp_conn);
    00045         } else {
    00046             // other (IO) error
    00047             PION_LOG_INFO(m_logger, "Lost connection on port " << getPort());
    00048             tcp_conn->finish();
    00049         }
    00050         return;
    00051     }
    00052
    00053     PION_LOG_DEBUG(m_logger, "Received a valid HTTP request");
    00054
    00055     // strip off trailing slash if the request has one
    00056     std::string resource_requested(stripTrailingSlash(http_request->getResource()));
    00057
    00058     // apply any redirection
    00059     RedirectMap::const_iterator it = m_redirects.find(resource_requested);
    00060     unsigned int num_redirects = 0;
    00061     while (it != m_redirects.end()) {
    00062         if (++num_redirects > MAX_REDIRECTS) {
    00063             PION_LOG_ERROR(m_logger, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource: " << http_request->getOriginalResource());
    00064             m_server_error_handler(http_request, tcp_conn, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource");
    00065             return;
    00066         }
    00067         resource_requested = it->second;
    00068         http_request->changeResource(resource_requested);
    00069         it = m_redirects.find(resource_requested);
    00070     }
    00071
    00072     // if authentication activated, check current request
    00073     if (m_auth) {
    00074         // try to verify authentication
    00075         if (! m_auth->handleRequest(http_request, tcp_conn)) {
    00076             // the HTTP 401 message has already been sent by the authentication object
    00077             PION_LOG_DEBUG(m_logger, "Authentication required for HTTP resource: "
    00078                 << resource_requested);
    00079             if (http_request->getResource() != http_request->getOriginalResource()) {
    00080                 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
    00081             }
    00082             return;
    00083         }
    00084     }
    00085
    00086     // search for a handler matching the resource requested
    00087     RequestHandler request_handler;
    00088     if (findRequestHandler(resource_requested, request_handler)) {
    00089
    00090         // try to handle the request
    00091         try {
    00092             request_handler(http_request, tcp_conn);
    00093             PION_LOG_DEBUG(m_logger, "Found request handler for HTTP resource: "
    00094                            << resource_requested);
    00095             if (http_request->getResource() != http_request->getOriginalResource()) {
    00096                 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
    00097             }
    00098         } catch (std::bad_alloc&) {
    00099             // propagate memory errors (FATAL)
    00100             throw;
    00101         } catch (std::exception& e) {
    00102             // recover gracefully from other exceptions thrown request handlers
    00103             PION_LOG_ERROR(m_logger, "HTTP request handler: " << e.what());
    00104             m_server_error_handler(http_request, tcp_conn, e.what());
    00105         }
    00106
    00107     } else {
    00108
    00109         // no web services found that could handle the request
    00110         PION_LOG_INFO(m_logger, "No HTTP request handlers found for resource: "
    00111                       << resource_requested);
    00112         if (http_request->getResource() != http_request->getOriginalResource()) {
    00113             PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
    00114         }
    00115         m_not_found_handler(http_request, tcp_conn);
    00116     }
    00117 }
    00118
    00119 bool HTTPServer::findRequestHandler(const std::string& resource,
    00120                                     RequestHandler& request_handler) const
    00121 {
    00122     // first make sure that HTTP resources are registered
    00123     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
    00124     if (m_resources.empty())
    00125         return false;
    00126
    00127     // iterate through each resource entry that may match the resource
    00128     ResourceMap::const_iterator i = m_resources.upper_bound(resource);
    00129     while (i != m_resources.begin()) {
    00130         --i;
    00131         // check for a match if the first part of the strings match
    00132         if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) {
    00133             // only if the resource matches the plug-in's identifier
    00134             // or if resource is followed first with a '/' character
    00135             if (resource.size() == i->first.size() || resource[i->first.size()]=='/') {
    00136                 request_handler = i->second;
    00137                 return true;
    00138             }
    00139         }
    00140     }
    00141
    00142     return false;
    00143 }
    00144
    00145 void HTTPServer::addResource(const std::string& resource,
    00146                              RequestHandler request_handler)
    00147 {
    00148     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
    00149     const std::string clean_resource(stripTrailingSlash(resource));
    00150     m_resources.insert(std::make_pair(clean_resource, request_handler));
    00151     PION_LOG_INFO(m_logger, "Added request handler for HTTP resource: " << clean_resource);
    00152 }
    00153
    00154 void HTTPServer::removeResource(const std::string& resource)
    00155 {
    00156     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
    00157     const std::string clean_resource(stripTrailingSlash(resource));
    00158     m_resources.erase(clean_resource);
    00159     PION_LOG_INFO(m_logger, "Removed request handler for HTTP resource: " << clean_resource);
    00160 }
    00161
    00162 void HTTPServer::addRedirect(const std::string& requested_resource,
    00163                              const std::string& new_resource)
    00164 {
    00165     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
    00166     const std::string clean_requested_resource(stripTrailingSlash(requested_resource));
    00167     const std::string clean_new_resource(stripTrailingSlash(new_resource));
    00168     m_redirects.insert(std::make_pair(clean_requested_resource, clean_new_resource));
    00169     PION_LOG_INFO(m_logger, "Added redirection for HTTP resource " << clean_requested_resource << " to resource " << clean_new_resource);
    00170 }
    00171
    00172 void HTTPServer::handleBadRequest(HTTPRequestPtr& http_request,
    00173                                   TCPConnectionPtr& tcp_conn)
    00174 {
    00175     static const std::string BAD_REQUEST_HTML =
    00176         "<html><head>\n"
    00177         "<title>400 Bad Request</title>\n"
    00178         "</head><body>\n"
    00179         "<h1>Bad Request</h1>\n"
    00180         "<p>Your browser sent a request that this server could not understand.</p>\n"
    00181         "</body></html>\n";
    00182     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
    00183                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
    00184     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_BAD_REQUEST);
    00185     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_BAD_REQUEST);
    00186     writer->writeNoCopy(BAD_REQUEST_HTML);
    00187     writer->send();
    00188 }
    00189
    00190 void HTTPServer::handleNotFoundRequest(HTTPRequestPtr& http_request,
    00191                                        TCPConnectionPtr& tcp_conn)
    00192 {
    00193     static const std::string NOT_FOUND_HTML_START =
    00194         "<html><head>\n"
    00195         "<title>404 Not Found</title>\n"
    00196         "</head><body>\n"
    00197         "<h1>Not Found</h1>\n"
    00198         "<p>The requested URL ";
    00199     static const std::string NOT_FOUND_HTML_FINISH =
    00200         " was not found on this server.</p>\n"
    00201         "</body></html>\n";
    00202     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
    00203                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
    00204     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
    00205     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
    00206     writer->writeNoCopy(NOT_FOUND_HTML_START);
    00207     writer << http_request->getResource();
    00208     writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
    00209     writer->send();
    00210 }
    00211
    00212 void HTTPServer::handleServerError(HTTPRequestPtr& http_request,
    00213                                    TCPConnectionPtr& tcp_conn,
    00214                                    const std::string& error_msg)
    00215 {
    00216     static const std::string SERVER_ERROR_HTML_START =
    00217         "<html><head>\n"
    00218         "<title>500 Server Error</title>\n"
    00219         "</head><body>\n"
    00220         "<h1>Internal Server Error</h1>\n"
    00221         "<p>The server encountered an internal error: <strong>";
    00222     static const std::string SERVER_ERROR_HTML_FINISH =
    00223         "</strong></p>\n"
    00224         "</body></html>\n";
    00225     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
    00226                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
    00227     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
    00228     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
    00229     writer->writeNoCopy(SERVER_ERROR_HTML_START);
    00230     writer << error_msg;
    00231     writer->writeNoCopy(SERVER_ERROR_HTML_FINISH);
    00232     writer->send();
    00233 }
    00234
    00235 void HTTPServer::handleForbiddenRequest(HTTPRequestPtr& http_request,
    00236                                         TCPConnectionPtr& tcp_conn,
    00237                                         const std::string& error_msg)
    00238 {
    00239     static const std::string FORBIDDEN_HTML_START =
    00240         "<html><head>\n"
    00241         "<title>403 Forbidden</title>\n"
    00242         "</head><body>\n"
    00243         "<h1>Forbidden</h1>\n"
    00244         "<p>User not authorized to access the requested URL ";
    00245     static const std::string FORBIDDEN_HTML_MIDDLE =
    00246         "</p><p><strong>\n";
    00247     static const std::string FORBIDDEN_HTML_FINISH =
    00248         "</strong></p>\n"
    00249         "</body></html>\n";
    00250     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
    00251                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
    00252     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
    00253     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
    00254     writer->writeNoCopy(FORBIDDEN_HTML_START);
    00255     writer << http_request->getResource();
    00256     writer->writeNoCopy(FORBIDDEN_HTML_MIDDLE);
    00257     writer << error_msg;
    00258     writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
    00259     writer->send();
    00260 }
    00261
    00262 void HTTPServer::handleMethodNotAllowed(HTTPRequestPtr& http_request,
    00263                                         TCPConnectionPtr& tcp_conn,
    00264                                         const std::string& allowed_methods)
    00265 {
    00266     static const std::string NOT_ALLOWED_HTML_START =
    00267         "<html><head>\n"
    00268         "<title>405 Method Not Allowed</title>\n"
    00269         "</head><body>\n"
    00270         "<h1>Not Allowed</h1>\n"
    00271         "<p>The requested method ";
    00272     static const std::string NOT_ALLOWED_HTML_FINISH =
    00273         " is not allowed on this server.</p>\n"
    00274         "</body></html>\n";
    00275     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
    00276                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
    00277     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
    00278     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
    00279     if (! allowed_methods.empty())
    00280         writer->getResponse().addHeader("Allow", allowed_methods);
    00281     writer->writeNoCopy(NOT_ALLOWED_HTML_START);
    00282     writer << http_request->getMethod();
    00283     writer->writeNoCopy(NOT_ALLOWED_HTML_FINISH);
    00284     writer->send();
    00285 }
    00286
    00287 }   // end namespace net
    00288 }   // end namespace pion
    00289