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