alaCarte Maps
Renderer for OpenStreetMap tiles
http_request.cpp
Go to the documentation of this file.
1 
22 #include <boost/lexical_cast.hpp>
23 
24 #include "server/http_request.hpp"
25 #include "server/tile.hpp"
28 #include "server/http_server.hpp"
29 #include "server/cache.hpp"
30 
31 HttpRequest::HttpRequest ( boost::asio::io_service &ioService, const shared_ptr<HttpServer>& server, const shared_ptr<RequestManager> &manager )
32  : socket ( ioService )
33  , server ( server )
34  , manager ( manager )
35  , answered (false)
36 {
37 
38 };
39 
41 {
42  readSome();
43 }
44 
46 {
47  socket.close();
48 }
50 {
51  socket.async_read_some ( boost::asio::buffer ( buffer ),
52  boost::bind(&HttpRequest::handleRead, shared_from_this(),
53  boost::asio::placeholders::error,
54  boost::asio::placeholders::bytes_transferred
55  )
56  );
57 }
58 void HttpRequest::handleRead ( const boost::system::error_code &e, std::size_t bytes_transferred )
59 {
60  shared_ptr<RequestManager> manager = this->manager.lock();
61  shared_ptr<HttpServer> server = this->server.lock();
62  assert(manager && server);
63 
64  if ( !e ) {
65  boost::tribool result;
66  boost::tie ( result, boost::tuples::ignore ) = parser.parse ( shared_from_this(), buffer.data(), buffer.data() + bytes_transferred );
67 
68  if ( result ) {
69  manager->enqueue ( shared_from_this() );
70  } else if ( !result ) {
71  if (checkifAnswered()) return;
73  } else {
74  readSome();
75  }
76  } else if ( e != boost::asio::error::operation_aborted ) {
77  server->stopRequest ( shared_from_this() );
78  }
79 
80 }
81 
82 void HttpRequest::handleWrite ( const boost::system::error_code &e )
83 {
84  shared_ptr<HttpServer> server = this->server.lock();
85  assert(server);
86 
87  if ( !e ) {
88  // Initiate graceful connection closure.
89  boost::system::error_code ignored_ec;
90  socket.shutdown ( boost::asio::ip::tcp::socket::shutdown_both, ignored_ec );
91  }
92 
93  if ( e != boost::asio::error::operation_aborted ) {
94  server->stopRequest ( shared_from_this() );
95  }
96 }
97 
98 const string &HttpRequest::getURL() const
99 {
100  return data.uri;
101 }
102 
103 boost::asio::ip::tcp::socket &HttpRequest::getSocket()
104 {
105  return socket;
106 }
107 
109 {
110  if (answered) {
111  LOG_SEV(server_log, error) << "Tried to answer an already answered HttpRequest: " << getURL();
112  }
113  return answered;
114 }
115 
117 {
118  answered = true;
119  boost::asio::async_write ( socket, reply.toBuffers(),
120  boost::bind ( &HttpRequest::handleWrite, shared_from_this(),
121  boost::asio::placeholders::error ) );
122 }
123 
125 {
126  shared_ptr<RequestManager> manager = this->manager.lock();
127  assert(manager);
128 
129  answer(manager->getCache()->getDefaultTile(), status);
130 }
131 
132 void HttpRequest::answer ( const shared_ptr<Tile>& tile, Reply::StatusType status )
133 {
134  if (checkifAnswered()) return;
135  reply.status = status;
136  reply.content = "";
137  reply.tile = tile;
138  reply.headers.resize ( 2 );
139  reply.headers[0].name = "Content-Length";
140  reply.headers[0].value = boost::lexical_cast<string> ( tile->getImage()->size() );
141  reply.headers[1].name = "Content-Type";
142  reply.headers[1].value = "image/";
143  reply.headers[1].value.append ( tile->getIdentifier()->getImageFormatString() );
144 
145  // IP Date Method url Version Reply Size duration
146  //80.101.90.180 - [02/Jun/2009:15:11:52 -0400] "GET /css/style.css HTTP/1.1" 200 2816 12
147  auto now = boost::posix_time::second_clock::local_time();
148  LOG_SEV(access_log, info) << socket.remote_endpoint().address().to_string()
149  << " - ["
150  << now.date().day() << "/" << now.date().month() << "/" << now.date().year()
151  << ":" << now.time_of_day().hours() << ":" << now.time_of_day().minutes() << ":" << now.time_of_day().seconds()
152  << "] \""
153  << data.method << " " << data.uri << " HTTP/" << data.http_version_major << "." << data.http_version_minor << "\" "
154  << reply.status << " " << reply.headers[0].value;
155 
156  LOG_SEV(server_log, info) << "Answered \"" << data.uri << "\"";
157  answer();
158 }
159 
160 namespace status_strings
161 {
162 
164  "HTTP/1.0 200 OK\r\n";
166  "HTTP/1.0 201 Created\r\n";
168  "HTTP/1.0 202 Accepted\r\n";
170  "HTTP/1.0 204 No Content\r\n";
172  "HTTP/1.0 300 Multiple Choices\r\n";
174  "HTTP/1.0 301 Moved Permanently\r\n";
176  "HTTP/1.0 302 Moved Temporarily\r\n";
178  "HTTP/1.0 304 Not Modified\r\n";
180  "HTTP/1.0 400 Bad Request\r\n";
182  "HTTP/1.0 401 Unauthorized\r\n";
184  "HTTP/1.0 403 Forbidden\r\n";
186  "HTTP/1.0 404 Not Found\r\n";
188  "HTTP/1.0 500 Internal Server Error\r\n";
190  "HTTP/1.0 501 Not Implemented\r\n";
192  "HTTP/1.0 502 Bad Gateway\r\n";
194  "HTTP/1.0 503 Service Unavailable\r\n";
195 
196 boost::asio::const_buffer to_buffer ( HttpRequest::Reply::StatusType status )
197 {
198  switch ( status ) {
200  return boost::asio::buffer ( ok );
202  return boost::asio::buffer ( created );
204  return boost::asio::buffer ( accepted );
206  return boost::asio::buffer ( no_content );
208  return boost::asio::buffer ( multiple_choices );
210  return boost::asio::buffer ( moved_permanently );
212  return boost::asio::buffer ( moved_temporarily );
214  return boost::asio::buffer ( not_modified );
216  return boost::asio::buffer ( bad_request );
218  return boost::asio::buffer ( unauthorized );
220  return boost::asio::buffer ( forbidden );
222  return boost::asio::buffer ( not_found );
224  return boost::asio::buffer ( internal_server_error );
226  return boost::asio::buffer ( not_implemented );
228  return boost::asio::buffer ( bad_gateway );
230  return boost::asio::buffer ( service_unavailable );
231  default:
232  return boost::asio::buffer ( internal_server_error );
233  }
234 }
235 
236 } // namespace status_strings
237 
238 std::vector<boost::asio::const_buffer> HttpRequest::Reply::toBuffers ()
239 {
240  static const char name_value_separator[] = { ':', ' ' };
241  static const char crlf[] = { '\r', '\n' };
242  std::vector<boost::asio::const_buffer> buffers;
243  buffers.push_back ( status_strings::to_buffer ( status ) );
244 
245  for ( std::size_t i = 0; i < headers.size(); ++i ) {
246  Header &h = headers[i];
247  buffers.push_back ( boost::asio::buffer ( h.name ) );
248  buffers.push_back ( boost::asio::buffer ( name_value_separator ) );
249  buffers.push_back ( boost::asio::buffer ( h.value ) );
250  buffers.push_back ( boost::asio::buffer ( crlf ) );
251  }
252 
253  buffers.push_back ( boost::asio::buffer ( crlf ) );
254  buffers.push_back ( boost::asio::buffer ( content ) );
255  if (tile) {
256  buffers.push_back ( boost::asio::const_buffer ( ( void * ) tile->getImage()->data(), tile->getImage()->size() ) );
257  }
258  return buffers;
259 }
weak_ptr< HttpServer > server
const std::string ok
Reply reply
The reply to be sent back to the client.
const std::string forbidden
void handleRead(const boost::system::error_code &e, std::size_t bytes_transferred)
Handle completion of a read operation.
void readSome()
const std::string created
HttpRequest(boost::asio::io_service &ioService, const shared_ptr< HttpServer > &server, const shared_ptr< RequestManager > &manager)
This file is part of alaCarte.
std::array< char, 8192 > buffer
Buffer for incoming data.
const std::string accepted
std::vector< Header > headers
The headers to be included in the Reply.
RequestData data
std::string string
Definition: settings.hpp:110
TESTABLE const string & getURL() const
#define LOG_SEV(log, lvl)
Definition: settings.hpp:78
boost::asio::ip::tcp::socket socket
Socket for the connection.
static const char * access_log
Filepath to the access log (type: string)
const std::string not_found
bool checkifAnswered()
void handleWrite(const boost::system::error_code &e)
Handle completion of a write operation.
HttpRequestParser parser
The parser for the incoming request.
const std::string bad_gateway
std::string content
The content to be sent in the Reply.
const std::string not_implemented
const std::string not_modified
const std::string bad_request
void startCollectingData()
Start the first asynchronous operation for the connection.
void close()
Stop all asynchronous operations associated with the connection.
StatusType status
The Status of the Reply.
const std::string unauthorized
boost::asio::const_buffer to_buffer(HttpRequest::Reply::StatusType status)
weak_ptr< RequestManager > manager
const std::string internal_server_error
shared_ptr< Tile > tile
The Tile to be sent in the Reply.
boost::asio::ip::tcp::socket & getSocket()
const std::string moved_temporarily
const std::string multiple_choices
const std::string no_content
std::vector< boost::asio::const_buffer > toBuffers()
Convert the Reply into a vector of buffers.
const std::string moved_permanently
boost::tuple< boost::tribool, InputIterator > parse(shared_ptr< HttpRequest > req, InputIterator begin, InputIterator end)
Parse some data.
const std::string service_unavailable