26 #ifndef STATICLIB_PION_HTTP_RESPONSE_WRITER_HPP
27 #define STATICLIB_PION_HTTP_RESPONSE_WRITER_HPP
37 #include "staticlib/config.hpp"
38 #include "staticlib/io/span.hpp"
55 tcp_connection_ptr tcp_conn;
60 std::vector<asio::const_buffer> content_buffers;
65 std::vector<std::unique_ptr<char[]>> payload_cache;
70 size_t content_length;
75 bool client_supports_chunks;
90 std::unique_ptr<http_response> response;
95 std::string response_line;
108 client_supports_chunks(true),
109 sending_chunks(false),
139 content_buffers.clear();
140 payload_cache.clear();
149 void write(sl::io::span<const char> data) {
150 if (response->is_body_allowed() && data.size() > 0) {
151 content_buffers.push_back(add_to_cache(data));
152 content_length += data.size();
164 if (response->is_body_allowed() && data.size() > 0) {
165 content_buffers.push_back(asio::buffer(data.data(), data.size()));
166 content_length += data.size();
177 static void send(std::unique_ptr<http_response_writer>
self) {
178 auto self_ptr =
self.get();
179 auto self_shared = sl::support::make_shared_with_release_deleter(
self.release());
180 self_ptr->send_more_data(
false,
181 [self_shared](
const std::error_code& ec, std::size_t bt) {
182 auto self = sl::support::make_unique_from_shared_with_release_deleter(self_shared);
183 if (
nullptr !=
self.
get()) {
184 handle_write(std::move(
self), ec, bt);
187 "Lost context detected in 'async_write'");
202 template <
typename SendHandler>
void send_chunk(SendHandler send_handler) {
203 sending_chunks =
true;
207 tcp_conn->set_lifecycle(tcp_connection::lifecycle::close);
210 if (tcp_conn->is_open()) {
212 send_more_data(
false, send_handler);
228 self->sending_chunks =
true;
229 auto self_ptr =
self.get();
230 auto self_shared = sl::support::make_shared_with_release_deleter(
self.release());
231 self_ptr->send_more_data(
true,
232 [self_shared](
const std::error_code& ec, std::size_t bt) {
233 auto self = sl::support::make_unique_from_shared_with_release_deleter(self_shared);
234 if (
nullptr !=
self.
get()) {
235 handle_write(std::move(
self), ec, bt);
238 "Lost context detected in 'async_write' final");
258 return content_length;
267 client_supports_chunks = b;
276 return client_supports_chunks;
285 return sending_chunks;
295 void prepare_buffers_for_send(std::vector<asio::const_buffer>& write_buffers) {
299 response->prepare_buffers_for_send(write_buffers,
get_connection()->get_keep_alive(),
310 static void handle_write(std::unique_ptr<http_response_writer>
self,
311 const std::error_code& ec, std::size_t bytes_written) {
314 if (self->sending_chunked_message()) {
316 "Sent HTTP response chunk of " << bytes_written <<
" bytes");
319 "Sent HTTP response of " << bytes_written <<
" bytes ("
320 << (self->get_connection()->get_keep_alive() ?
"keeping alive)" :
"closing)"));
323 self->tcp_conn->finish();
332 template <
typename SendHandler>
333 void send_more_data(
const bool send_final_chunk, SendHandler send_handler) {
335 if (tcp_conn->is_open()) {
338 auto write_buffers = std::vector<asio::const_buffer>();
339 prepare_write_buffers(write_buffers, send_final_chunk);
341 auto send_handler_stranded = tcp_conn->get_strand().wrap(send_handler);
342 tcp_conn->async_write(write_buffers, send_handler_stranded);
354 void prepare_write_buffers(std::vector<asio::const_buffer>& write_buffers,
355 const bool send_final_chunk) {
357 if (! sent_headers) {
359 prepare_buffers_for_send(write_buffers);
370 if (content_length > 0) {
374 auto cast_buf = std::array<char, 35>();
376 auto written = sprintf_s(cast_buf.data(), cast_buf.size(),
"%lx",
static_cast<long>(content_length));
378 auto written = sprintf(cast_buf.data(),
"%lx",
static_cast<long>(content_length));
383 write_buffers.push_back(add_to_cache({cast_buf.data(), written}));
388 write_buffers.insert(write_buffers.end(), content_buffers.begin(),
389 content_buffers.end());
394 write_buffers.insert(write_buffers.end(), content_buffers.begin(),
395 content_buffers.end());
404 write_buffers.push_back(add_to_cache({std::addressof(zero), 1}));
417 asio::const_buffer add_to_cache(sl::io::span<const char> data) {
418 payload_cache.emplace_back(
new char[data.size()]);
419 char* dest = payload_cache.back().get();
420 std::memcpy(dest, data.data(), data.size());
421 return asio::buffer(dest, data.size());
428 using response_writer_ptr = std::unique_ptr<http_response_writer>;
433 #endif // STATICLIB_PION_HTTP_RESPONSE_WRITER_HPP