pion-net  4.0.9
HTTPServer.cpp
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <pion/net/HTTPServer.hpp>
11 #include <pion/net/HTTPRequest.hpp>
12 #include <pion/net/HTTPRequestReader.hpp>
13 #include <pion/net/HTTPResponseWriter.hpp>
14 
15 
16 namespace pion { // begin namespace pion
17 namespace net { // begin namespace net (Pion Network Library)
18 
19 
20 // static members of HTTPServer
21 
22 const unsigned int HTTPServer::MAX_REDIRECTS = 10;
23 
24 
25 // HTTPServer member functions
26 
27 void HTTPServer::handleConnection(TCPConnectionPtr& tcp_conn)
28 {
29  HTTPRequestReaderPtr reader_ptr;
30  reader_ptr = HTTPRequestReader::create(tcp_conn, boost::bind(&HTTPServer::handleRequest,
31  this, _1, _2, _3));
32  reader_ptr->setMaxContentLength(m_max_content_length);
33  reader_ptr->receive();
34 }
35 
36 void HTTPServer::handleRequest(HTTPRequestPtr& http_request,
37  TCPConnectionPtr& tcp_conn, const boost::system::error_code& ec)
38 {
39  if (ec || ! http_request->isValid()) {
40  tcp_conn->setLifecycle(TCPConnection::LIFECYCLE_CLOSE); // make sure it will get closed
41  if (tcp_conn->is_open() && (&ec.category() == &HTTPParser::getErrorCategory())) {
42  // HTTP parser error
43  PION_LOG_INFO(m_logger, "Invalid HTTP request (" << ec.message() << ")");
44  m_bad_request_handler(http_request, tcp_conn);
45  } else {
46  // other (IO) error
47  PION_LOG_INFO(m_logger, "Lost connection on port " << getPort());
48  tcp_conn->finish();
49  }
50  return;
51  }
52 
53  PION_LOG_DEBUG(m_logger, "Received a valid HTTP request");
54 
55  // strip off trailing slash if the request has one
56  std::string resource_requested(stripTrailingSlash(http_request->getResource()));
57 
58  // apply any redirection
59  RedirectMap::const_iterator it = m_redirects.find(resource_requested);
60  unsigned int num_redirects = 0;
61  while (it != m_redirects.end()) {
62  if (++num_redirects > MAX_REDIRECTS) {
63  PION_LOG_ERROR(m_logger, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource: " << http_request->getOriginalResource());
64  m_server_error_handler(http_request, tcp_conn, "Maximum number of redirects (HTTPServer::MAX_REDIRECTS) exceeded for requested resource");
65  return;
66  }
67  resource_requested = it->second;
68  http_request->changeResource(resource_requested);
69  it = m_redirects.find(resource_requested);
70  }
71 
72  // if authentication activated, check current request
73  if (m_auth) {
74  // try to verify authentication
75  if (! m_auth->handleRequest(http_request, tcp_conn)) {
76  // the HTTP 401 message has already been sent by the authentication object
77  PION_LOG_DEBUG(m_logger, "Authentication required for HTTP resource: "
78  << resource_requested);
79  if (http_request->getResource() != http_request->getOriginalResource()) {
80  PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
81  }
82  return;
83  }
84  }
85 
86  // search for a handler matching the resource requested
87  RequestHandler request_handler;
88  if (findRequestHandler(resource_requested, request_handler)) {
89 
90  // try to handle the request
91  try {
92  request_handler(http_request, tcp_conn);
93  PION_LOG_DEBUG(m_logger, "Found request handler for HTTP resource: "
94  << resource_requested);
95  if (http_request->getResource() != http_request->getOriginalResource()) {
96  PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
97  }
98  } catch (std::bad_alloc&) {
99  // propagate memory errors (FATAL)
100  throw;
101  } catch (std::exception& e) {
102  // recover gracefully from other exceptions thrown request handlers
103  PION_LOG_ERROR(m_logger, "HTTP request handler: " << e.what());
104  m_server_error_handler(http_request, tcp_conn, e.what());
105  }
106 
107  } else {
108 
109  // no web services found that could handle the request
110  PION_LOG_INFO(m_logger, "No HTTP request handlers found for resource: "
111  << resource_requested);
112  if (http_request->getResource() != http_request->getOriginalResource()) {
113  PION_LOG_DEBUG(m_logger, "Original resource requested was: " << http_request->getOriginalResource());
114  }
115  m_not_found_handler(http_request, tcp_conn);
116  }
117 }
118 
119 bool HTTPServer::findRequestHandler(const std::string& resource,
120  RequestHandler& request_handler) const
121 {
122  // first make sure that HTTP resources are registered
123  boost::mutex::scoped_lock resource_lock(m_resource_mutex);
124  if (m_resources.empty())
125  return false;
126 
127  // iterate through each resource entry that may match the resource
128  ResourceMap::const_iterator i = m_resources.upper_bound(resource);
129  while (i != m_resources.begin()) {
130  --i;
131  // check for a match if the first part of the strings match
132  if (i->first.empty() || resource.compare(0, i->first.size(), i->first) == 0) {
133  // only if the resource matches the plug-in's identifier
134  // or if resource is followed first with a '/' character
135  if (resource.size() == i->first.size() || resource[i->first.size()]=='/') {
136  request_handler = i->second;
137  return true;
138  }
139  }
140  }
141 
142  return false;
143 }
144 
145 void HTTPServer::addResource(const std::string& resource,
146  RequestHandler request_handler)
147 {
148  boost::mutex::scoped_lock resource_lock(m_resource_mutex);
149  const std::string clean_resource(stripTrailingSlash(resource));
150  m_resources.insert(std::make_pair(clean_resource, request_handler));
151  PION_LOG_INFO(m_logger, "Added request handler for HTTP resource: " << clean_resource);
152 }
153 
154 void HTTPServer::removeResource(const std::string& resource)
155 {
156  boost::mutex::scoped_lock resource_lock(m_resource_mutex);
157  const std::string clean_resource(stripTrailingSlash(resource));
158  m_resources.erase(clean_resource);
159  PION_LOG_INFO(m_logger, "Removed request handler for HTTP resource: " << clean_resource);
160 }
161 
162 void HTTPServer::addRedirect(const std::string& requested_resource,
163  const std::string& new_resource)
164 {
165  boost::mutex::scoped_lock resource_lock(m_resource_mutex);
166  const std::string clean_requested_resource(stripTrailingSlash(requested_resource));
167  const std::string clean_new_resource(stripTrailingSlash(new_resource));
168  m_redirects.insert(std::make_pair(clean_requested_resource, clean_new_resource));
169  PION_LOG_INFO(m_logger, "Added redirection for HTTP resource " << clean_requested_resource << " to resource " << clean_new_resource);
170 }
171 
172 void HTTPServer::handleBadRequest(HTTPRequestPtr& http_request,
173  TCPConnectionPtr& tcp_conn)
174 {
175  static const std::string BAD_REQUEST_HTML =
176  "<html><head>\n"
177  "<title>400 Bad Request</title>\n"
178  "</head><body>\n"
179  "<h1>Bad Request</h1>\n"
180  "<p>Your browser sent a request that this server could not understand.</p>\n"
181  "</body></html>\n";
182  HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
183  boost::bind(&TCPConnection::finish, tcp_conn)));
184  writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_BAD_REQUEST);
185  writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_BAD_REQUEST);
186  writer->writeNoCopy(BAD_REQUEST_HTML);
187  writer->send();
188 }
189 
190 void HTTPServer::handleNotFoundRequest(HTTPRequestPtr& http_request,
191  TCPConnectionPtr& tcp_conn)
192 {
193  static const std::string NOT_FOUND_HTML_START =
194  "<html><head>\n"
195  "<title>404 Not Found</title>\n"
196  "</head><body>\n"
197  "<h1>Not Found</h1>\n"
198  "<p>The requested URL ";
199  static const std::string NOT_FOUND_HTML_FINISH =
200  " was not found on this server.</p>\n"
201  "</body></html>\n";
202  HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
203  boost::bind(&TCPConnection::finish, tcp_conn)));
204  writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_NOT_FOUND);
205  writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_NOT_FOUND);
206  writer->writeNoCopy(NOT_FOUND_HTML_START);
207  writer << http_request->getResource();
208  writer->writeNoCopy(NOT_FOUND_HTML_FINISH);
209  writer->send();
210 }
211 
212 void HTTPServer::handleServerError(HTTPRequestPtr& http_request,
213  TCPConnectionPtr& tcp_conn,
214  const std::string& error_msg)
215 {
216  static const std::string SERVER_ERROR_HTML_START =
217  "<html><head>\n"
218  "<title>500 Server Error</title>\n"
219  "</head><body>\n"
220  "<h1>Internal Server Error</h1>\n"
221  "<p>The server encountered an internal error: <strong>";
222  static const std::string SERVER_ERROR_HTML_FINISH =
223  "</strong></p>\n"
224  "</body></html>\n";
225  HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
226  boost::bind(&TCPConnection::finish, tcp_conn)));
227  writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_SERVER_ERROR);
228  writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_SERVER_ERROR);
229  writer->writeNoCopy(SERVER_ERROR_HTML_START);
230  writer << error_msg;
231  writer->writeNoCopy(SERVER_ERROR_HTML_FINISH);
232  writer->send();
233 }
234 
235 void HTTPServer::handleForbiddenRequest(HTTPRequestPtr& http_request,
236  TCPConnectionPtr& tcp_conn,
237  const std::string& error_msg)
238 {
239  static const std::string FORBIDDEN_HTML_START =
240  "<html><head>\n"
241  "<title>403 Forbidden</title>\n"
242  "</head><body>\n"
243  "<h1>Forbidden</h1>\n"
244  "<p>User not authorized to access the requested URL ";
245  static const std::string FORBIDDEN_HTML_MIDDLE =
246  "</p><p><strong>\n";
247  static const std::string FORBIDDEN_HTML_FINISH =
248  "</strong></p>\n"
249  "</body></html>\n";
250  HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
251  boost::bind(&TCPConnection::finish, tcp_conn)));
252  writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_FORBIDDEN);
253  writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_FORBIDDEN);
254  writer->writeNoCopy(FORBIDDEN_HTML_START);
255  writer << http_request->getResource();
256  writer->writeNoCopy(FORBIDDEN_HTML_MIDDLE);
257  writer << error_msg;
258  writer->writeNoCopy(FORBIDDEN_HTML_FINISH);
259  writer->send();
260 }
261 
262 void HTTPServer::handleMethodNotAllowed(HTTPRequestPtr& http_request,
263  TCPConnectionPtr& tcp_conn,
264  const std::string& allowed_methods)
265 {
266  static const std::string NOT_ALLOWED_HTML_START =
267  "<html><head>\n"
268  "<title>405 Method Not Allowed</title>\n"
269  "</head><body>\n"
270  "<h1>Not Allowed</h1>\n"
271  "<p>The requested method ";
272  static const std::string NOT_ALLOWED_HTML_FINISH =
273  " is not allowed on this server.</p>\n"
274  "</body></html>\n";
275  HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
276  boost::bind(&TCPConnection::finish, tcp_conn)));
277  writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
278  writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);
279  if (! allowed_methods.empty())
280  writer->getResponse().addHeader("Allow", allowed_methods);
281  writer->writeNoCopy(NOT_ALLOWED_HTML_START);
282  writer << http_request->getMethod();
283  writer->writeNoCopy(NOT_ALLOWED_HTML_FINISH);
284  writer->send();
285 }
286 
287 } // end namespace net
288 } // end namespace pion
289 
virtual void handleConnection(TCPConnectionPtr &tcp_conn)
Definition: HTTPServer.cpp:27
void addRedirect(const std::string &requested_resource, const std::string &new_resource)
Definition: HTTPServer.cpp:162
static void handleBadRequest(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn)
Definition: HTTPServer.cpp:172
static std::string stripTrailingSlash(const std::string &str)
Definition: HTTPServer.hpp:159
static void handleServerError(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn, const std::string &error_msg)
Definition: HTTPServer.cpp:212
boost::function2< void, HTTPRequestPtr &, TCPConnectionPtr & > RequestHandler
type of function that is used to handle requests
Definition: HTTPServer.hpp:42
static boost::shared_ptr< HTTPRequestReader > create(TCPConnectionPtr &tcp_conn, FinishedHandler handler)
void addResource(const std::string &resource, RequestHandler request_handler)
Definition: HTTPServer.cpp:145
PionLogger m_logger
primary logging interface used by this class
Definition: TCPServer.hpp:153
static void handleForbiddenRequest(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn, const std::string &error_msg)
Definition: HTTPServer.cpp:235
static void handleMethodNotAllowed(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn, const std::string &allowed_methods="")
Definition: HTTPServer.cpp:262
unsigned int getPort(void) const
returns tcp port number that the server listens for connections on
Definition: TCPServer.hpp:63
virtual bool findRequestHandler(const std::string &resource, RequestHandler &request_handler) const
Definition: HTTPServer.cpp:119
static ErrorCategory & getErrorCategory(void)
returns an instance of HTTPParser::ErrorCategory
Definition: HTTPParser.hpp:356
static void handleNotFoundRequest(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn)
Definition: HTTPServer.cpp:190
void removeResource(const std::string &resource)
Definition: HTTPServer.cpp:154
static boost::shared_ptr< HTTPResponseWriter > create(TCPConnectionPtr &tcp_conn, HTTPResponsePtr &http_response, FinishedHandler handler=FinishedHandler())
virtual void handleRequest(HTTPRequestPtr &http_request, TCPConnectionPtr &tcp_conn, const boost::system::error_code &ec)
Definition: HTTPServer.cpp:36