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