@@ -143,9 +143,9 @@ class client_session : public std::enable_shared_from_this<client_session> {
143143};
144144
145145tcp_server::tcp_server (stream_info_impl_p info, io_context_p io, send_buffer_p sendbuf,
146- factory_p factory, int chunk_size, bool allow_v4, bool allow_v6)
146+ factory_p factory, int chunk_size, bool allow_v4, bool allow_v6, bool do_sync )
147147 : chunk_size_(chunk_size), info_(std::move(info)), io_(std::move(io)),
148- factory_ (std::move(factory)), send_buffer_(std::move(sendbuf)) {
148+ factory_ (std::move(factory)), send_buffer_(std::move(sendbuf)), transfer_is_sync_(do_sync) {
149149 // assign connection-dependent fields
150150 info_->session_id (api_config::get_instance ()->session_id ());
151151 info_->reset_uid ();
@@ -232,7 +232,47 @@ void tcp_server::accept_next_connection(tcp_acceptor_p &acceptor) {
232232}
233233
234234
235- // === graceful cancellation of in-flight sockets ===
235+ // === synchronous transfer
236+
237+ void tcp_server::write_all_blocking (std::vector<asio::const_buffer> bufs) {
238+ int writes_outstanding = 0 ;
239+ bool any_session_broken = false ;
240+
241+ for (auto &sock : sync_sockets_) {
242+ asio::async_write (*sock, bufs,
243+ [this , sock, &writes_outstanding](
244+ const asio::error_code &ec, size_t bytes_transferred) {
245+ writes_outstanding--;
246+ switch (ec.value ()) {
247+ case 0 : break ; // success
248+ case asio::error::broken_pipe:
249+ case asio::error::connection_reset:
250+ LOG_F (WARNING, " Broken Pipe / Connection Reset detected. Closing socket." );
251+ {
252+ asio::error_code close_ec;
253+ sock->close (close_ec);
254+ }
255+ break ;
256+ default :
257+ LOG_F (WARNING, " Unhandled write_all_blocking error: %s." , ec.message ().c_str ());
258+ }
259+ });
260+ writes_outstanding++;
261+ }
262+ try {
263+ assert (sync_transfer_io_ctx_);
264+ sync_transfer_io_ctx_->restart ();
265+ while (writes_outstanding) sync_transfer_io_ctx_->run_one ();
266+ if (any_session_broken) {
267+ // remove sessions whose socket was closed
268+ auto new_end_it = std::remove_if (sync_sockets_.begin (), sync_sockets_.end (),
269+ [](const tcp_socket_p &sock) {
270+ return !sock->is_open ();
271+ });
272+ sync_sockets_.erase (new_end_it, sync_sockets_.end ());
273+ }
274+ } catch (std::exception &e) { LOG_F (ERROR, " Error during write_all_blocking: %s" , e.what ()); }
275+ }
236276
237277void tcp_server::register_inflight_session (const std::shared_ptr<client_session> &session) {
238278 std::lock_guard<std::recursive_mutex> lock (inflight_mut_);
@@ -535,6 +575,16 @@ void client_session::handle_send_feedheader_outcome(err_t err, std::size_t n) {
535575 // convenient for unit tests
536576 if (max_buffered_ <= 0 ) return ;
537577
578+ if (serv->transfer_is_sync_ ) {
579+ LOG_F (INFO, " Using synchronous blocking transfers for new client session." );
580+ asio::post (*serv->sync_transfer_io_ctx_ ,
581+ [serv, sock_p = std::make_shared<tcp_socket>(std::move (sock_))]() {
582+ serv->sync_sockets_ .emplace_back (std::move (sock_p));
583+ });
584+ serv->unregister_inflight_session (this );
585+ return ;
586+ }
587+
538588 // determine transfer parameters
539589 auto queue = serv->send_buffer_ ->new_consumer (max_buffered_);
540590
0 commit comments