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