pion-net
4.0.9
|
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