bool is_readable() const override;
bool wait_readable() const override;
bool wait_writable() const override;
+ bool is_peer_alive() const override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
void get_remote_ip_and_port(std::string &ip, int &port) const override;
return ok;
};
- data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
while (offset < end_offset && !is_shutting_down()) {
- if (!strm.wait_writable()) {
+ if (!strm.wait_writable() || !strm.is_peer_alive()) {
error = Error::Write;
return false;
} else if (!content_provider(offset, end_offset - offset, data_sink)) {
}
}
+ if (offset < end_offset) { // exited due to is_shutting_down(), not completion
+ error = Error::Write;
+ return false;
+ }
+
error = Error::Success;
return true;
}
return ok;
};
- data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
data_sink.done = [&](void) { data_available = false; };
while (data_available && !is_shutting_down()) {
- if (!strm.wait_writable()) {
+ if (!strm.wait_writable() || !strm.is_peer_alive()) {
return false;
} else if (!content_provider(offset, 0, data_sink)) {
return false;
return false;
}
}
- return true;
+ return !data_available; // true only if done() was called, false if shutting
+ // down
}
template <typename T, typename U>
return ok;
};
- data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
+ data_sink.is_writable = [&]() -> bool { return strm.is_peer_alive(); };
auto done_with_trailer = [&](const Headers *trailer) {
if (!ok) { return; }
};
while (data_available && !is_shutting_down()) {
- if (!strm.wait_writable()) {
+ if (!strm.wait_writable() || !strm.is_peer_alive()) {
error = Error::Write;
return false;
} else if (!content_provider(offset, 0, data_sink)) {
}
}
+ if (data_available) { // exited due to is_shutting_down(), not done()
+ error = Error::Write;
+ return false;
+ }
+
error = Error::Success;
return true;
}
bool is_readable() const override;
bool wait_readable() const override;
bool wait_writable() const override;
+ bool is_peer_alive() const override;
ssize_t read(char *ptr, size_t size) override;
ssize_t write(const char *ptr, size_t size) override;
void get_remote_ip_and_port(std::string &ip, int &port) const override;
}
bool SocketStream::wait_writable() const {
- return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
- is_socket_alive(sock_);
+ return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
+}
+
+bool SocketStream::is_peer_alive() const {
+ return detail::is_socket_alive(sock_);
}
ssize_t SocketStream::read(char *ptr, size_t size) {
bool SSLSocketStream::wait_writable() const {
return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
- is_socket_alive(sock_) && !tls::is_peer_closed(session_, sock_);
+ !tls::is_peer_closed(session_, sock_);
+}
+
+bool SSLSocketStream::is_peer_alive() const {
+ return !tls::is_peer_closed(session_, sock_);
}
ssize_t SSLSocketStream::read(char *ptr, size_t size) {
if (post_routing_handler_) { post_routing_handler_(req, res); }
// Response line and headers
- {
- detail::BufferStream bstrm;
- if (!detail::write_response_line(bstrm, res.status)) { return false; }
- if (header_writer_(bstrm, res.headers) <= 0) { return false; }
+ detail::BufferStream bstrm;
+ if (!detail::write_response_line(bstrm, res.status)) { return false; }
+ if (header_writer_(bstrm, res.headers) <= 0) { return false; }
- // Flush buffer
- auto &data = bstrm.get_buffer();
- detail::write_data(strm, data.data(), data.size());
+ // Combine small body with headers to reduce write syscalls
+ if (req.method != "HEAD" && !res.body.empty() && !res.content_provider_) {
+ bstrm.write(res.body.data(), res.body.size());
}
- // Body
+ // Log before writing to avoid race condition with client-side code that
+ // accesses logger-captured data immediately after receiving the response.
+ output_log(req, res);
+
+ // Flush buffer
+ auto &data = bstrm.get_buffer();
+ if (!detail::write_data(strm, data.data(), data.size())) { return false; }
+
+ // Streaming body
auto ret = true;
- if (req.method != "HEAD") {
- if (!res.body.empty()) {
- if (!detail::write_data(strm, res.body.data(), res.body.size())) {
- ret = false;
- }
- } else if (res.content_provider_) {
- if (write_content_with_provider(strm, req, res, boundary, content_type)) {
- res.content_provider_success_ = true;
- } else {
- ret = false;
- }
+ if (req.method != "HEAD" && res.content_provider_) {
+ if (write_content_with_provider(strm, req, res, boundary, content_type)) {
+ res.content_provider_success_ = true;
+ } else {
+ ret = false;
}
}
- // Log
- output_log(req, res);
-
return ret;
}
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
-#define CPPHTTPLIB_VERSION "0.33.1"
-#define CPPHTTPLIB_VERSION_NUM "0x002101"
+#define CPPHTTPLIB_VERSION "0.34.0"
+#define CPPHTTPLIB_VERSION_NUM "0x002200"
/*
* Platform compatibility check
return fdp;
}
+inline std::pair<size_t, ContentProvider>
+make_file_body(const std::string &filepath) {
+ std::ifstream f(filepath, std::ios::binary | std::ios::ate);
+ if (!f) { return {0, ContentProvider{}}; }
+ auto size = static_cast<size_t>(f.tellg());
+
+ ContentProvider provider = [filepath](size_t offset, size_t length,
+ DataSink &sink) -> bool {
+ std::ifstream f(filepath, std::ios::binary);
+ if (!f) { return false; }
+ f.seekg(static_cast<std::streamoff>(offset));
+ if (!f.good()) { return false; }
+ char buf[8192];
+ while (length > 0) {
+ auto to_read = (std::min)(sizeof(buf), length);
+ f.read(buf, static_cast<std::streamsize>(to_read));
+ auto n = static_cast<size_t>(f.gcount());
+ if (n == 0) { break; }
+ if (!sink.write(buf, n)) { return false; }
+ length -= n;
+ }
+ return true;
+ };
+ return {size, std::move(provider)};
+}
+
using ContentReceiverWithProgress = std::function<bool(
const char *data, size_t data_length, size_t offset, size_t total_length)>;
virtual bool is_readable() const = 0;
virtual bool wait_readable() const = 0;
virtual bool wait_writable() const = 0;
+ virtual bool is_peer_alive() const { return wait_writable(); }
virtual ssize_t read(char *ptr, size_t size) = 0;
virtual ssize_t write(const char *ptr, size_t size) = 0;