Fixed networking flush algorithm for the file transfer
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "./LocalFileProvider.h"
|
||||
#include "./duration_utils.h"
|
||||
#include "HTTPUtils.h"
|
||||
#include "LocalFileProvider.h"
|
||||
|
||||
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
|
||||
#define TCP_NOPUSH TCP_CORK
|
||||
@@ -132,6 +133,8 @@ bool FileClient::enqueue_network_buffer_bytes(const void *snd_buffer, size_t siz
|
||||
size_t buffer_size;
|
||||
{
|
||||
std::lock_guard block{this->network_buffer.mutex};
|
||||
if(this->network_buffer.write_disconnected)
|
||||
goto write_disconnected;
|
||||
*this->network_buffer.buffer_tail = tbuffer;
|
||||
this->network_buffer.buffer_tail = &tbuffer->next;
|
||||
|
||||
@@ -140,6 +143,28 @@ bool FileClient::enqueue_network_buffer_bytes(const void *snd_buffer, size_t siz
|
||||
|
||||
this->add_network_write_event(false);
|
||||
return buffer_size > TRANSFER_MAX_CACHED_BYTES;
|
||||
|
||||
write_disconnected:
|
||||
free_buffer(tbuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileClient::flush_network_buffer() {
|
||||
Buffer* current_head;
|
||||
{
|
||||
std::lock_guard block{this->network_buffer.mutex};
|
||||
|
||||
this->network_buffer.write_disconnected = true;
|
||||
this->network_buffer.bytes = 0;
|
||||
current_head = std::exchange(this->network_buffer.buffer_head, nullptr);
|
||||
this->network_buffer.buffer_tail = &this->network_buffer.buffer_head;
|
||||
}
|
||||
|
||||
while(current_head) {
|
||||
auto next = current_head->next;
|
||||
free_buffer(current_head);
|
||||
current_head = next;
|
||||
}
|
||||
}
|
||||
|
||||
NetworkingStartResult LocalFileTransfer::start_networking() {
|
||||
@@ -347,16 +372,18 @@ bool LocalFileTransfer::initialize_client_ssl(const std::shared_ptr<FileClient>
|
||||
if(!ssl_option_supplier || !(options = ssl_option_supplier())) {
|
||||
logError(0, "{} Failed to initialize client SSL pipe because we've no SSL options.", client->log_prefix());
|
||||
|
||||
client->flush_network_buffer(); /* invalidate all network write operations */
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client, slock, false);
|
||||
client->handle->disconnect_client(client, slock, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ssl_pipe.initialize(options, error)) {
|
||||
logWarning(0, "{} Failed to initialize client SSL pipe ({}). Disconnecting client.", client->log_prefix(), error);
|
||||
|
||||
client->flush_network_buffer(); /* invalidate all network write operations */
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client, slock, false);
|
||||
client->handle->disconnect_client(client, slock, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -470,7 +497,7 @@ void LocalFileTransfer::callback_transfer_network_read(int fd, short events, voi
|
||||
if(read == 0) {
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
auto original_state = transfer->state;
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, true);
|
||||
slock.unlock();
|
||||
|
||||
switch(original_state) {
|
||||
@@ -505,7 +532,7 @@ void LocalFileTransfer::callback_transfer_network_read(int fd, short events, voi
|
||||
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
auto original_state = transfer->state;
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, true);
|
||||
slock.unlock();
|
||||
|
||||
switch(original_state) {
|
||||
@@ -580,44 +607,20 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
if((unsigned) events & (unsigned) EV_TIMEOUT) {
|
||||
if(transfer->state == FileClient::STATE_DISCONNECTING) {
|
||||
{
|
||||
std::lock_guard nb_lock{transfer->network_buffer.mutex};
|
||||
std::lock_guard db_lock{transfer->disk_buffer.mutex};
|
||||
if(!std::exchange(transfer->network_buffer.flushed, true)) {
|
||||
std::unique_lock nb_lock{transfer->network_buffer.mutex};
|
||||
if(transfer->network_buffer.bytes > 0) {
|
||||
nb_lock.unlock();
|
||||
transfer->flush_network_buffer();
|
||||
debugMessage(LOG_FT, "{} Failed to flush networking buffer in given timeout. Marking it as flushed.", transfer->log_prefix());
|
||||
}
|
||||
if(!transfer->disk_buffer.flushed) {
|
||||
logTrace(LOG_FT, "{} Disk IO hasn't been fully flushed yet, awaiting disk IO flush.", transfer->log_prefix());
|
||||
return;
|
||||
}
|
||||
logTrace(LOG_FT, "{} Disk IO and network buffer have been flushed.", transfer->log_prefix());
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
}
|
||||
transfer->handle->test_disconnecting_state(transfer->shared_from_this());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if((unsigned) events & (unsigned) EV_WRITE) {
|
||||
if(transfer->state != FileClient::STATE_DISCONNECTING && transfer->state != FileClient::STATE_TRANSFERRING) {
|
||||
if(!(transfer->state == FileClient::STATE_AWAITING_KEY && transfer->networking.protocol == FileClient::PROTOCOL_HTTPS)) {
|
||||
debugMessage(LOG_FT, "{} Tried to write data to send only stream. Dropping buffers.", transfer->log_prefix());
|
||||
|
||||
std::unique_lock block{transfer->network_buffer.mutex};
|
||||
auto head = std::exchange(transfer->network_buffer.buffer_head, nullptr);
|
||||
transfer->network_buffer.buffer_tail = &transfer->network_buffer.buffer_head;
|
||||
|
||||
while(head) {
|
||||
auto next = head->next;
|
||||
free_buffer(head);
|
||||
head = next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer* buffer{nullptr};
|
||||
size_t buffer_left_size{0};
|
||||
|
||||
@@ -637,37 +640,30 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
assert(buffer->offset < buffer->length);
|
||||
auto written = ::send(fd, buffer->data + buffer->offset, std::min(buffer->length - buffer->offset, max_write_bytes), MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||
if(written <= 0) {
|
||||
if(transfer->state != FileClient::STATE_TRANSFERRING) {
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
return;
|
||||
if(errno == EAGAIN) {
|
||||
transfer->add_network_write_event(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(written == 0) {
|
||||
/* EOF, how the hell is this event possible?! (Read should already catch it) */
|
||||
logError(LOG_FT, "{} Client disconnected unexpectedly on write. Send {} bytes out of {}.",
|
||||
transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer ? transfer->transfer->expected_file_size - transfer->transfer->file_offset : -1);
|
||||
if(transfer->state == FileClient::STATE_TRANSFERRING) {
|
||||
assert(transfer->transfer);
|
||||
if(written == 0) {
|
||||
/* EOF, how the hell is this event possible?! (Read should already catch it) */
|
||||
logError(LOG_FT, "{} Client disconnected unexpectedly on write. Send {} bytes out of {}.",
|
||||
transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer ? transfer->transfer->expected_file_size - transfer->transfer->file_offset : -1);
|
||||
|
||||
transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::UNEXPECTED_CLIENT_DISCONNECT, "" });
|
||||
{
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
}
|
||||
} else {
|
||||
if(errno == EAGAIN) {
|
||||
transfer->add_network_write_event(false);
|
||||
break;
|
||||
}
|
||||
transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::UNEXPECTED_CLIENT_DISCONNECT, "" });
|
||||
} else {
|
||||
logError(LOG_FT, "{} Received network write error. Send {} bytes out of {}. Closing transfer.",
|
||||
transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer ? transfer->transfer->expected_file_size - transfer->transfer->file_offset : -1);
|
||||
|
||||
logError(LOG_FT, "{} Received network write error. Send {} bytes out of {}. Closing transfer.",
|
||||
transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer ? transfer->transfer->expected_file_size - transfer->transfer->file_offset : -1);
|
||||
|
||||
transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::NETWORK_IO_ERROR, strerror(errno) });
|
||||
{
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
transfer->handle->invoke_aborted_callback(transfer->shared_from_this(), { TransferError::NETWORK_IO_ERROR, strerror(errno) });
|
||||
}
|
||||
}
|
||||
|
||||
transfer->flush_network_buffer(); /* invalidate all network write operations */
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, true);
|
||||
return;
|
||||
} else {
|
||||
buffer->offset += written;
|
||||
@@ -699,22 +695,10 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer_left_size > 0)
|
||||
if(buffer_left_size > 0) {
|
||||
transfer->add_network_write_event(false);
|
||||
else if(transfer->state == FileClient::STATE_DISCONNECTING) {
|
||||
{
|
||||
std::lock_guard nb_lock{transfer->network_buffer.mutex};
|
||||
std::lock_guard db_lock{transfer->disk_buffer.mutex};
|
||||
if(std::exchange(transfer->network_buffer.flushed, true))
|
||||
return;
|
||||
|
||||
if(!transfer->disk_buffer.flushed) {
|
||||
debugMessage(LOG_FT, "{} Disk IO hasn't been fully flushed yet, awaiting disk IO flush.", transfer->log_prefix());
|
||||
return;
|
||||
}
|
||||
|
||||
debugMessage(LOG_FT, "{} Disk IO and network buffer have been flushed.", transfer->log_prefix());
|
||||
}
|
||||
} else if(transfer->state == FileClient::STATE_DISCONNECTING) {
|
||||
transfer->handle->test_disconnecting_state(transfer->shared_from_this());
|
||||
|
||||
if(!std::exchange(transfer->finished_signal_send, true)) {
|
||||
if(transfer->transfer && transfer->statistics.file_transferred.total_bytes + transfer->transfer->file_offset == transfer->transfer->expected_file_size) {
|
||||
@@ -724,7 +708,9 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
callback(transfer->transfer);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
/* no need to flush here, since we read only from the disk and all bytes which sould be send have been written already */
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
return;
|
||||
}
|
||||
@@ -753,7 +739,7 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
logWarning(LOG_FT, "{} Read bytes with unknown protocol. Closing connection.", client->log_prefix());
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, false);
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
@@ -761,7 +747,7 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
logWarning(LOG_FT, "{} Read bytes with unknown protocol but having not awaiting key state. Closing connection.", client->log_prefix());
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, false);
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
@@ -831,7 +817,7 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, false);
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
@@ -871,9 +857,9 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
if(!http::parse_request(std::string{header_view.data(), header_end}, request)) {
|
||||
logError(LOG_FT, "{} Failed to parse HTTP request. Disconnecting client.", client->log_prefix());
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return (size_t) -1;
|
||||
response.code = http::code::code(400, "Bad Request");
|
||||
response.setHeader("x-error-message", { "failed to parse http request" });
|
||||
goto send_response_exit;
|
||||
}
|
||||
|
||||
if(auto header = request.findHeader("Sec-Fetch-Mode"); request.method == "OPTIONS") {
|
||||
|
||||
Reference in New Issue
Block a user