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
34namespace fawkes {
35
36/// @cond INTERNAL
37static MHD_RESULT
38cookie_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
45static MHD_RESULT
46get_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
56static MHD_RESULT
57header_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 */
79WebRequest::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 */
90void
91WebRequest::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 */
180void
181WebRequest::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 */
197void
198WebRequest::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 */
209void
210WebRequest::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 */
218void
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 */
231void
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 */
240size_t
242{
243 return reply_size_;
244}
245
246/** Get method as string.
247 * @return HTTP method as string
248 */
249const 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 */
268const 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 */
281void
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 & url() const
Get URL.
Definition: request.h:68
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
const std::string & client_addr() const
Get client address as string.
Definition: request.h:118
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
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.