Crow  0.3
A C++ microframework for the web
http_response.h
1 #pragma once
2 #include <string>
3 #include <unordered_map>
4 #include <ios>
5 #include <fstream>
6 #include <sstream>
7 #include <sys/stat.h>
8 
9 #include "crow/http_request.h"
10 #include "crow/ci_map.h"
11 #include "crow/socket_adaptors.h"
12 #include "crow/logging.h"
13 #include "crow/mime_types.h"
14 #include "crow/returnable.h"
15 
16 
17 namespace crow
18 {
19  template <typename Adaptor, typename Handler, typename ... Middlewares>
20  class Connection;
21 
22  /// HTTP response
23  struct response
24  {
25  template <typename Adaptor, typename Handler, typename ... Middlewares>
26  friend class crow::Connection;
27 
28  int code{200}; ///< The Status code for the response.
29  std::string body; ///< The actual payload containing the response data.
30  ci_map headers; ///< HTTP headers.
31 
32 #ifdef CROW_ENABLE_COMPRESSION
33  bool compressed = true; ///< If compression is enabled and this is false, the individual response will not be compressed.
34 #endif
35  bool is_head_response = false; ///< Whether this is a response to a HEAD request.
36  bool manual_length_header = false; ///< Whether Crow should automatically add a "Content-Length" header.
37 
38  /// Set the value of an existing header in the response.
39  void set_header(std::string key, std::string value)
40  {
41  headers.erase(key);
42  headers.emplace(std::move(key), std::move(value));
43  }
44 
45  /// Add a new header to the response.
46  void add_header(std::string key, std::string value)
47  {
48  headers.emplace(std::move(key), std::move(value));
49  }
50 
51  const std::string& get_header_value(const std::string& key)
52  {
53  return crow::get_header_value(headers, key);
54  }
55 
56 
57  response() {}
58  explicit response(int code) : code(code) {}
59  response(std::string body) : body(std::move(body)) {}
60  response(int code, std::string body) : code(code), body(std::move(body)) {}
61  response (returnable&& value)
62  {
63  body = value.dump();
64  set_header("Content-Type",value.content_type);
65  }
66  response (returnable& value)
67  {
68  body = value.dump();
69  set_header("Content-Type",value.content_type);
70  }
71  response (int code, returnable& value) : code(code)
72  {
73  body = value.dump();
74  set_header("Content-Type",value.content_type);
75  }
76 
77  response(response&& r)
78  {
79  *this = std::move(r);
80  }
81 
82  response& operator = (const response& r) = delete;
83 
84  response& operator = (response&& r) noexcept
85  {
86  body = std::move(r.body);
87  code = r.code;
88  headers = std::move(r.headers);
89  completed_ = r.completed_;
90  file_info = std::move(r.file_info);
91  return *this;
92  }
93 
94  /// Check if the response has completed (whether response.end() has been called)
95  bool is_completed() const noexcept
96  {
97  return completed_;
98  }
99 
100  void clear()
101  {
102  body.clear();
103  code = 200;
104  headers.clear();
105  completed_ = false;
106  file_info = static_file_info{};
107  }
108 
109  /// Return a "Temporary Redirect" response.
110  ///
111  /// Location can either be a route or a full URL.
112  void redirect(const std::string& location)
113  {
114  code = 307;
115  set_header("Location", location);
116  }
117 
118  /// Return a "Permanent Redirect" response.
119  ///
120  /// Location can either be a route or a full URL.
121  void redirect_perm(const std::string& location)
122  {
123  code = 308;
124  set_header("Location", location);
125  }
126 
127  /// Return a "Found (Moved Temporarily)" response.
128  ///
129  /// Location can either be a route or a full URL.
130  void moved(const std::string& location)
131  {
132  code = 302;
133  set_header("Location", location);
134  }
135 
136  /// Return a "Moved Permanently" response.
137  ///
138  /// Location can either be a route or a full URL.
139  void moved_perm(const std::string& location)
140  {
141  code = 301;
142  set_header("Location", location);
143  }
144 
145  void write(const std::string& body_part)
146  {
147  body += body_part;
148  }
149 
150  /// Set the response completion flag and call the handler (to send the response).
151  void end()
152  {
153  if (!completed_)
154  {
155  completed_ = true;
156  if (is_head_response)
157  {
158  set_header("Content-Length", std::to_string(body.size()));
159  body = "";
160  manual_length_header = true;
161  }
162  if (complete_request_handler_)
163  {
164  complete_request_handler_();
165  }
166  }
167  }
168 
169  /// Same as end() except it adds a body part right before ending.
170  void end(const std::string& body_part)
171  {
172  body += body_part;
173  end();
174  }
175 
176  /// Check if the connection is still alive (usually by checking the socket status).
177  bool is_alive()
178  {
179  return is_alive_helper_ && is_alive_helper_();
180  }
181 
182  /// Check whether the response has a static file defined.
184  {
185  return file_info.path.size();
186  }
187 
188  /// This constains metadata (coming from the `stat` command) related to any static files associated with this response.
189 
190  /// Either a static file or a string body can be returned as 1 response.
191  ///
193  std::string path = "";
194  struct stat statbuf;
195  int statResult;
196  };
197 
198  ///Return a static file as the response body
199  void set_static_file_info(std::string path){
200  file_info.path = path;
201  file_info.statResult = stat(file_info.path.c_str(), &file_info.statbuf);
202 #ifdef CROW_ENABLE_COMPRESSION
203  compressed = false;
204 #endif
205  if (file_info.statResult == 0)
206  {
207  std::size_t last_dot = path.find_last_of(".");
208  std::string extension = path.substr(last_dot+1);
209  std::string mimeType = "";
210  code = 200;
211  this->add_header("Content-length", std::to_string(file_info.statbuf.st_size));
212 
213  if (extension != ""){
214  mimeType = mime_types[extension];
215  if (mimeType != "")
216  this-> add_header("Content-Type", mimeType);
217  else
218  this-> add_header("content-Type", "text/plain");
219  }
220  }
221  else
222  {
223  code = 404;
224  this->end();
225  }
226  }
227 
228  /// Stream a static file.
229  template<typename Adaptor>
230  void do_stream_file(Adaptor& adaptor)
231  {
232  if (file_info.statResult == 0)
233  {
234  std::ifstream is(file_info.path.c_str(), std::ios::in | std::ios::binary);
235  write_streamed(is, adaptor);
236  }
237  }
238 
239  /// Stream the response body (send the body in chunks).
240  template<typename Adaptor>
241  void do_stream_body(Adaptor& adaptor)
242  {
243  if (body.length() > 0)
244  {
245  write_streamed_string(body, adaptor);
246  }
247  }
248 
249  private:
250  bool completed_{};
251  std::function<void()> complete_request_handler_;
252  std::function<bool()> is_alive_helper_;
253  static_file_info file_info;
254 
255  template<typename Stream, typename Adaptor>
256  void write_streamed(Stream& is, Adaptor& adaptor)
257  {
258  char buf[16384];
259  while (is.read(buf, sizeof(buf)).gcount() > 0)
260  {
261  std::vector<asio::const_buffer> buffers;
262  buffers.push_back(boost::asio::buffer(buf));
263  write_buffer_list(buffers, adaptor);
264  }
265  }
266 
267  //THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
268  template<typename Adaptor>
269  void write_streamed_string(std::string& is, Adaptor& adaptor)
270  {
271  std::string buf;
272  std::vector<asio::const_buffer> buffers;
273 
274  while (is.length() > 16384)
275  {
276  //buf.reserve(16385);
277  buf = is.substr(0, 16384);
278  is = is.substr(16384);
279  push_and_write(buffers, buf, adaptor);
280  }
281  //Collect whatever is left (less than 16KB) and send it down the socket
282  //buf.reserve(is.length());
283  buf = is;
284  is.clear();
285  push_and_write(buffers, buf, adaptor);
286  }
287 
288  template<typename Adaptor>
289  inline void push_and_write(std::vector<asio::const_buffer>& buffers, std::string& buf, Adaptor& adaptor)
290  {
291  buffers.clear();
292  buffers.push_back(boost::asio::buffer(buf));
293  write_buffer_list(buffers, adaptor);
294  }
295 
296  template<typename Adaptor>
297  inline void write_buffer_list(std::vector<asio::const_buffer>& buffers, Adaptor& adaptor)
298  {
299  boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t)
300  {
301  if (!ec)
302  {
303  return false;
304  }
305  else
306  {
307  CROW_LOG_ERROR << ec << " - happened while sending buffers";
308  this->end();
309  return true;
310  }
311  });
312  }
313 
314  };
315 }
crow::response::set_static_file_info
void set_static_file_info(std::string path)
Return a static file as the response body.
Definition: http_response.h:199
crow::response::is_alive
bool is_alive()
Check if the connection is still alive (usually by checking the socket status).
Definition: http_response.h:177
crow::response::static_file_info
This constains metadata (coming from the stat command) related to any static files associated with th...
Definition: http_response.h:192
crow::response::redirect
void redirect(const std::string &location)
Definition: http_response.h:112
crow::response::headers
ci_map headers
HTTP headers.
Definition: http_response.h:30
crow::response::end
void end(const std::string &body_part)
Same as end() except it adds a body part right before ending.
Definition: http_response.h:170
crow::response::is_static_type
bool is_static_type()
Check whether the response has a static file defined.
Definition: http_response.h:183
crow::Connection
An HTTP connection.
Definition: http_connection.h:185
crow::response::code
int code
The Status code for the response.
Definition: http_response.h:28
crow::response::is_completed
bool is_completed() const noexcept
Check if the response has completed (whether response.end() has been called)
Definition: http_response.h:95
crow::response::do_stream_file
void do_stream_file(Adaptor &adaptor)
Stream a static file.
Definition: http_response.h:230
crow::response
HTTP response.
Definition: http_response.h:23
crow::response::do_stream_body
void do_stream_body(Adaptor &adaptor)
Stream the response body (send the body in chunks).
Definition: http_response.h:241
crow::response::moved
void moved(const std::string &location)
Definition: http_response.h:130
crow::response::redirect_perm
void redirect_perm(const std::string &location)
Definition: http_response.h:121
crow::response::moved_perm
void moved_perm(const std::string &location)
Definition: http_response.h:139
crow::response::add_header
void add_header(std::string key, std::string value)
Add a new header to the response.
Definition: http_response.h:46
crow::response::manual_length_header
bool manual_length_header
Whether Crow should automatically add a "Content-Length" header.
Definition: http_response.h:36
crow::response::end
void end()
Set the response completion flag and call the handler (to send the response).
Definition: http_response.h:151
crow::response::set_header
void set_header(std::string key, std::string value)
Set the value of an existing header in the response.
Definition: http_response.h:39
crow::response::is_head_response
bool is_head_response
Whether this is a response to a HEAD request.
Definition: http_response.h:35
crow::response::body
std::string body
The actual payload containing the response data.
Definition: http_response.h:29