Skip to content

Commit 1777284

Browse files
authored
Use string_view in API methods where applicable (#288)
* Issue #286: use string_view in API methods where applicable This change reduces required copying of immutable data primarily in the HTTP request API. Methods returning strings now return string_view to avoid copying immutable data from the underlying MHD impl. get_*s methods still return a std::map which must be allocated and constructed, but does not copy underlying strings. Fix a few instances of these method calls in the code base. Introduce some type aliases for maps from string_view to string_view, string to string to reduce code verbosity. Overload the comparator operator()s as required. Provide overloads for dump_*_map functions and a template impl to avoid code duplication. Bump C++ version to 17 required to use string_view. Remove CI configurations using ancient compilers that don't support string_view or C++17 * Issue #286: use string_view in API methods where applicable This change reduces required copying of immutable data primarily in the HTTP request API. Methods returning strings now return string_view to avoid copying immutable data from the underlying MHD impl. get_*s methods still return a std::map which must be allocated and constructed, but does not copy underlying strings. Fix a few instances of these method calls in the code base. Introduce some type aliases for maps from string_view to string_view, string to string to reduce code verbosity. Overload the comparator operator()s as required. Provide overloads for dump_*_map functions and a template impl to avoid code duplication. Bump C++ version to 17 required to use string_view. Remove CI configurations using ancient compilers that don't support string_view or C++17 * Issue #286: use string_view in API methods where applicable This change reduces required copying of immutable data primarily in the HTTP request API. Methods returning strings now return string_view to avoid copying immutable data from the underlying MHD impl. get_*s methods still return a std::map which must be allocated and constructed, but does not copy underlying strings. Fix a few instances of these method calls in the code base. Introduce some type aliases for maps from string_view to string_view, string to string to reduce code verbosity. Overload the comparator operator()s as required. Bump C++ version to 17 required to use string_view. Remove CI configurations using ancient compilers that don't support string_view or C++17 * remove accidentally added line of code * fix data scope issue In the file upload test, it was saving the arg_view_map from the request, which went out of scope, leaving dangling refs. Fix by making a deep copy. * undo change from arg_map to arg_view_map Because build_request_args makes a temp copy of underlying data and mutates it, it is not feasible currently to return a view over that data. * fix readme after changing get_args signature * cache transformed data to return views over data Add a data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. * cache transformed data to return views over data Add a data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. * cache transformed data to return views over data Add a data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. * cache transformed data to return views over data Add a data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. * cache transformed data to return views over data Add a http_request_data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. Remove the existing http_request::args member, use the cache instead. Make the http_request class move-only. * cache transformed data to return views over data Add a http_request_data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. Remove the existing http_request::args member, use the cache instead. Make the http_request class move-only. * cache transformed data to return views over data Add a http_request_data_cache type to the http request type, and a unique_ptr instance member. This allows caching of transformed and/or copied data from MHD, while preserving const-ness of all accessor methods. Change all applicable methods to string_view, fix a few call sites. Remove the existing http_request::args member, use the cache instead. Make the http_request class move-only.
1 parent f33553c commit 1777284

18 files changed

+229
-189
lines changed

.github/workflows/verify-build.yml

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,6 @@ jobs:
9898
cc-compiler: clang++-6.0
9999
debug: debug
100100
coverage: nocoverage
101-
- test-group: extra
102-
os: ubuntu-18.04
103-
os-type: ubuntu
104-
build-type: none
105-
compiler-family: gcc
106-
c-compiler: gcc-5
107-
cc-compiler: g++-5
108-
debug: nodebug
109-
coverage: nocoverage
110-
- test-group: extra
111-
os: ubuntu-18.04
112-
os-type: ubuntu
113-
build-type: none
114-
compiler-family: gcc
115-
c-compiler: gcc-6
116-
cc-compiler: g++-6
117-
debug: nodebug
118-
coverage: nocoverage
119101
- test-group: extra
120102
os: ubuntu-latest
121103
os-type: ubuntu
@@ -152,24 +134,6 @@ jobs:
152134
cc-compiler: g++-10
153135
debug: nodebug
154136
coverage: nocoverage
155-
- test-group: extra
156-
os: ubuntu-18.04
157-
os-type: ubuntu
158-
build-type: none
159-
compiler-family: clang
160-
c-compiler: clang-3.9
161-
cc-compiler: clang++-3.9
162-
debug: nodebug
163-
coverage: nocoverage
164-
- test-group: extra
165-
os: ubuntu-18.04
166-
os-type: ubuntu
167-
build-type: none
168-
compiler-family: clang
169-
c-compiler: clang-4.0
170-
cc-compiler: clang++-4.0
171-
debug: nodebug
172-
coverage: nocoverage
173137
- test-group: extra
174138
os: ubuntu-18.04
175139
os-type: ubuntu

README.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,9 @@ There are essentially four ways to specify an endpoint string:
511511
class url_args_resource : public http_resource {
512512
public:
513513
std::shared_ptr<http_response> render(const http_request& req) {
514-
return std::shared_ptr<http_response>(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2")));
514+
std::string arg1(req.get_arg("arg1"));
515+
std::string arg2(req.get_arg("arg2"));
516+
return std::shared_ptr<http_response>(new string_response("ARGS: " + arg1 + " and " + arg2));
515517
}
516518
};
517519

@@ -559,13 +561,13 @@ The `http_request` class has a set of methods you will have access to when imple
559561
* _**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 `'/'`.
560562
* _**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).
561563
* _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client.
562-
* _**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.
563-
* _**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.
564-
* _**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.
565-
* _**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).
566-
* _**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.
567-
* _**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.
568-
* _**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).
564+
* _**std::string_view** get_header(**std::string_view** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
565+
* _**std::string_view** get_cookie(**std::string_view** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise.
566+
* _**std::string_view** get_footer(**std::string_view** 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.
567+
* _**std::string_view** get_arg(**std::string_view** 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).
568+
* _**const std::map<std::string_view, std::string_view, http::header_comparator>** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request.
569+
* _**const std::map<std::string_view, std::string_view, http::header_comparator>** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request.
570+
* _**const std::map<std::string_view, std::string_view, 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).
569571
* _**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).
570572
* _**const std::map<std::string, std::map<std::string, http::file_info>>** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the key (as identifier of the outer map), the original file name (as identifier of the inner map) and a class `file_info`, which includes the size of the file and the path to the file in the file system.
571573
* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request.
@@ -597,7 +599,7 @@ Details on the `http::file_info` structure.
597599
class hello_world_resource : public http_resource {
598600
public:
599601
std::shared_ptr<http_response> render(const http_request& req) {
600-
return std::shared_ptr<http_response>(new string_response("Hello: " + req.get_arg("name")));
602+
return std::shared_ptr<http_response>(new string_response("Hello: " + std::string(req.get_arg("name"))));
601603
}
602604
};
603605

configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ AC_LANG([C++])
4444
AC_SYS_LARGEFILE
4545

4646
# Minimal feature-set required
47-
AX_CXX_COMPILE_STDCXX([14])
47+
AX_CXX_COMPILE_STDCXX([17])
4848

4949
native_srcdir=$srcdir
5050

@@ -197,7 +197,7 @@ AM_LDFLAGS="-lstdc++"
197197

198198
if test x"$debugit" = x"yes"; then
199199
AC_DEFINE([DEBUG],[],[Debug Mode])
200-
AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wextra -Werror -pedantic -std=c++14 -Wno-unused-command-line-argument -O0"
200+
AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wextra -Werror -pedantic -std=c++17 -Wno-unused-command-line-argument -O0"
201201
AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wextra -Werror -pedantic -Wno-unused-command-line-argument -O0"
202202
else
203203
AC_DEFINE([NDEBUG],[],[No-debug Mode])

examples/basic_authentication.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class user_pass_resource : public httpserver::http_resource {
2727
return std::shared_ptr<httpserver::basic_auth_fail_response>(new httpserver::basic_auth_fail_response("FAIL", "test@example.com"));
2828
}
2929

30-
return std::shared_ptr<httpserver::string_response>(new httpserver::string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain"));
30+
return std::shared_ptr<httpserver::string_response>(new httpserver::string_response(std::string(req.get_user()) + " " + std::string(req.get_pass()), 200, "text/plain"));
3131
}
3232
};
3333

examples/hello_with_get_arg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
class hello_world_resource : public httpserver::http_resource {
2424
public:
2525
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request& req) {
26-
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Hello: " + req.get_arg("name")));
26+
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Hello: " + std::string(req.get_arg("name"))));
2727
}
2828
};
2929

examples/hello_world.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ class hello_world_resource : public httpserver::http_resource {
3333
std::shared_ptr<httpserver::http_response> hello_world_resource::render(const httpserver::http_request& req) {
3434
// It is possible to store data inside the resource object that can be altered through the requests
3535
std::cout << "Data was: " << data << std::endl;
36-
std::string datapar = req.get_arg("data");
37-
set_some_data(datapar == "" ? "no data passed!!!" : datapar);
36+
std::string_view datapar = req.get_arg("data");
37+
set_some_data(datapar == "" ? "no data passed!!!" : std::string(datapar));
3838
std::cout << "Now data is:" << data << std::endl;
3939

4040
// It is possible to send a response initializing an http_string_response that reads the content to send in response from a string.

examples/url_registration.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ class hello_world_resource : public httpserver::http_resource {
3030
class handling_multiple_resource : public httpserver::http_resource {
3131
public:
3232
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request& req) {
33-
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Your URL: " + req.get_path()));
33+
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("Your URL: " + std::string(req.get_path())));
3434
}
3535
};
3636

3737
class url_args_resource : public httpserver::http_resource {
3838
public:
3939
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request& req) {
40-
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2")));
40+
return std::shared_ptr<httpserver::http_response>(new httpserver::string_response("ARGS: " + std::string(req.get_arg("arg1")) + " and " + std::string(req.get_arg("arg2"))));
4141
}
4242
};
4343

src/http_request.cpp

Lines changed: 72 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,17 @@ const char http_request::EMPTY[] = "";
3232

3333
struct arguments_accumulator {
3434
unescaper_ptr unescaper;
35-
std::map<std::string, std::string, http::arg_comparator>* arguments;
35+
http::arg_map* arguments;
3636
};
3737

3838
void http_request::set_method(const std::string& method) {
3939
this->method = string_utilities::to_upper_copy(method);
4040
}
4141

4242
bool http_request::check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool* reload_nonce) const {
43-
std::string digested_user = get_digested_user();
43+
std::string_view digested_user = get_digested_user();
4444

45-
int val = MHD_digest_auth_check(underlying_connection, realm.c_str(), digested_user.c_str(), password.c_str(), nonce_timeout);
45+
int val = MHD_digest_auth_check(underlying_connection, realm.c_str(), digested_user.data(), password.c_str(), nonce_timeout);
4646

4747
if (val == MHD_INVALID_NONCE) {
4848
*reload_nonce = true;
@@ -55,8 +55,8 @@ bool http_request::check_digest_auth(const std::string& realm, const std::string
5555
return true;
5656
}
5757

58-
const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const {
59-
const char* header_c = MHD_lookup_connection_value(underlying_connection, kind, key.c_str());
58+
std::string_view http_request::get_connection_value(std::string_view key, enum MHD_ValueKind kind) const {
59+
const char* header_c = MHD_lookup_connection_value(underlying_connection, kind, key.data());
6060

6161
if (header_c == nullptr) return EMPTY;
6262

@@ -67,76 +67,84 @@ MHD_Result http_request::build_request_header(void *cls, enum MHD_ValueKind kind
6767
// Parameters needed to respect MHD interface, but not used in the implementation.
6868
std::ignore = kind;
6969

70-
std::map<std::string, std::string, http::header_comparator>* dhr = static_cast<std::map<std::string, std::string, http::header_comparator>*>(cls);
70+
http::header_view_map* dhr = static_cast<http::header_view_map*>(cls);
7171
(*dhr)[key] = value;
7272
return MHD_YES;
7373
}
7474

75-
const std::map<std::string, std::string, http::header_comparator> http_request::get_headerlike_values(enum MHD_ValueKind kind) const {
76-
std::map<std::string, std::string, http::header_comparator> headers;
75+
const http::header_view_map http_request::get_headerlike_values(enum MHD_ValueKind kind) const {
76+
http::header_view_map headers;
7777

7878
MHD_get_connection_values(underlying_connection, kind, &build_request_header, reinterpret_cast<void*>(&headers));
7979

8080
return headers;
8181
}
8282

83-
const std::string http_request::get_header(const std::string& key) const {
83+
std::string_view http_request::get_header(std::string_view key) const {
8484
return get_connection_value(key, MHD_HEADER_KIND);
8585
}
8686

87-
const std::map<std::string, std::string, http::header_comparator> http_request::get_headers() const {
87+
const http::header_view_map http_request::get_headers() const {
8888
return get_headerlike_values(MHD_HEADER_KIND);
8989
}
9090

91-
const std::string http_request::get_footer(const std::string& key) const {
91+
std::string_view http_request::get_footer(std::string_view key) const {
9292
return get_connection_value(key, MHD_FOOTER_KIND);
9393
}
9494

95-
const std::map<std::string, std::string, http::header_comparator> http_request::get_footers() const {
95+
const http::header_view_map http_request::get_footers() const {
9696
return get_headerlike_values(MHD_FOOTER_KIND);
9797
}
9898

99-
const std::string http_request::get_cookie(const std::string& key) const {
99+
std::string_view http_request::get_cookie(std::string_view key) const {
100100
return get_connection_value(key, MHD_COOKIE_KIND);
101101
}
102102

103-
const std::map<std::string, std::string, http::header_comparator> http_request::get_cookies() const {
103+
const http::header_view_map http_request::get_cookies() const {
104104
return get_headerlike_values(MHD_COOKIE_KIND);
105105
}
106106

107-
const std::string http_request::get_arg(const std::string& key) const {
108-
std::map<std::string, std::string>::const_iterator it = args.find(key);
107+
std::string_view http_request::get_arg(std::string_view key) const {
108+
std::map<std::string, std::string>::const_iterator it = cache->unescaped_args.find(std::string(key));
109109

110-
if (it != args.end()) {
110+
if (it != cache->unescaped_args.end()) {
111111
return it->second;
112112
}
113113

114114
return get_connection_value(key, MHD_GET_ARGUMENT_KIND);
115115
}
116116

117-
const std::map<std::string, std::string, http::arg_comparator> http_request::get_args() const {
118-
std::map<std::string, std::string, http::arg_comparator> arguments;
119-
arguments.insert(args.begin(), args.end());
117+
const http::arg_view_map http_request::get_args() const {
118+
http::arg_view_map arguments;
119+
120+
if (!cache->unescaped_args.empty()) {
121+
arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end());
122+
return arguments;
123+
}
120124

121125
arguments_accumulator aa;
122126
aa.unescaper = unescaper;
123-
aa.arguments = &arguments;
127+
aa.arguments = &cache->unescaped_args;
124128

125129
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast<void*>(&aa));
126130

131+
arguments.insert(cache->unescaped_args.begin(), cache->unescaped_args.end());
132+
127133
return arguments;
128134
}
129135

130136
http::file_info& http_request::get_or_create_file_info(const std::string& key, const std::string& upload_file_name) {
131137
return files[key][upload_file_name];
132138
}
133139

134-
const std::string http_request::get_querystring() const {
135-
std::string querystring = "";
140+
std::string_view http_request::get_querystring() const {
141+
if (!cache->querystring.empty()) {
142+
return cache->querystring;
143+
}
136144

137-
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_querystring, reinterpret_cast<void*>(&querystring));
145+
MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_querystring, reinterpret_cast<void*>(&cache->querystring));
138146

139-
return querystring;
147+
return cache->querystring;
140148
}
141149

142150
MHD_Result http_request::build_request_args(void *cls, enum MHD_ValueKind kind, const char *key, const char *arg_value) {
@@ -173,47 +181,50 @@ MHD_Result http_request::build_request_querystring(void *cls, enum MHD_ValueKind
173181
return MHD_YES;
174182
}
175183

176-
const std::string http_request::get_user() const {
177-
char* username = nullptr;
184+
void http_request::fetch_user_pass() const {
178185
char* password = nullptr;
186+
auto* username = MHD_basic_auth_get_username_password(underlying_connection, &password);
179187

180-
username = MHD_basic_auth_get_username_password(underlying_connection, &password);
181-
if (password != nullptr) free(password);
182-
183-
std::string user;
184-
if (username != nullptr) user = username;
185-
186-
free(username);
187-
188-
return user;
188+
if (username != nullptr) {
189+
cache->username = username;
190+
MHD_free(username);
191+
}
192+
if (password != nullptr) {
193+
cache->password = password;
194+
MHD_free(password);
195+
}
189196
}
190197

191-
const std::string http_request::get_pass() const {
192-
char* username = nullptr;
193-
char* password = nullptr;
194-
195-
username = MHD_basic_auth_get_username_password(underlying_connection, &password);
196-
if (username != nullptr) free(username);
197-
198-
std::string pass;
199-
if (password != nullptr) pass = password;
200-
201-
free(password);
198+
std::string_view http_request::get_user() const {
199+
if (!cache->username.empty()) {
200+
return cache->username;
201+
}
202+
fetch_user_pass();
203+
return cache->username;
204+
}
202205

203-
return pass;
206+
std::string_view http_request::get_pass() const {
207+
if (!cache->password.empty()) {
208+
return cache->password;
209+
}
210+
fetch_user_pass();
211+
return cache->password;
204212
}
205213

206-
const std::string http_request::get_digested_user() const {
207-
char* digested_user_c = nullptr;
208-
digested_user_c = MHD_digest_auth_get_username(underlying_connection);
214+
std::string_view http_request::get_digested_user() const {
215+
if (!cache->digested_user.empty()) {
216+
return cache->digested_user;
217+
}
218+
219+
char* digested_user_c = MHD_digest_auth_get_username(underlying_connection);
209220

210-
std::string digested_user = EMPTY;
221+
cache->digested_user = EMPTY;
211222
if (digested_user_c != nullptr) {
212-
digested_user = digested_user_c;
223+
cache->digested_user = digested_user_c;
213224
free(digested_user_c);
214225
}
215226

216-
return digested_user;
227+
return cache->digested_user;
217228
}
218229

219230
#ifdef HAVE_GNUTLS
@@ -229,10 +240,15 @@ gnutls_session_t http_request::get_tls_session() const {
229240
}
230241
#endif // HAVE_GNUTLS
231242

232-
const std::string http_request::get_requestor() const {
243+
std::string_view http_request::get_requestor() const {
244+
if (!cache->requestor_ip.empty()) {
245+
return cache->requestor_ip;
246+
}
247+
233248
const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS);
234249

235-
return http::get_ip_str(conninfo->client_addr);
250+
cache->requestor_ip = http::get_ip_str(conninfo->client_addr);
251+
return cache->requestor_ip;
236252
}
237253

238254
uint16_t http_request::get_requestor_port() const {

0 commit comments

Comments
 (0)