00001
00002
00003
00004
00005
00006
00007
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 {
00017 namespace net {
00018
00019
00020
00021
00022 const unsigned int HTTPServer::MAX_REDIRECTS = 10;
00023
00024
00025
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);
00041 if (tcp_conn->is_open() && (&ec.category() == &HTTPParser::getErrorCategory())) {
00042
00043 PION_LOG_INFO(m_logger, "Invalid HTTP request (" << ec.message() << ")");
00044 m_bad_request_handler(http_request, tcp_conn);
00045 } else {
00046
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
00056 std::string resource_requested(stripTrailingSlash(http_request->getResource()));
00057
00058
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
00073 if (m_auth) {
00074
00075 if (! m_auth->handleRequest(http_request, tcp_conn)) {
00076
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
00087 RequestHandler request_handler;
00088 if (findRequestHandler(resource_requested, request_handler)) {
00089
00090
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
00100 throw;
00101 } catch (std::exception& e) {
00102
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
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
00123 boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00124 if (m_resources.empty())
00125 return false;
00126
00127
00128 ResourceMap::const_iterator i = m_resources.upper_bound(resource);
00129 while (i != m_resources.begin()) {
00130 --i;
00131
00132 if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) {
00133
00134
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 }
00288 }
00289