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));
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)
00038 {
00039     if (! http_request->isValid()) {
00040         // the request is invalid or an error occured
00041         PION_LOG_INFO(m_logger, "Received an invalid HTTP request");
00042         m_bad_request_handler(http_request, tcp_conn);
00043         return;
00044     }
00045         
00046     PION_LOG_DEBUG(m_logger, "Received a valid HTTP request");
00047 
00048     // strip off trailing slash if the request has one
00049     std::string resource_requested(stripTrailingSlash(http_request->getResource()));
00050 
00051     // apply any redirection
00052     RedirectMap::const_iterator it = m_redirects.find(resource_requested);
00053     unsigned int num_redirects = 0;
00054     while (it != m_redirects.end()) {
00055         if (++num_redirects > MAX_REDIRECTS) {
00056             PION_LOG_ERROR(m_logger, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource: " << http_request->getOriginalResource());
00057             m_server_error_handler(http_request, tcp_conn, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource");
00058             return;
00059         }
00060         resource_requested = it->second;
00061         http_request->changeResource(resource_requested);
00062         it = m_redirects.find(resource_requested);
00063     }
00064 
00065     // if authentication activated, check current request
00066     if (m_auth) {
00067         // try to verify authentication
00068         if (! m_auth->handleRequest(http_request, tcp_conn)) {
00069             // the HTTP 401 message has already been sent by the authentication object
00070             PION_LOG_DEBUG(m_logger, "Authentication required for HTTP resource: "
00071                 << resource_requested);
00072             if (http_request->getResource() != http_request->getOriginalResource()) {
00073                 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
00074             }
00075             return;
00076         }
00077     }
00078     
00079     // search for a handler matching the resource requested
00080     RequestHandler request_handler;
00081     if (findRequestHandler(resource_requested, request_handler)) {
00082         
00083         // try to handle the request
00084         try {
00085             request_handler(http_request, tcp_conn);
00086             PION_LOG_DEBUG(m_logger, "Found request handler for HTTP resource: "
00087                            << resource_requested);
00088             if (http_request->getResource() != http_request->getOriginalResource()) {
00089                 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
00090             }
00091         } catch (HTTPResponseWriter::LostConnectionException& e) {
00092             // the connection was lost while or before sending the response
00093             PION_LOG_WARN(m_logger, "HTTP request handler: " << e.what());
00094             tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // make sure it will get closed
00095             tcp_conn->finish();
00096         } catch (std::bad_alloc&) {
00097             // propagate memory errors (FATAL)
00098             throw;
00099         } catch (std::exception& e) {
00100             // recover gracefully from other exceptions thrown request handlers
00101             PION_LOG_ERROR(m_logger, "HTTP request handler: " << e.what());
00102             m_server_error_handler(http_request, tcp_conn, e.what());
00103         }
00104         
00105     } else {
00106         
00107         // no web services found that could handle the request
00108         PION_LOG_INFO(m_logger, "No HTTP request handlers found for resource: "
00109                       << resource_requested);
00110         if (http_request->getResource() != http_request->getOriginalResource()) {
00111             PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
00112         }
00113         m_not_found_handler(http_request, tcp_conn);
00114     }
00115 }
00116     
00117 bool HTTPServer::findRequestHandler(const std::string& resource,
00118                                     RequestHandler& request_handler) const
00119 {
00120     // first make sure that HTTP resources are registered
00121     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00122     if (m_resources.empty())
00123         return false;
00124     
00125     // iterate through each resource entry that may match the resource
00126     ResourceMap::const_iterator i = m_resources.upper_bound(resource);
00127     while (i != m_resources.begin()) {
00128         --i;
00129         // check for a match if the first part of the strings match
00130         if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) {
00131             // only if the resource matches the plug-in's identifier
00132             // or if resource is followed first with a '/' character
00133             if (resource.size() == i->first.size() || resource[i->first.size()]=='/') {
00134                 request_handler = i->second;
00135                 return true;
00136             }
00137         }
00138     }
00139     
00140     return false;
00141 }
00142 
00143 void HTTPServer::addResource(const std::string& resource,
00144                              RequestHandler request_handler)
00145 {
00146     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00147     const std::string clean_resource(stripTrailingSlash(resource));
00148     m_resources.insert(std::make_pair(clean_resource, request_handler));
00149     PION_LOG_INFO(m_logger, "Added request handler for HTTP resource: " << clean_resource);
00150 }
00151 
00152 void HTTPServer::removeResource(const std::string& resource)
00153 {
00154     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00155     const std::string clean_resource(stripTrailingSlash(resource));
00156     m_resources.erase(clean_resource);
00157     PION_LOG_INFO(m_logger, "Removed request handler for HTTP resource: " << clean_resource);
00158 }
00159 
00160 void HTTPServer::addRedirect(const std::string& requested_resource,
00161                              const std::string& new_resource)
00162 {
00163     boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00164     const std::string clean_requested_resource(stripTrailingSlash(requested_resource));
00165     const std::string clean_new_resource(stripTrailingSlash(new_resource));
00166     m_redirects.insert(std::make_pair(clean_requested_resource, clean_new_resource));
00167     PION_LOG_INFO(m_logger, "Added redirection for HTTP resource " << clean_requested_resource << " to resource " << clean_new_resource);
00168 }
00169 
00170 void HTTPServer::handleBadRequest(HTTPRequestPtr& http_request,
00171                                   TCPConnectionPtr& tcp_conn)
00172 {
00173     static const std::string BAD_REQUEST_HTML =
00174         "<html><head>\n"
00175         "<title>400 Bad Request</title>\n"
00176         "</head><body>\n"
00177         "<h1>Bad Request</h1>\n"
00178         "<p>Your browser sent a request that this server could not understand.</p>\n"
00179         "</body></html>\n";
00180     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
00181                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
00182     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_BAD_REQUEST);
00183     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_BAD_REQUEST);
00184     writer->writeNoCopy(BAD_REQUEST_HTML);
00185     writer->send();
00186 }
00187 
00188 void HTTPServer::handleNotFoundRequest(HTTPRequestPtr& http_request,
00189                                        TCPConnectionPtr& tcp_conn)
00190 {
00191     static const std::string NOT_FOUND_HTML_START =
00192         "<html><head>\n"
00193         "<title>404 Not Found</title>\n"
00194         "</head><body>\n"
00195         "<h1>Not Found</h1>\n"
00196         "<p>The requested URL ";
00197     static const std::string NOT_FOUND_HTML_FINISH =
00198         " was not found on this server.</p>\n"
00199         "</body></html>\n";
00200     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
00201                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
00202     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
00203     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
00204     writer->writeNoCopy(NOT_FOUND_HTML_START);
00205     writer << http_request->getResource();
00206     writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
00207     writer->send();
00208 }
00209 
00210 void HTTPServer::handleServerError(HTTPRequestPtr& http_request,
00211                                    TCPConnectionPtr& tcp_conn,
00212                                    const std::string& error_msg)
00213 {
00214     static const std::string SERVER_ERROR_HTML_START =
00215         "<html><head>\n"
00216         "<title>500 Server Error</title>\n"
00217         "</head><body>\n"
00218         "<h1>Internal Server Error</h1>\n"
00219         "<p>The server encountered an internal error: <strong>";
00220     static const std::string SERVER_ERROR_HTML_FINISH =
00221         "</strong></p>\n"
00222         "</body></html>\n";
00223     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
00224                                                             boost::bind(&TCPConnection::finish, tcp_conn)));
00225     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
00226     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
00227     writer->writeNoCopy(SERVER_ERROR_HTML_START);
00228     writer << error_msg;
00229     writer->writeNoCopy(SERVER_ERROR_HTML_FINISH);
00230     writer->send();
00231 }
00232 
00233 }   // end namespace net
00234 }   // end namespace pion
00235 

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7