Fawkes API  Fawkes Development Version
request.cpp
1 
2 /***************************************************************************
3  * request.cpp - Web request
4  *
5  * Created: Mon Jun 17 18:04:04 2013
6  * Copyright 2006-2018 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include <core/exception.h>
23 #include <netinet/in.h>
24 #include <sys/select.h>
25 #include <sys/types.h>
26 #include <webview/request.h>
27 
28 #include <cstring>
29 #include <microhttpd.h>
30 #include <stdint.h>
31 #include <unistd.h>
32 
33 namespace fawkes {
34 
35 /// @cond INTERNAL
36 static int
37 cookie_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
38 {
39  WebRequest *request = static_cast<WebRequest *>(cls);
40  request->set_cookie(key, value);
41  return MHD_YES;
42 }
43 
44 static int
45 get_argument_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
46 {
47  WebRequest *request = static_cast<WebRequest *>(cls);
48  if (value == NULL)
49  request->set_get_value(key, "");
50  else
51  request->set_get_value(key, value);
52  return MHD_YES;
53 }
54 
55 static int
56 header_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
57 {
58  WebRequest *request = static_cast<WebRequest *>(cls);
59  if (value == NULL)
60  request->set_header(key, "");
61  else
62  request->set_header(key, value);
63  return MHD_YES;
64 }
65 /// @endcond
66 
67 /** @class WebRequest <webview/request.h>
68  * Web request meta data carrier.
69  * For incoming web requests this class is instantiate to carry the
70  * necessary information for carriers like URL, request method,
71  * or cookie and POST form values.
72  * @author Tim Niemueller
73  */
74 
75 /** Constructor.
76  * @param uri URI of the request
77  */
78 WebRequest::WebRequest(const char *uri) : pp_(NULL), is_setup_(false), uri_(uri)
79 {
80  reply_size_ = 0;
81 }
82 
83 /** Complete setting up of request.
84  * @param url requested URL
85  * @param method HTTP transfer method
86  * @param version HTTP version string
87  * @param connection MicroHTTPd connection
88  */
89 void
90 WebRequest::setup(const char * url,
91  const char * method,
92  const char * version,
93  MHD_Connection *connection)
94 {
95  url_ = url;
96 
97  if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
98  method_ = METHOD_GET;
99  } else if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
100  method_ = METHOD_POST;
101  } else if (0 == strcmp(method, MHD_HTTP_METHOD_HEAD)) {
102  method_ = METHOD_HEAD;
103  } else if (0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
104  method_ = METHOD_DELETE;
105  } else if (0 == strcmp(method, MHD_HTTP_METHOD_PUT)) {
106  method_ = METHOD_PUT;
107  } else if (0 == strcmp(method, MHD_HTTP_METHOD_CONNECT)) {
108  method_ = METHOD_CONNECT;
109  } else if (0 == strcmp(method, MHD_HTTP_METHOD_OPTIONS)) {
110  method_ = METHOD_OPTIONS;
111  } else if (0 == strcmp(method, MHD_HTTP_METHOD_TRACE)) {
112  method_ = METHOD_TRACE;
113  } else if (0 == strcmp(method, MHD_HTTP_METHOD_PATCH)) {
114  method_ = METHOD_PATCH;
115  }
116 
117  if (0 == strcmp(version, MHD_HTTP_VERSION_1_0)) {
118  http_version_ = HTTP_VERSION_1_0;
119  } else if (0 == strcmp(version, MHD_HTTP_VERSION_1_1)) {
120  http_version_ = HTTP_VERSION_1_1;
121  }
122 
123  MHD_get_connection_values(connection, MHD_HEADER_KIND, &header_iterator, this);
124  MHD_get_connection_values(connection, MHD_COOKIE_KIND, &cookie_iterator, this);
125  MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, &get_argument_iterator, this);
126 
127  // check for reverse proxy header fields
128  if (headers_.find("X-Forwarded-For") != headers_.end()) {
129  std::string forwarded_for{headers_["X-Forwarded-For"]};
130  std::string::size_type comma_pos = forwarded_for.find(",");
131  if (comma_pos != std::string::npos) {
132  forwarded_for = forwarded_for.substr(0, comma_pos);
133  }
134  client_addr_ = forwarded_for;
135 
136  } else {
137  struct sockaddr *client_addr =
138  MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS)->client_addr;
139 
140  char addr_str[INET6_ADDRSTRLEN];
141  switch (client_addr->sa_family) {
142  case AF_INET:
143  inet_ntop(AF_INET,
144  &(((struct sockaddr_in *)client_addr)->sin_addr),
145  addr_str,
146  INET6_ADDRSTRLEN);
147  break;
148 
149  case AF_INET6:
150  inet_ntop(AF_INET6,
151  &(((struct sockaddr_in6 *)client_addr)->sin6_addr),
152  addr_str,
153  INET6_ADDRSTRLEN);
154  break;
155 
156  default: strncpy(addr_str, "Unknown AF", INET6_ADDRSTRLEN);
157  }
158 
159  client_addr_ = addr_str;
160  }
161 
162  is_setup_ = true;
163 }
164 
165 /** Destructor. */
167 {
168  if (pp_) {
169  MHD_destroy_post_processor(pp_);
170  pp_ = NULL;
171  }
172 }
173 
174 /** Set a POST value.
175  * @param key key of the value
176  * @param data data of the value
177  * @param size size in bytes of @p data
178  */
179 void
180 WebRequest::set_post_value(const char *key, const char *data, size_t size)
181 {
182  std::string val_add(data, size);
183  if (post_values_.find(key) != post_values_.end()) {
184  post_values_[key] += val_add;
185  } else {
186  post_values_[key] = val_add;
187  }
188 }
189 
190 /** Set request body.
191  * The data is copied as is without assuming a human-readable string
192  * or even just zero-termination.
193  * @param data data to copy
194  * @param data_size size in bytes of \@p data
195  */
196 void
197 WebRequest::set_body(const char *data, size_t data_size)
198 {
199  body_ = std::string(data, data_size);
200 }
201 
202 /** Add to request body.
203  * The data is copied as is without assuming a human-readable string
204  * or even just zero-termination.
205  * @param data data to copy
206  * @param data_size size in bytes of \@p data
207  */
208 void
209 WebRequest::addto_body(const char *data, size_t data_size)
210 {
211  body_ += std::string(data, data_size);
212 }
213 
214 /** Finalize body handling.
215  * Check for zero termination of body, and if it does not exist, add it.
216  */
217 void
219 {
220  if (body_.length() == 0)
221  return;
222  if (body_[body_.length() - 1] != 0) {
223  body_ += '\0';
224  }
225 }
226 
227 /** Increment reply bytes counter.
228  * @param increment_by number of bytes sent
229  */
230 void
231 WebRequest::increment_reply_size(size_t increment_by)
232 {
233  reply_size_ += increment_by;
234 }
235 
236 /** Get number of bytes actually sent out so far.
237  * @return number of bytes sent
238  */
239 size_t
241 {
242  return reply_size_;
243 }
244 
245 /** Get method as string.
246  * @return HTTP method as string
247  */
248 const char *
250 {
251  switch (method_) {
252  case METHOD_CONNECT: return MHD_HTTP_METHOD_CONNECT;
253  case METHOD_DELETE: return MHD_HTTP_METHOD_DELETE;
254  case METHOD_GET: return MHD_HTTP_METHOD_GET;
255  case METHOD_HEAD: return MHD_HTTP_METHOD_HEAD;
256  case METHOD_OPTIONS: return MHD_HTTP_METHOD_OPTIONS;
257  case METHOD_POST: return MHD_HTTP_METHOD_POST;
258  case METHOD_PUT: return MHD_HTTP_METHOD_PUT;
259  case METHOD_TRACE: return MHD_HTTP_METHOD_TRACE;
260  default: return "UNKNOWN_METHOD";
261  }
262 }
263 
264 /** Get HTTP version as string.
265  * @return HTTP version as string.
266  */
267 const char *
269 {
270  switch (http_version_) {
271  case HTTP_VERSION_1_0: return MHD_HTTP_VERSION_1_0;
272  case HTTP_VERSION_1_1: return MHD_HTTP_VERSION_1_1;
273  default: return "UNKNOWN_VERSION";
274  }
275 }
276 
277 /** Set HTTP code of the final reply.
278  * @param code reply code
279  */
280 void
282 {
283  reply_code_ = code;
284 }
285 
286 /** Get HTTP code of reply.
287  * @return HTTP code of reply
288  */
291 {
292  return reply_code_;
293 }
294 
295 } // end namespace fawkes
fawkes::WebRequest::reply_code
WebReply::Code reply_code() const
Get HTTP code of reply.
Definition: request.cpp:293
fawkes::WebRequest::METHOD_OPTIONS
OPTIONS.
Definition: request.h:51
fawkes::WebRequest::METHOD_DELETE
DELETE.
Definition: request.h:48
fawkes::WebRequest::method_str
const char * method_str() const
Get method as string.
Definition: request.cpp:252
fawkes::WebRequest::increment_reply_size
void increment_reply_size(size_t increment_by)
Increment reply bytes counter.
Definition: request.cpp:234
fawkes::WebRequest::addto_body
void addto_body(const char *data, size_t data_size)
Add to request body.
Definition: request.cpp:212
fawkes::WebRequest::METHOD_PATCH
PATCH.
Definition: request.h:55
fawkes::WebRequest::url
const std::string & url() const
Get URL.
Definition: request.h:67
fawkes::WebRequest::METHOD_PUT
PUT.
Definition: request.h:53
fawkes::WebRequest::client_addr
const std::string & client_addr() const
Get client address as string.
Definition: request.h:117
fawkes::WebReply::Code
Code
HTTP response code.
Definition: reply.h:41
fawkes::WebRequest::METHOD_HEAD
HEAD.
Definition: request.h:50
fawkes::WebRequest::set_post_value
void set_post_value(const char *key, const char *data, size_t size)
Set a POST value.
Definition: request.cpp:183
fawkes
fawkes::WebRequest::set_reply_code
void set_reply_code(WebReply::Code code)
Set HTTP code of the final reply.
Definition: request.cpp:284
fawkes::WebRequest::METHOD_POST
POST.
Definition: request.h:52
fawkes::WebRequest::METHOD_CONNECT
CONNECT.
Definition: request.h:47
fawkes::WebRequest::set_body
void set_body(const char *data, size_t data_size)
Set request body.
Definition: request.cpp:200
fawkes::WebRequest::finish_body
void finish_body()
Finalize body handling.
Definition: request.cpp:221
fawkes::WebRequest::METHOD_TRACE
TRACE.
Definition: request.h:54
fawkes::WebRequest::http_version_str
const char * http_version_str() const
Get HTTP version as string.
Definition: request.cpp:271
fawkes::WebRequest::method
Method method() const
Get HTTP transfer method.
Definition: request.h:83
fawkes::WebRequest::WebRequest
WebRequest(const char *uri)
Constructor.
Definition: request.cpp:81
fawkes::WebRequest::METHOD_GET
GET.
Definition: request.h:49
fawkes::WebRequest::~WebRequest
~WebRequest()
Destructor.
Definition: request.cpp:169
fawkes::WebRequest::reply_size
size_t reply_size() const
Get number of bytes actually sent out so far.
Definition: request.cpp:243