00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <boost/exception/diagnostic_information.hpp>
00011 #include <pion/http/server.hpp>
00012 #include <pion/http/request.hpp>
00013 #include <pion/http/request_reader.hpp>
00014 #include <pion/http/response_writer.hpp>
00015
00016
00017 namespace pion {
00018 namespace http {
00019
00020
00021
00022
00023 const unsigned int server::MAX_REDIRECTS = 10;
00024
00025
00026
00027
00028 void server::handle_connection(tcp::connection_ptr& tcp_conn)
00029 {
00030 request_reader_ptr my_reader_ptr;
00031 my_reader_ptr = request_reader::create(tcp_conn, boost::bind(&server::handle_request,
00032 this, _1, _2, _3));
00033 my_reader_ptr->set_max_content_length(m_max_content_length);
00034 my_reader_ptr->receive();
00035 }
00036
00037 void server::handle_request(http::request_ptr& http_request_ptr,
00038 tcp::connection_ptr& tcp_conn, const boost::system::error_code& ec)
00039 {
00040 if (ec || ! http_request_ptr->is_valid()) {
00041 tcp_conn->set_lifecycle(tcp::connection::LIFECYCLE_CLOSE);
00042 if (tcp_conn->is_open() && (ec.category() == http::parser::get_error_category())) {
00043
00044 PION_LOG_INFO(m_logger, "Invalid HTTP request (" << ec.message() << ")");
00045 m_bad_request_handler(http_request_ptr, tcp_conn);
00046 } else {
00047 static const boost::system::error_condition
00048 ERRCOND_CANCELED(boost::system::errc::operation_canceled, boost::system::system_category()),
00049 ERRCOND_EOF(boost::asio::error::eof, boost::asio::error::misc_category);
00050
00051 if (ec == ERRCOND_CANCELED || ec == ERRCOND_EOF) {
00052
00053 PION_LOG_DEBUG(m_logger, "Lost connection on port " << get_port() << " (" << ec.message() << ")");
00054 } else {
00055 PION_LOG_INFO(m_logger, "Lost connection on port " << get_port() << " (" << ec.message() << ")");
00056 }
00057
00058 tcp_conn->finish();
00059 }
00060 return;
00061 }
00062
00063 PION_LOG_DEBUG(m_logger, "Received a valid HTTP request");
00064
00065
00066 std::string resource_requested(strip_trailing_slash(http_request_ptr->get_resource()));
00067
00068
00069 redirect_map_t::const_iterator it = m_redirects.find(resource_requested);
00070 unsigned int num_redirects = 0;
00071 while (it != m_redirects.end()) {
00072 if (++num_redirects > MAX_REDIRECTS) {
00073 PION_LOG_ERROR(m_logger, "Maximum number of redirects (server::MAX_REDIRECTS) exceeded for requested resource: " << http_request_ptr->get_original_resource());
00074 m_server_error_handler(http_request_ptr, tcp_conn, "Maximum number of redirects (server::MAX_REDIRECTS) exceeded for requested resource");
00075 return;
00076 }
00077 resource_requested = it->second;
00078 http_request_ptr->change_resource(resource_requested);
00079 it = m_redirects.find(resource_requested);
00080 }
00081
00082
00083 if (m_auth_ptr) {
00084
00085 if (! m_auth_ptr->handle_request(http_request_ptr, tcp_conn)) {
00086
00087 PION_LOG_DEBUG(m_logger, "Authentication required for HTTP resource: "
00088 << resource_requested);
00089 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) {
00090 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource());
00091 }
00092 return;
00093 }
00094 }
00095
00096
00097 request_handler_t request_handler;
00098 if (find_request_handler(resource_requested, request_handler)) {
00099
00100
00101 try {
00102 request_handler(http_request_ptr, tcp_conn);
00103 PION_LOG_DEBUG(m_logger, "Found request handler for HTTP resource: "
00104 << resource_requested);
00105 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) {
00106 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource());
00107 }
00108 } catch (std::bad_alloc&) {
00109
00110 throw;
00111 } catch (std::exception& e) {
00112
00113 PION_LOG_ERROR(m_logger, "HTTP request handler: " << boost::diagnostic_information(e));
00114 m_server_error_handler(http_request_ptr, tcp_conn, boost::diagnostic_information(e));
00115 }
00116
00117 } else {
00118
00119
00120 PION_LOG_INFO(m_logger, "No HTTP request handlers found for resource: "
00121 << resource_requested);
00122 if (http_request_ptr->get_resource() != http_request_ptr->get_original_resource()) {
00123 PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request_ptr->get_original_resource());
00124 }
00125 m_not_found_handler(http_request_ptr, tcp_conn);
00126 }
00127 }
00128
00129 bool server::find_request_handler(const std::string& resource,
00130 request_handler_t& request_handler) const
00131 {
00132
00133 boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00134 if (m_resources.empty())
00135 return false;
00136
00137
00138 resource_map_t::const_iterator i = m_resources.upper_bound(resource);
00139 while (i != m_resources.begin()) {
00140 --i;
00141
00142 if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) {
00143
00144
00145 if (resource.size() == i->first.size() || resource[i->first.size()]=='/') {
00146 request_handler = i->second;
00147 return true;
00148 }
00149 }
00150 }
00151
00152 return false;
00153 }
00154
00155 void server::add_resource(const std::string& resource,
00156 request_handler_t request_handler)
00157 {
00158 boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00159 const std::string clean_resource(strip_trailing_slash(resource));
00160 m_resources.insert(std::make_pair(clean_resource, request_handler));
00161 PION_LOG_INFO(m_logger, "Added request handler for HTTP resource: " << clean_resource);
00162 }
00163
00164 void server::remove_resource(const std::string& resource)
00165 {
00166 boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00167 const std::string clean_resource(strip_trailing_slash(resource));
00168 m_resources.erase(clean_resource);
00169 PION_LOG_INFO(m_logger, "Removed request handler for HTTP resource: " << clean_resource);
00170 }
00171
00172 void server::add_redirect(const std::string& requested_resource,
00173 const std::string& new_resource)
00174 {
00175 boost::mutex::scoped_lock resource_lock(m_resource_mutex);
00176 const std::string clean_requested_resource(strip_trailing_slash(requested_resource));
00177 const std::string clean_new_resource(strip_trailing_slash(new_resource));
00178 m_redirects.insert(std::make_pair(clean_requested_resource, clean_new_resource));
00179 PION_LOG_INFO(m_logger, "Added redirection for HTTP resource " << clean_requested_resource << " to resource " << clean_new_resource);
00180 }
00181
00182 void server::handle_bad_request(http::request_ptr& http_request_ptr,
00183 tcp::connection_ptr& tcp_conn)
00184 {
00185 static const std::string BAD_REQUEST_HTML =
00186 "<html><head>\n"
00187 "<title>400 Bad Request</title>\n"
00188 "</head><body>\n"
00189 "<h1>Bad Request</h1>\n"
00190 "<p>Your browser sent a request that this server could not understand.</p>\n"
00191 "</body></html>\n";
00192 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00193 boost::bind(&tcp::connection::finish, tcp_conn)));
00194 writer->get_response().set_status_code(http::types::RESPONSE_CODE_BAD_REQUEST);
00195 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_BAD_REQUEST);
00196 writer->write_no_copy(BAD_REQUEST_HTML);
00197 writer->send();
00198 }
00199
00200 void server::handle_not_found_request(http::request_ptr& http_request_ptr,
00201 tcp::connection_ptr& tcp_conn)
00202 {
00203 static const std::string NOT_FOUND_HTML_START =
00204 "<html><head>\n"
00205 "<title>404 Not Found</title>\n"
00206 "</head><body>\n"
00207 "<h1>Not Found</h1>\n"
00208 "<p>The requested URL ";
00209 static const std::string NOT_FOUND_HTML_FINISH =
00210 " was not found on this server.</p>\n"
00211 "</body></html>\n";
00212 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00213 boost::bind(&tcp::connection::finish, tcp_conn)));
00214 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NOT_FOUND);
00215 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND);
00216 writer->write_no_copy(NOT_FOUND_HTML_START);
00217 writer << http_request_ptr->get_resource();
00218 writer->write_no_copy(NOT_FOUND_HTML_FINISH);
00219 writer->send();
00220 }
00221
00222 void server::handle_server_error(http::request_ptr& http_request_ptr,
00223 tcp::connection_ptr& tcp_conn,
00224 const std::string& error_msg)
00225 {
00226 static const std::string SERVER_ERROR_HTML_START =
00227 "<html><head>\n"
00228 "<title>500 Server Error</title>\n"
00229 "</head><body>\n"
00230 "<h1>Internal Server Error</h1>\n"
00231 "<p>The server encountered an internal error: <strong>";
00232 static const std::string SERVER_ERROR_HTML_FINISH =
00233 "</strong></p>\n"
00234 "</body></html>\n";
00235 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00236 boost::bind(&tcp::connection::finish, tcp_conn)));
00237 writer->get_response().set_status_code(http::types::RESPONSE_CODE_SERVER_ERROR);
00238 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_SERVER_ERROR);
00239 writer->write_no_copy(SERVER_ERROR_HTML_START);
00240 writer << error_msg;
00241 writer->write_no_copy(SERVER_ERROR_HTML_FINISH);
00242 writer->send();
00243 }
00244
00245 void server::handle_forbidden_request(http::request_ptr& http_request_ptr,
00246 tcp::connection_ptr& tcp_conn,
00247 const std::string& error_msg)
00248 {
00249 static const std::string FORBIDDEN_HTML_START =
00250 "<html><head>\n"
00251 "<title>403 Forbidden</title>\n"
00252 "</head><body>\n"
00253 "<h1>Forbidden</h1>\n"
00254 "<p>User not authorized to access the requested URL ";
00255 static const std::string FORBIDDEN_HTML_MIDDLE =
00256 "</p><p><strong>\n";
00257 static const std::string FORBIDDEN_HTML_FINISH =
00258 "</strong></p>\n"
00259 "</body></html>\n";
00260 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00261 boost::bind(&tcp::connection::finish, tcp_conn)));
00262 writer->get_response().set_status_code(http::types::RESPONSE_CODE_FORBIDDEN);
00263 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_FORBIDDEN);
00264 writer->write_no_copy(FORBIDDEN_HTML_START);
00265 writer << http_request_ptr->get_resource();
00266 writer->write_no_copy(FORBIDDEN_HTML_MIDDLE);
00267 writer << error_msg;
00268 writer->write_no_copy(FORBIDDEN_HTML_FINISH);
00269 writer->send();
00270 }
00271
00272 void server::handle_method_not_allowed(http::request_ptr& http_request_ptr,
00273 tcp::connection_ptr& tcp_conn,
00274 const std::string& allowed_methods)
00275 {
00276 static const std::string NOT_ALLOWED_HTML_START =
00277 "<html><head>\n"
00278 "<title>405 Method Not Allowed</title>\n"
00279 "</head><body>\n"
00280 "<h1>Not Allowed</h1>\n"
00281 "<p>The requested method ";
00282 static const std::string NOT_ALLOWED_HTML_FINISH =
00283 " is not allowed on this server.</p>\n"
00284 "</body></html>\n";
00285 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00286 boost::bind(&tcp::connection::finish, tcp_conn)));
00287 writer->get_response().set_status_code(http::types::RESPONSE_CODE_METHOD_NOT_ALLOWED);
00288 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
00289 if (! allowed_methods.empty())
00290 writer->get_response().add_header("Allow", allowed_methods);
00291 writer->write_no_copy(NOT_ALLOWED_HTML_START);
00292 writer << http_request_ptr->get_method();
00293 writer->write_no_copy(NOT_ALLOWED_HTML_FINISH);
00294 writer->send();
00295 }
00296
00297 }
00298 }