Add missing template method implementations (#1013)

When using the split version of httplib.h the templated implementation
of e.g. Client::set_connection_timeout ends up in httplib.cc and
therefore results in a linker error since the needed template
specialization has not been instantiated. Fix this by moving the
implementation of template methods into the part that ends up in
httplib.h after the split.

Fixes #1008.
This commit is contained in:
Joel Rosdahl 2021-07-30 16:05:49 +02:00 committed by GitHub
parent 1b3b098329
commit 9c2c15ca45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

302
httplib.h
View file

@ -257,7 +257,7 @@ namespace detail {
template <class T, class... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args &&...args) {
make_unique(Args &&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
@ -490,7 +490,7 @@ public:
virtual socket_t socket() const = 0;
template <typename... Args>
ssize_t write_format(const char *fmt, const Args &...args);
ssize_t write_format(const char *fmt, const Args &... args);
ssize_t write(const char *ptr);
ssize_t write(const std::string &s);
};
@ -1466,6 +1466,153 @@ private:
};
#endif
/*
* Implementation of template methods.
*/
namespace detail {
template <typename T, typename U>
inline void duration_to_sec_and_usec(const T &duration, U callback) {
auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
duration - std::chrono::seconds(sec))
.count();
callback(sec, usec);
}
template <typename T>
inline T get_header_value(const Headers & /*headers*/, const char * /*key*/,
size_t /*id*/ = 0, uint64_t /*def*/ = 0) {}
template <>
inline uint64_t get_header_value<uint64_t>(const Headers &headers,
const char *key, size_t id,
uint64_t def) {
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
if (it != rng.second) {
return std::strtoull(it->second.data(), nullptr, 10);
}
return def;
}
} // namespace detail
template <typename T>
inline T Request::get_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
}
template <typename T>
inline T Response::get_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
}
template <typename... Args>
inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
const auto bufsiz = 2048;
std::array<char, bufsiz> buf;
#if defined(_MSC_VER) && _MSC_VER < 1900
auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
#else
auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
#endif
if (sn <= 0) { return sn; }
auto n = static_cast<size_t>(sn);
if (n >= buf.size() - 1) {
std::vector<char> glowable_buf(buf.size());
while (n >= glowable_buf.size() - 1) {
glowable_buf.resize(glowable_buf.size() * 2);
#if defined(_MSC_VER) && _MSC_VER < 1900
n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
glowable_buf.size() - 1, fmt,
args...));
#else
n = static_cast<size_t>(
snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
#endif
}
return write(&glowable_buf[0], n);
} else {
return write(buf.data(), n);
}
}
template <class Rep, class Period>
inline Server &
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
return *this;
}
template <typename T>
inline T Result::get_request_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(request_headers_, key, id, 0);
}
template <class Rep, class Period>
inline void ClientImpl::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
set_connection_timeout(sec, usec);
});
}
template <class Rep, class Period>
inline void ClientImpl::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
}
template <class Rep, class Period>
inline void ClientImpl::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
}
template <class Rep, class Period>
inline void Client::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
cli_->set_connection_timeout(duration);
}
template <class Rep, class Period>
inline void
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_read_timeout(duration);
}
template <class Rep, class Period>
inline void
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_write_timeout(duration);
}
// ----------------------------------------------------------------------------
/*
@ -2798,23 +2945,6 @@ inline const char *get_header_value(const Headers &headers, const char *key,
return def;
}
template <typename T>
inline T get_header_value(const Headers & /*headers*/, const char * /*key*/,
size_t /*id*/ = 0, uint64_t /*def*/ = 0) {}
template <>
inline uint64_t get_header_value<uint64_t>(const Headers &headers,
const char *key, size_t id,
uint64_t def) {
auto rng = headers.equal_range(key);
auto it = rng.first;
std::advance(it, static_cast<ssize_t>(id));
if (it != rng.second) {
return std::strtoull(it->second.data(), nullptr, 10);
}
return def;
}
template <typename T>
inline bool parse_header(const char *beg, const char *end, T fn) {
// Skip trailing spaces and tabs.
@ -3827,9 +3957,9 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
string response;
{
auto H = algo == "SHA-256" ? detail::SHA_256
: algo == "SHA-512" ? detail::SHA_512
: detail::MD5;
auto H = algo == "SHA-256"
? detail::SHA_256
: algo == "SHA-512" ? detail::SHA_512 : detail::MD5;
auto A1 = username + ":" + auth.at("realm") + ":" + password;
@ -3912,15 +4042,6 @@ private:
ContentProviderWithoutLength content_provider_;
};
template <typename T, typename U>
inline void duration_to_sec_and_usec(const T &duration, U callback) {
auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(
duration - std::chrono::seconds(sec))
.count();
callback(sec, usec);
}
} // namespace detail
// Header utilities
@ -3963,11 +4084,6 @@ inline std::string Request::get_header_value(const char *key, size_t id) const {
return detail::get_header_value(headers, key, id, "");
}
template <typename T>
inline T Request::get_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
}
inline size_t Request::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
return static_cast<size_t>(std::distance(r.first, r.second));
@ -4027,11 +4143,6 @@ inline std::string Response::get_header_value(const char *key,
return detail::get_header_value(headers, key, id, "");
}
template <typename T>
inline T Response::get_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(headers, key, id, 0);
}
inline size_t Response::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
return static_cast<size_t>(std::distance(r.first, r.second));
@ -4119,11 +4230,6 @@ inline std::string Result::get_request_header_value(const char *key,
return detail::get_header_value(request_headers_, key, id, "");
}
template <typename T>
inline T Result::get_request_header_value(const char *key, size_t id) const {
return detail::get_header_value<T>(request_headers_, key, id, 0);
}
inline size_t Result::get_request_header_value_count(const char *key) const {
auto r = request_headers_.equal_range(key);
return static_cast<size_t>(std::distance(r.first, r.second));
@ -4138,40 +4244,6 @@ inline ssize_t Stream::write(const std::string &s) {
return write(s.data(), s.size());
}
template <typename... Args>
inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {
const auto bufsiz = 2048;
std::array<char, bufsiz> buf;
#if defined(_MSC_VER) && _MSC_VER < 1900
auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
#else
auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
#endif
if (sn <= 0) { return sn; }
auto n = static_cast<size_t>(sn);
if (n >= buf.size() - 1) {
std::vector<char> glowable_buf(buf.size());
while (n >= glowable_buf.size() - 1) {
glowable_buf.resize(glowable_buf.size() * 2);
#if defined(_MSC_VER) && _MSC_VER < 1900
n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
glowable_buf.size() - 1, fmt,
args...));
#else
n = static_cast<size_t>(
snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
#endif
}
return write(&glowable_buf[0], n);
} else {
return write(buf.data(), n);
}
}
namespace detail {
// Socket stream implementation
@ -4449,42 +4521,18 @@ inline Server &Server::set_read_timeout(time_t sec, time_t usec) {
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
return *this;
}
inline Server &Server::set_write_timeout(time_t sec, time_t usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
return *this;
}
inline Server &Server::set_idle_interval(time_t sec, time_t usec) {
idle_interval_sec_ = sec;
idle_interval_usec_ = usec;
return *this;
}
template <class Rep, class Period>
inline Server &
Server::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });
return *this;
}
inline Server &Server::set_payload_max_length(size_t length) {
payload_max_length_ = length;
return *this;
@ -5642,9 +5690,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
if (ca_cert_store_) {
cli.set_ca_cert_store(ca_cert_store_);
}
if (ca_cert_store_) { cli.set_ca_cert_store(ca_cert_store_); }
return detail::redirect(cli, req, res, next_path, location, error);
#else
return false;
@ -6453,38 +6499,16 @@ inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
connection_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {
set_connection_timeout(sec, usec);
});
}
inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_read_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });
}
inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
write_timeout_sec_ = sec;
write_timeout_usec_ = usec;
}
template <class Rep, class Period>
inline void ClientImpl::set_write_timeout(
const std::chrono::duration<Rep, Period> &duration) {
detail::duration_to_sec_and_usec(
duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
}
inline void ClientImpl::set_basic_auth(const char *username,
const char *password) {
basic_auth_username_ = username;
@ -6554,7 +6578,7 @@ inline void ClientImpl::set_proxy_digest_auth(const char *username,
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline void ClientImpl::set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path) {
const char *ca_cert_dir_path) {
if (ca_cert_file_path) { ca_cert_file_path_ = ca_cert_file_path; }
if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
}
@ -7616,32 +7640,14 @@ inline void Client::set_connection_timeout(time_t sec, time_t usec) {
cli_->set_connection_timeout(sec, usec);
}
template <class Rep, class Period>
inline void Client::set_connection_timeout(
const std::chrono::duration<Rep, Period> &duration) {
cli_->set_connection_timeout(duration);
}
inline void Client::set_read_timeout(time_t sec, time_t usec) {
cli_->set_read_timeout(sec, usec);
}
template <class Rep, class Period>
inline void
Client::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_read_timeout(duration);
}
inline void Client::set_write_timeout(time_t sec, time_t usec) {
cli_->set_write_timeout(sec, usec);
}
template <class Rep, class Period>
inline void
Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
cli_->set_write_timeout(duration);
}
inline void Client::set_basic_auth(const char *username, const char *password) {
cli_->set_basic_auth(username, password);
}