Skip to content

Commit 0eaa66d

Browse files
authored
Merge pull request #143 from etr/lazy_request_building
Lazy request building
2 parents a0920a8 + cb0b1a6 commit 0eaa66d

File tree

9 files changed

+374
-449
lines changed

9 files changed

+374
-449
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -542,23 +542,23 @@ The `http_request` class has a set of methods you will have access to when imple
542542
* _**const std::vector\<std::string\>&** get_path_pieces() **const**:_ Returns the components of the path requested by the HTTP client (each piece of the path split by `'/'`.
543543
* _**const std::string&** get_path_piece(int index) **const**:_ Returns one piece of the path requested by the HTTP client. The piece is selected through the `index` parameter (0-indexed).
544544
* _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client.
545-
* _**const std::string&** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
546-
* _**const std::string&** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
547-
* _**const std::string&** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise.
548-
* _**const std::string&** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default).
549-
* _**const std::map<std::string, std::string, http::header_comparator>&** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request.
550-
* _**const std::map<std::string, std::string, http::header_comparator>&** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request.
551-
* _**const std::map<std::string, std::string, http::header_comparator>&** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings).
552-
* _**const std::map<std::string, std::string, http::arg_comparator>&** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default).
545+
* _**const std::string** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
546+
* _**const std::string** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
547+
* _**const std::string** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise.
548+
* _**const std::string** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default).
549+
* _**const std::map<std::string, std::string, http::header_comparator>** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request.
550+
* _**const std::map<std::string, std::string, http::header_comparator>** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request.
551+
* _**const std::map<std::string, std::string, http::header_comparator>** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings).
552+
* _**const std::map<std::string, std::string, http::arg_comparator>** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default).
553553
* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request.
554554
* _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server.
555-
* _**const std::string&** get_querystring() **const**:_ Returns the `querystring` of the HTTP request.
555+
* _**const std::string** get_querystring() **const**:_ Returns the `querystring` of the HTTP request.
556556
* _**const std::string&** get_version() **const**:_ Returns the HTTP version of the client request.
557-
* _**const std::string&** get_requestor() **const**:_ Returns the IP from which the client is sending the request.
557+
* _**const std::string** get_requestor() **const**:_ Returns the IP from which the client is sending the request.
558558
* _**unsigned short** get_requestor_port() **const**:_ Returns the port from which the client is sending the request.
559-
* _**const std::string&** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default).
560-
* _**const std::string&** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default).
561-
* _**const std::string&** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default).
559+
* _**const std::string** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default).
560+
* _**const std::string** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default).
561+
* _**const std::string** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default).
562562
* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise.
563563

564564
#### Example of handler reading arguments from a request

src/http_request.cpp

Lines changed: 238 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ namespace httpserver
3131

3232
const std::string http_request::EMPTY = "";
3333

34+
struct arguments_accumulator
35+
{
36+
unescaper_ptr unescaper;
37+
std::map<std::string, std::string, http::arg_comparator>* arguments;
38+
};
39+
3440
void http_request::set_method(const std::string& method)
3541
{
3642
this->method = string_utilities::to_upper_copy(method);
@@ -43,6 +49,8 @@ bool http_request::check_digest_auth(
4349
bool& reload_nonce
4450
) const
4551
{
52+
std::string digested_user = get_digested_user();
53+
4654
int val = MHD_digest_auth_check(
4755
underlying_connection,
4856
realm.c_str(),
@@ -65,21 +73,242 @@ bool http_request::check_digest_auth(
6573
return true;
6674
}
6775

76+
const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const
77+
{
78+
const char* header_c = MHD_lookup_connection_value(
79+
this->underlying_connection,
80+
kind,
81+
key.c_str()
82+
);
83+
84+
if (header_c == NULL) return EMPTY;
85+
86+
return header_c;
87+
}
88+
89+
int http_request::build_request_header(
90+
void *cls,
91+
enum MHD_ValueKind kind,
92+
const char *key,
93+
const char *value
94+
)
95+
{
96+
std::map<std::string, std::string, http::header_comparator>* dhr = static_cast<std::map<std::string, std::string, http::header_comparator>*>(cls);
97+
(*dhr)[key] = value;
98+
return MHD_YES;
99+
}
100+
101+
const std::map<std::string, std::string, http::header_comparator> http_request::get_headerlike_values(enum MHD_ValueKind kind) const
102+
{
103+
std::map<std::string, std::string, http::header_comparator> headers;
104+
105+
MHD_get_connection_values(
106+
this->underlying_connection,
107+
kind,
108+
&build_request_header,
109+
(void*) &headers
110+
);
111+
112+
return headers;
113+
}
114+
115+
const std::string http_request::get_header(const std::string& key) const
116+
{
117+
return get_connection_value(key, MHD_HEADER_KIND);
118+
}
119+
120+
const std::map<std::string, std::string, http::header_comparator> http_request::get_headers() const
121+
{
122+
return get_headerlike_values(MHD_HEADER_KIND);
123+
}
124+
125+
const std::string http_request::get_footer(const std::string& key) const
126+
{
127+
return get_connection_value(key, MHD_FOOTER_KIND);
128+
}
129+
130+
const std::map<std::string, std::string, http::header_comparator> http_request::get_footers() const
131+
{
132+
return get_headerlike_values(MHD_FOOTER_KIND);
133+
}
134+
135+
const std::string http_request::get_cookie(const std::string& key) const
136+
{
137+
return get_connection_value(key, MHD_COOKIE_KIND);
138+
}
139+
140+
const std::map<std::string, std::string, http::header_comparator> http_request::get_cookies() const
141+
{
142+
return get_headerlike_values(MHD_COOKIE_KIND);
143+
}
144+
145+
const std::string http_request::get_arg(const std::string& key) const
146+
{
147+
std::map<std::string, std::string>::const_iterator it = this->args.find(key);
148+
149+
if(it != this->args.end())
150+
{
151+
return it->second;
152+
}
153+
154+
return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
155+
}
156+
157+
const std::map<std::string, std::string, http::arg_comparator> http_request::get_args() const
158+
{
159+
std::map<std::string, std::string, http::arg_comparator> arguments;
160+
arguments.insert(this->args.begin(), this->args.end());
161+
162+
arguments_accumulator aa;
163+
aa.unescaper = this->unescaper;
164+
aa.arguments = &arguments;
165+
166+
MHD_get_connection_values(
167+
this->underlying_connection,
168+
MHD_GET_ARGUMENT_KIND,
169+
&build_request_args,
170+
(void*) &aa
171+
);
172+
173+
return arguments;
174+
}
175+
176+
const std::string http_request::get_querystring() const
177+
{
178+
std::string querystring = "";
179+
180+
MHD_get_connection_values(
181+
this->underlying_connection,
182+
MHD_GET_ARGUMENT_KIND,
183+
&build_request_querystring,
184+
(void*) &querystring
185+
);
186+
187+
return querystring;
188+
}
189+
190+
int http_request::build_request_args(
191+
void *cls,
192+
enum MHD_ValueKind kind,
193+
const char *key,
194+
const char *arg_value
195+
)
196+
{
197+
arguments_accumulator* aa = static_cast<arguments_accumulator*>(cls);
198+
std::string value = ((arg_value == NULL) ? "" : arg_value);
199+
200+
http::base_unescaper(value, aa->unescaper);
201+
(*aa->arguments)[key] = value;
202+
return MHD_YES;
203+
}
204+
205+
int http_request::build_request_querystring(
206+
void *cls,
207+
enum MHD_ValueKind kind,
208+
const char *key,
209+
const char *arg_value
210+
)
211+
{
212+
std::string* querystring = static_cast<std::string*>(cls);
213+
std::string value = ((arg_value == NULL) ? "" : arg_value);
214+
{
215+
char buf[std::string(key).size() + value.size() + 3];
216+
if(*querystring == "")
217+
{
218+
snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str());
219+
*querystring = buf;
220+
}
221+
else
222+
{
223+
snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str());
224+
*querystring += string(buf);
225+
}
226+
}
227+
228+
return MHD_YES;
229+
}
230+
231+
const std::string http_request::get_user() const
232+
{
233+
char* username = 0x0;
234+
char* password = 0x0;
235+
236+
username = MHD_basic_auth_get_username_password(underlying_connection, &password);
237+
if (password != 0x0) free(password);
238+
239+
std::string user;
240+
if (username != 0x0) user = username;
241+
242+
free(username);
243+
244+
return user;
245+
}
246+
247+
const std::string http_request::get_pass() const
248+
{
249+
char* username = 0x0;
250+
char* password = 0x0;
251+
252+
username = MHD_basic_auth_get_username_password(underlying_connection, &password);
253+
if (username != 0x0) free(username);
254+
255+
std::string pass;
256+
if (password != 0x0) pass = password;
257+
258+
free(password);
259+
260+
return pass;
261+
}
262+
263+
const std::string http_request::get_digested_user() const
264+
{
265+
char* digested_user_c = 0x0;
266+
digested_user_c = MHD_digest_auth_get_username(underlying_connection);
267+
268+
std::string digested_user = EMPTY;
269+
if (digested_user_c != 0x0)
270+
{
271+
digested_user = digested_user_c;
272+
free(digested_user_c);
273+
}
274+
275+
return digested_user;
276+
}
277+
278+
const std::string http_request::get_requestor() const
279+
{
280+
const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(
281+
underlying_connection,
282+
MHD_CONNECTION_INFO_CLIENT_ADDRESS
283+
);
284+
285+
return http::get_ip_str(conninfo->client_addr);
286+
}
287+
288+
unsigned short http_request::get_requestor_port() const
289+
{
290+
const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(
291+
underlying_connection,
292+
MHD_CONNECTION_INFO_CLIENT_ADDRESS
293+
);
294+
295+
return http::get_port(conninfo->client_addr);
296+
}
297+
68298
std::ostream &operator<< (std::ostream &os, const http_request &r)
69299
{
70-
os << r.method << " Request [user:\"" << r.user << "\" pass:\"" << r.pass << "\"] path:\""
71-
<< r.path << "\"" << std::endl;
300+
os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\""
301+
<< r.get_path() << "\"" << std::endl;
72302

73-
http::dump_header_map(os,"Headers",r.headers);
74-
http::dump_header_map(os,"Footers",r.footers);
75-
http::dump_header_map(os,"Cookies",r.cookies);
76-
http::dump_arg_map(os,"Query Args",r.args);
303+
http::dump_header_map(os,"Headers",r.get_headers());
304+
http::dump_header_map(os,"Footers",r.get_footers());
305+
http::dump_header_map(os,"Cookies",r.get_cookies());
306+
http::dump_arg_map(os,"Query Args",r.get_args());
77307

78-
os << " Version [ " << r.version << " ] Requestor [ " << r.requestor
79-
<< " ] Port [ " << r.requestor_port << " ]" << std::endl;
308+
os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor()
309+
<< " ] Port [ " << r.get_requestor_port() << " ]" << std::endl;
80310

81311
return os;
82312
}
83313

84-
85314
}

src/http_utils.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,18 @@ void dump_arg_map(std::ostream &os, const std::string &prefix,
593593
}
594594
}
595595

596+
size_t base_unescaper(std::string& s, unescaper_ptr unescaper)
597+
{
598+
if(s[0] == 0) return 0;
599+
600+
if(unescaper != 0x0)
601+
{
602+
unescaper(s);
603+
return s.size();
604+
}
605+
606+
return http_unescape(s);
607+
}
596608

597609
};
598610
};

src/httpserver/create_webserver.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class http_request;
3939

4040
typedef const std::shared_ptr<http_response>(*render_ptr)(const http_request&);
4141
typedef bool(*validator_ptr)(const std::string&);
42-
typedef void(*unescaper_ptr)(std::string&);
4342
typedef void(*log_access_ptr)(const std::string&);
4443
typedef void(*log_error_ptr)(const std::string&);
4544

0 commit comments

Comments
 (0)