Some bug fixes and final version before release
This commit is contained in:
@@ -7,8 +7,11 @@
|
||||
#include <netinet/tcp.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/net.h>
|
||||
#include <misc/base64.h>
|
||||
#include <include/files/Config.h>
|
||||
#include "./LocalFileProvider.h"
|
||||
#include "./duration_utils.h"
|
||||
#include "HTTPUtils.h"
|
||||
|
||||
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
|
||||
#define TCP_NOPUSH TCP_CORK
|
||||
@@ -88,8 +91,6 @@ void FileClient::add_network_write_event_nolock(bool ignore_bandwidth) {
|
||||
return;
|
||||
|
||||
case STATE_DISCONNECTING:
|
||||
if(this->transfer && this->transfer->direction == Transfer::DIRECTION_UPLOAD)
|
||||
return;
|
||||
/* flush our write buffer */
|
||||
break;
|
||||
|
||||
@@ -113,16 +114,16 @@ void FileClient::add_network_write_event_nolock(bool ignore_bandwidth) {
|
||||
|
||||
bool FileClient::send_file_bytes(const void *snd_buffer, size_t size) {
|
||||
if(this->networking.protocol == FileClient::PROTOCOL_TS_V1) {
|
||||
return this->enqueue_buffer_bytes(snd_buffer, size);
|
||||
return this->enqueue_network_buffer_bytes(snd_buffer, size);
|
||||
} else if(this->networking.protocol == FileClient::PROTOCOL_HTTPS) {
|
||||
this->networking.pipe_ssl.send(pipes::buffer_view{snd_buffer, size});
|
||||
return this->buffer.bytes > TRANSFER_MAX_CACHED_BYTES;
|
||||
return this->network_buffer.bytes > TRANSFER_MAX_CACHED_BYTES;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileClient::enqueue_buffer_bytes(const void *snd_buffer, size_t size) {
|
||||
bool FileClient::enqueue_network_buffer_bytes(const void *snd_buffer, size_t size) {
|
||||
auto tbuffer = allocate_buffer(size);
|
||||
tbuffer->length = size;
|
||||
tbuffer->offset = 0;
|
||||
@@ -130,11 +131,11 @@ bool FileClient::enqueue_buffer_bytes(const void *snd_buffer, size_t size) {
|
||||
|
||||
size_t buffer_size;
|
||||
{
|
||||
std::lock_guard block{this->buffer.mutex};
|
||||
*this->buffer.buffer_tail = tbuffer;
|
||||
this->buffer.buffer_tail = &tbuffer->next;
|
||||
std::lock_guard block{this->network_buffer.mutex};
|
||||
*this->network_buffer.buffer_tail = tbuffer;
|
||||
this->network_buffer.buffer_tail = &tbuffer->next;
|
||||
|
||||
buffer_size = (this->buffer.bytes += size);
|
||||
buffer_size = (this->network_buffer.bytes += size);
|
||||
}
|
||||
|
||||
this->add_network_write_event(false);
|
||||
@@ -341,11 +342,21 @@ bool LocalFileTransfer::initialize_client_ssl(const std::shared_ptr<FileClient>
|
||||
|
||||
auto& ssl_pipe = client->networking.pipe_ssl;
|
||||
|
||||
if(!ssl_pipe.initialize(this->ssl_options_, error)) {
|
||||
std::shared_ptr<pipes::SSL::Options> options{};
|
||||
auto ssl_option_supplier = config::ssl_option_supplier;
|
||||
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());
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client, slock, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ssl_pipe.initialize(options, error)) {
|
||||
logWarning(0, "{} Failed to initialize client SSL pipe ({}). Disconnecting client.", client->log_prefix(), error);
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, false);
|
||||
client->handle->disconnect_client(client, slock, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -369,11 +380,11 @@ bool LocalFileTransfer::initialize_client_ssl(const std::shared_ptr<FileClient>
|
||||
logMessage(LOG_FT, "{} Received SSL error ({}/{}). Closing connection.", client->log_prefix(), error, error_detail);
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, false);
|
||||
client->handle->disconnect_client(client, slock, false);
|
||||
});
|
||||
|
||||
ssl_pipe.callback_write([client](const pipes::buffer_view& buffer) {
|
||||
client->enqueue_buffer_bytes(buffer.data_ptr(), buffer.length());
|
||||
client->enqueue_network_buffer_bytes(buffer.data_ptr(), buffer.length());
|
||||
client->add_network_write_event(false);
|
||||
});
|
||||
|
||||
@@ -549,7 +560,7 @@ void LocalFileTransfer::callback_transfer_network_read(int fd, short events, voi
|
||||
break;
|
||||
|
||||
if(bytes_buffered > TRANSFER_MAX_CACHED_BYTES) {
|
||||
transfer->buffer.buffering_stopped = true;
|
||||
transfer->network_buffer.buffering_stopped = true;
|
||||
debugMessage(LOG_FT, "{} Stopping network read, temp buffer full.", transfer->log_prefix());
|
||||
return; /* no read event readd, buffer filled */
|
||||
}
|
||||
@@ -568,12 +579,23 @@ 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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
logMessage(LOG_FT, "{} Flush timeout. Force closing connection.", transfer->log_prefix());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -583,9 +605,9 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
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->buffer.mutex};
|
||||
auto head = std::exchange(transfer->buffer.buffer_head, nullptr);
|
||||
transfer->buffer.buffer_tail = &transfer->buffer.buffer_head;
|
||||
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;
|
||||
@@ -601,9 +623,9 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
|
||||
while(true) {
|
||||
{
|
||||
std::lock_guard block{transfer->buffer.mutex};
|
||||
buffer = transfer->buffer.buffer_head;
|
||||
buffer_left_size = transfer->buffer.bytes;
|
||||
std::lock_guard block{transfer->network_buffer.mutex};
|
||||
buffer = transfer->network_buffer.buffer_head;
|
||||
buffer_left_size = transfer->network_buffer.bytes;
|
||||
}
|
||||
if(!buffer) {
|
||||
break;
|
||||
@@ -618,7 +640,7 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
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->expected_file_size - transfer->transfer->file_offset);
|
||||
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, "" });
|
||||
{
|
||||
@@ -632,7 +654,7 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
}
|
||||
|
||||
logError(LOG_FT, "{} Received network write error. Send {} bytes out of {}. Closing transfer.",
|
||||
transfer->log_prefix(), transfer->statistics.file_transferred.total_bytes, transfer->transfer->expected_file_size - transfer->transfer->file_offset);
|
||||
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) });
|
||||
{
|
||||
@@ -646,21 +668,21 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
assert(buffer->offset <= buffer->length);
|
||||
if(buffer->length == buffer->offset) {
|
||||
{
|
||||
std::lock_guard block{transfer->buffer.mutex};
|
||||
transfer->buffer.buffer_head = buffer->next;
|
||||
std::lock_guard block{transfer->network_buffer.mutex};
|
||||
transfer->network_buffer.buffer_head = buffer->next;
|
||||
if(!buffer->next)
|
||||
transfer->buffer.buffer_tail = &transfer->buffer.buffer_head;
|
||||
assert(transfer->buffer.bytes >= written);
|
||||
transfer->buffer.bytes -= written;
|
||||
buffer_left_size = transfer->buffer.bytes;
|
||||
transfer->network_buffer.buffer_tail = &transfer->network_buffer.buffer_head;
|
||||
assert(transfer->network_buffer.bytes >= written);
|
||||
transfer->network_buffer.bytes -= written;
|
||||
buffer_left_size = transfer->network_buffer.bytes;
|
||||
}
|
||||
|
||||
free_buffer(buffer);
|
||||
} else {
|
||||
std::lock_guard block{transfer->buffer.mutex};
|
||||
assert(transfer->buffer.bytes >= written);
|
||||
transfer->buffer.bytes -= written;
|
||||
buffer_left_size = transfer->buffer.bytes;
|
||||
std::lock_guard block{transfer->network_buffer.mutex};
|
||||
assert(transfer->network_buffer.bytes >= written);
|
||||
transfer->network_buffer.bytes -= written;
|
||||
buffer_left_size = transfer->network_buffer.bytes;
|
||||
}
|
||||
|
||||
transfer->timings.last_write = std::chrono::system_clock::now();
|
||||
@@ -674,13 +696,27 @@ void LocalFileTransfer::callback_transfer_network_write(int fd, short events, vo
|
||||
if(buffer_left_size > 0)
|
||||
transfer->add_network_write_event(false);
|
||||
else if(transfer->state == FileClient::STATE_DISCONNECTING) {
|
||||
if(transfer->transfer && transfer->statistics.file_transferred.total_bytes + transfer->transfer->file_offset == transfer->transfer->expected_file_size) {
|
||||
logMessage(LOG_FT, "{} Finished file transfer within {}. Closing connection.", transfer->log_prefix(), duration_to_string(std::chrono::system_clock::now() - transfer->timings.key_received));
|
||||
transfer->handle->report_transfer_statistics(transfer->shared_from_this());
|
||||
if(auto callback{transfer->handle->callback_transfer_finished}; callback)
|
||||
callback(transfer->transfer);
|
||||
} else {
|
||||
debugMessage(LOG_FT, "{} Flushed output buffer.", transfer->log_prefix());
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
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) {
|
||||
logMessage(LOG_FT, "{} Finished file transfer within {}. Closing connection.", transfer->log_prefix(), duration_to_string(std::chrono::system_clock::now() - transfer->timings.key_received));
|
||||
transfer->handle->report_transfer_statistics(transfer->shared_from_this());
|
||||
if(auto callback{transfer->handle->callback_transfer_finished}; callback)
|
||||
callback(transfer->transfer);
|
||||
}
|
||||
}
|
||||
std::unique_lock slock{transfer->state_mutex};
|
||||
transfer->handle->disconnect_client(transfer->shared_from_this(), slock, false);
|
||||
@@ -705,7 +741,7 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
return this->handle_transfer_read(client, buffer, length);
|
||||
} else if(client->networking.protocol == FileClient::PROTOCOL_HTTPS) {
|
||||
client->networking.pipe_ssl.process_incoming_data(pipes::buffer_view{buffer, length});
|
||||
return client->buffer.bytes;
|
||||
return client->network_buffer.bytes;
|
||||
} else if(client->networking.protocol != FileClient::PROTOCOL_UNKNOWN) {
|
||||
assert(false);
|
||||
logWarning(LOG_FT, "{} Read bytes with unknown protocol. Closing connection.", client->log_prefix());
|
||||
@@ -751,7 +787,7 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
client->networking.pipe_ssl.process_incoming_data(pipes::buffer_view{first_bytes, TRANSFER_KEY_LENGTH});
|
||||
if(length > 0)
|
||||
client->networking.pipe_ssl.process_incoming_data(pipes::buffer_view{buffer, length});
|
||||
return client->buffer.bytes;
|
||||
return client->network_buffer.bytes;
|
||||
} else {
|
||||
client->networking.protocol = FileClient::PROTOCOL_TS_V1;
|
||||
debugMessage(LOG_FT, "{} Using protocol RAWv1 for file transfer.", client->log_prefix());
|
||||
@@ -760,8 +796,12 @@ size_t LocalFileTransfer::handle_transfer_read_raw(const std::shared_ptr<FileCli
|
||||
auto key_result = this->handle_transfer_key_provided(client, error_detail);
|
||||
switch(key_result) {
|
||||
case TransferKeyApplyResult::SUCCESS:
|
||||
if(client->transfer->direction == Transfer::DIRECTION_DOWNLOAD)
|
||||
if(client->transfer->direction == Transfer::DIRECTION_DOWNLOAD) {
|
||||
logMessage(LOG_FT, "{} Successfully initialized file download for file {}.", client->log_prefix(), client->transfer->absolute_file_path);
|
||||
this->enqueue_disk_io(client); /* we've to take initiative */
|
||||
} else {
|
||||
logMessage(LOG_FT, "{} Successfully upload file download to file {}.", client->log_prefix(), client->transfer->absolute_file_path);
|
||||
}
|
||||
|
||||
return length ? this->handle_transfer_read(client, buffer, length) : 0;
|
||||
|
||||
@@ -830,28 +870,49 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
const auto transfer_key_header = request.findHeader("transfer-key");
|
||||
if(!transfer_key_header || transfer_key_header.values.empty()) {
|
||||
logMessage(0, "{} Missing transfer key header. Disconnecting client.", client->log_prefix());
|
||||
response.code = http::code::code(510, "Not Extended");
|
||||
if(auto header = request.findHeader("Sec-Fetch-Mode"); request.method == "OPTIONS") {
|
||||
logMessage(0, "{} Received options request (probably due to cors). Sending allow response and disconnecting client.", client->log_prefix());
|
||||
response.code = http::code::_200;
|
||||
goto send_response_exit;
|
||||
}
|
||||
|
||||
#if 0
|
||||
std::map<std::string, std::string> query{};
|
||||
if(!http::parse_url_parameters(request.url, query)) {
|
||||
logMessage(0, "{} Received request but missing URL parameters ({})", client->log_prefix(), request.url);
|
||||
response.code = http::code::code(400, "Bad Request");
|
||||
goto send_response_exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string transfer_key{};
|
||||
if(request.parameters.count("transfer-key") == 0 || (transfer_key = request.parameters.at("transfer-key")).empty()) {
|
||||
logMessage(0, "{} Missing transfer key parameter. Disconnecting client.", client->log_prefix());
|
||||
response.code = http::code::code(400, "Bad Request");
|
||||
response.setHeader("x-error-message", { "missing transfer key" });
|
||||
goto send_response_exit;
|
||||
}
|
||||
|
||||
const auto& transfer_key = transfer_key_header.values[0];
|
||||
if(transfer_key.length() != TRANSFER_KEY_LENGTH) {
|
||||
logMessage(0, "{} Received too short/long transfer key. Expected {} but received {}. Disconnecting client.", client->log_prefix(), TRANSFER_KEY_LENGTH, transfer_key.length());
|
||||
response.code = http::code::code(510, "Not Extended");
|
||||
response.code = http::code::code(400, "Bad Request");
|
||||
response.setHeader("x-error-message", { "key too short/long" });
|
||||
goto send_response_exit;
|
||||
}
|
||||
client->transfer_key.provided_bytes = TRANSFER_KEY_LENGTH;
|
||||
memcpy(client->transfer_key.key, transfer_key.data(), TRANSFER_KEY_LENGTH);
|
||||
|
||||
client->file.query_media_bytes = true;
|
||||
|
||||
std::string error_detail{};
|
||||
auto key_result = this->handle_transfer_key_provided(client, error_detail);
|
||||
switch(key_result) {
|
||||
case TransferKeyApplyResult::SUCCESS:
|
||||
if(client->transfer->direction == Transfer::DIRECTION_DOWNLOAD) {
|
||||
logMessage(LOG_FT, "{} Successfully initialized file download for file {}.", client->log_prefix(), client->transfer->absolute_file_path);
|
||||
} else {
|
||||
logMessage(LOG_FT, "{} Successfully upload file download to file {}.", client->log_prefix(), client->transfer->absolute_file_path);
|
||||
}
|
||||
break;
|
||||
|
||||
case TransferKeyApplyResult::FILE_ERROR:
|
||||
@@ -885,25 +946,18 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
|
||||
response.setHeader("Content-type", {"application/octet-stream; "});
|
||||
response.setHeader("Content-Transfer-Encoding", {"binary"});
|
||||
|
||||
response.setHeader("Content-Disposition", {
|
||||
"attachment; filename=\"" + http::encode_url(download_name && !download_name.values.empty() ? download_name.values[0] : "TeaWeb Download") + "\""
|
||||
"attachment; filename=\"" + http::encode_url(request.parameters.count("download-name") > 0 ? request.parameters.at("download-name") : client->transfer->file_name) + "\""
|
||||
});
|
||||
|
||||
/* TODO: X-media-bytes */
|
||||
#if 0
|
||||
if(this->pendingKey->size > 1) {
|
||||
char header_buffer[64];
|
||||
auto read = fstream->readsome(header_buffer, 64);
|
||||
if(read > 0)
|
||||
response.setHeader("X-media-bytes", {base64::encode(header_buffer, read)});
|
||||
fstream->seekg(this->pendingKey->offset, std::ios::beg);
|
||||
}
|
||||
#endif
|
||||
response.setHeader("X-media-bytes", { base64::encode((char*) client->file.media_bytes, client->file.media_bytes_length) });
|
||||
client->networking.http_state = FileClient::HTTP_STATE_DOWNLOADING;
|
||||
goto send_response_exit;
|
||||
} else {
|
||||
response.setHeader("Content-Length", { std::to_string(client->transfer->expected_file_size) });
|
||||
/* we're sending a HTTP response later */
|
||||
client->networking.http_state = FileClient::HTTP_STATE_AWAITING_BOUNDARY;
|
||||
goto initialize_exit;
|
||||
}
|
||||
|
||||
overhead_length = header_buffer->offset - header_end - header_end_token.length();
|
||||
@@ -912,12 +966,13 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
|
||||
send_response_exit:
|
||||
this->send_http_response(client, response);
|
||||
if(response.code->code != 200) {
|
||||
if(response.code->code != 200 || !client->transfer) {
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
initialize_exit:
|
||||
if(client->transfer->direction == Transfer::DIRECTION_DOWNLOAD)
|
||||
this->enqueue_disk_io(client); /* we've to take initiative */
|
||||
|
||||
@@ -941,17 +996,22 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
std::string error_message{};
|
||||
const auto upload_result = client->handle->handle_transfer_upload_http(client, buffer, length);
|
||||
switch(upload_result) {
|
||||
case TransferUploadHTTPResult::FINISH: {this->report_transfer_statistics(client);
|
||||
case TransferUploadHTTPResult::FINISH: {
|
||||
assert(!client->finished_signal_send); /* we should be faster than the networking flush */
|
||||
client->finished_signal_send = true;
|
||||
logMessage(LOG_FT, "{} File upload has been completed in {}. Disconnecting client.", client->log_prefix(), duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
|
||||
this->report_transfer_statistics(client);
|
||||
if(auto callback{this->callback_transfer_finished}; callback)
|
||||
callback(client->transfer);
|
||||
|
||||
std::unique_lock slock{client->state_mutex};
|
||||
client->handle->disconnect_client(client->shared_from_this(), slock, true);
|
||||
return client->buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
return client->network_buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
}
|
||||
|
||||
case TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE:
|
||||
return client->buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
return client->network_buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
|
||||
case TransferUploadHTTPResult::MISSING_CONTENT_TYPE:
|
||||
logMessage(LOG_FT, "{} Missing boundary content type. Disconnecting client.", client->log_prefix());
|
||||
@@ -990,10 +1050,19 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
|
||||
return (size_t) -1;
|
||||
} else if(client->networking.protocol == FileClient::PROTOCOL_TS_V1) {
|
||||
auto result = this->handle_transfer_upload_raw(client, buffer, length);
|
||||
size_t written_bytes{0};
|
||||
auto result = this->handle_transfer_upload_raw(client, buffer, length, &written_bytes);
|
||||
|
||||
switch (result) {
|
||||
case TransferUploadRawResult::FINISH: {this->report_transfer_statistics(client);
|
||||
case TransferUploadRawResult::FINISH_OVERFLOW:
|
||||
case TransferUploadRawResult::FINISH: {
|
||||
assert(!client->finished_signal_send); /* we should be faster than the networking flush */
|
||||
client->finished_signal_send = true;
|
||||
if(result == TransferUploadRawResult::FINISH_OVERFLOW)
|
||||
logMessage(LOG_FT, "{} Client send {} too many bytes (Transfer length was {}). Dropping them, flushing the disk IO and closing the transfer.", client->log_prefix(), length - written_bytes, duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
else
|
||||
logMessage(LOG_FT, "{} File upload has been completed in {}. Flushing disk IO and closing the connection.", client->log_prefix(), duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
this->report_transfer_statistics(client);
|
||||
if(auto callback{this->callback_transfer_finished}; callback)
|
||||
callback(client->transfer);
|
||||
|
||||
@@ -1003,7 +1072,7 @@ size_t LocalFileTransfer::handle_transfer_read(const std::shared_ptr<FileClient>
|
||||
}
|
||||
|
||||
case TransferUploadRawResult::MORE_DATA_TO_RECEIVE:
|
||||
return client->buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
return client->network_buffer.bytes; /* a bit unexact but the best we could get away with it */
|
||||
}
|
||||
} else {
|
||||
logWarning(LOG_FT, "{} Read message for client with unknown protocol. Dropping {} bytes.", client->log_prefix(), length);
|
||||
@@ -1069,36 +1138,25 @@ TransferKeyApplyResult LocalFileTransfer::handle_transfer_key_provided(const std
|
||||
return TransferKeyApplyResult::SUCCESS;
|
||||
}
|
||||
|
||||
TransferUploadRawResult LocalFileTransfer::handle_transfer_upload_raw(const std::shared_ptr<FileClient> &client, const char *buffer, size_t length) {
|
||||
client->statistics.file_transferred.increase_bytes(length);
|
||||
TransferUploadRawResult LocalFileTransfer::handle_transfer_upload_raw(const std::shared_ptr<FileClient> &client, const char *buffer, size_t length, size_t* bytesWritten) {
|
||||
auto write_length = length;
|
||||
auto write_offset = client->statistics.file_transferred.total_bytes + client->transfer->file_offset;
|
||||
TransferUploadRawResult result{TransferUploadRawResult::MORE_DATA_TO_RECEIVE};
|
||||
|
||||
bool transfer_finished{false};
|
||||
auto writte_offset = client->statistics.file_transferred.total_bytes + client->transfer->file_offset;
|
||||
if(writte_offset > client->transfer->expected_file_size) {
|
||||
logMessage(LOG_FT, "{} Client send {} too many bytes (Transfer length was {}). Dropping them, flushing the disk IO and closing the transfer.", client->log_prefix(), writte_offset - client->transfer->expected_file_size, duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
length -= writte_offset - client->transfer->expected_file_size;
|
||||
|
||||
transfer_finished = true;
|
||||
} else if(writte_offset == client->transfer->expected_file_size) {
|
||||
logMessage(LOG_FT, "{} File upload has been completed in {}. Flushing disk IO and closing the connection.", client->log_prefix(), duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
transfer_finished = true;
|
||||
}
|
||||
|
||||
auto tbuffer = allocate_buffer(length);
|
||||
tbuffer->offset = 0;
|
||||
tbuffer->length = length;
|
||||
memcpy(tbuffer->data, buffer, length);
|
||||
|
||||
{
|
||||
std::lock_guard block{client->buffer.mutex};
|
||||
*client->buffer.buffer_tail = tbuffer;
|
||||
client->buffer.buffer_tail = &tbuffer->next;
|
||||
client->buffer.bytes += length;
|
||||
if(write_offset + write_length > client->transfer->expected_file_size) {
|
||||
result = TransferUploadRawResult::FINISH_OVERFLOW;
|
||||
write_length = client->transfer->expected_file_size - write_offset;
|
||||
} else if(write_offset == client->transfer->expected_file_size) {
|
||||
result = TransferUploadRawResult::FINISH;
|
||||
}
|
||||
|
||||
client->statistics.file_transferred.increase_bytes(write_length);
|
||||
client->enqueue_disk_buffer_bytes(buffer, write_length);
|
||||
this->enqueue_disk_io(client);
|
||||
if(bytesWritten)
|
||||
*bytesWritten = write_length;
|
||||
|
||||
return transfer_finished ? TransferUploadRawResult::FINISH : TransferUploadRawResult::MORE_DATA_TO_RECEIVE;
|
||||
return result;
|
||||
}
|
||||
|
||||
//Example boundary:
|
||||
@@ -1120,19 +1178,21 @@ TransferUploadHTTPResult LocalFileTransfer::handle_transfer_upload_http(const st
|
||||
memcpy(boundary_buffer->data + boundary_buffer->offset, buffer, length);
|
||||
boundary_buffer->offset += length;
|
||||
|
||||
auto boundary_view = std::string_view{boundary_buffer->data, boundary_buffer->offset};
|
||||
auto boundary_end = boundary_view.find(boundary_end_token, old_offset > 3 ? old_offset - 3 : 0);
|
||||
auto buffer_view = std::string_view{boundary_buffer->data, boundary_buffer->offset};
|
||||
auto boundary_end = buffer_view.find(boundary_end_token, old_offset > 3 ? old_offset - 3 : 0);
|
||||
if(boundary_end == std::string::npos)
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
|
||||
auto boundary_token_end = boundary_view.find(boundary_token_end_token);
|
||||
if(boundary_token_end >= boundary_end)
|
||||
auto boundary_token_end = buffer_view.find(boundary_token_end_token);
|
||||
if(boundary_token_end + boundary_token_end_token.size() >= boundary_end)
|
||||
return TransferUploadHTTPResult::BOUNDARY_TOKEN_INVALID;
|
||||
|
||||
const auto boundary_token = boundary_view.substr(0, boundary_token_end);
|
||||
debugMessage(LOG_FT, "{} Received clients HTTP file boundary ({}).", client->log_prefix(), boundary_token);
|
||||
const auto boundary_token = buffer_view.substr(0, boundary_token_end);
|
||||
client->networking.http_boundary = boundary_token;
|
||||
logTrace(LOG_FT, "{} Received clients HTTP file boundary ({}).", client->log_prefix(), boundary_token);
|
||||
|
||||
const auto boundary_payload = boundary_view.substr(boundary_token_end + boundary_token_end_token.size());
|
||||
const auto boundary_header_offset = boundary_token_end + boundary_token_end_token.size();
|
||||
const auto boundary_payload = buffer_view.substr(boundary_header_offset, boundary_end - boundary_header_offset);
|
||||
|
||||
http::HttpRequest boundary{};
|
||||
if(!http::parse_request(std::string{boundary_payload}, boundary))
|
||||
@@ -1141,8 +1201,10 @@ TransferUploadHTTPResult LocalFileTransfer::handle_transfer_upload_http(const st
|
||||
const auto content_type = boundary.findHeader("Content-Type");
|
||||
if(!content_type || content_type.values.empty())
|
||||
return TransferUploadHTTPResult::MISSING_CONTENT_TYPE;
|
||||
/* A bit relaxed here
|
||||
else if(content_type.values[0] != "application/octet-stream")
|
||||
return TransferUploadHTTPResult::INVALID_CONTENT_TYPE;
|
||||
*/
|
||||
|
||||
const auto overhead_length = boundary_buffer->offset - boundary_end - boundary_end_token.length();
|
||||
const auto overhead_data_ptr = boundary_buffer->data + boundary_end + boundary_end_token.length();
|
||||
@@ -1151,19 +1213,74 @@ TransferUploadHTTPResult LocalFileTransfer::handle_transfer_upload_http(const st
|
||||
boundary_buffer->offset = 0;
|
||||
return overhead_length == 0 ? TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE : this->handle_transfer_upload_http(client, overhead_data_ptr, overhead_length);
|
||||
} else if(client->networking.http_state == FileClient::HTTP_STATE_UPLOADING) {
|
||||
auto result = this->handle_transfer_upload_raw(client, buffer, length);
|
||||
size_t bytes_written{0};
|
||||
auto result = this->handle_transfer_upload_raw(client, buffer, length, &bytes_written);
|
||||
switch(result) {
|
||||
case TransferUploadRawResult::MORE_DATA_TO_RECEIVE:
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
|
||||
case TransferUploadRawResult::FINISH_OVERFLOW:
|
||||
case TransferUploadRawResult::FINISH:
|
||||
/* TODO: Try to read the end boundary! */
|
||||
return TransferUploadHTTPResult::FINISH;
|
||||
debugMessage(LOG_FT, "{} File upload has been completed in {}. Awaiting file end boundary.", client->log_prefix(), duration_to_string(std::chrono::system_clock::now() - client->timings.key_received));
|
||||
client->networking.http_state = FileClient::HTTP_STATE_AWAITING_BOUNDARY_END;
|
||||
|
||||
if(length != bytes_written)
|
||||
return this->handle_transfer_upload_http(client, buffer + bytes_written, length - bytes_written);
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
}
|
||||
} else if(client->networking.http_state == FileClient::HTTP_STATE_AWAITING_BOUNDARY_END) {
|
||||
assert(client->networking.http_header_buffer);
|
||||
|
||||
/* Notice: The buffer ptr might be some data within our header buffer! But since its somewhere in the back its okey */
|
||||
auto boundary_buffer = &*client->networking.http_header_buffer;
|
||||
if(boundary_buffer->offset + length > boundary_buffer->capacity)
|
||||
return TransferUploadHTTPResult::BOUNDARY_MISSING;
|
||||
|
||||
memcpy(boundary_buffer->data + boundary_buffer->offset, buffer, length);
|
||||
boundary_buffer->offset += length;
|
||||
|
||||
const auto expected_boundary_size = 2 + client->networking.http_boundary.size() + 4;
|
||||
if(boundary_buffer->offset < expected_boundary_size)
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
|
||||
if(memcmp(boundary_buffer->data, "\r\n", 2) != 0) {
|
||||
debugMessage(LOG_FT, "{} File boundary seems invalid/miss matching. Expected \\r\\n at {}.", client->log_prefix(), 0);
|
||||
goto callback_exit;
|
||||
}
|
||||
|
||||
if(memcmp(boundary_buffer->data + 2 + client->networking.http_boundary.size(), "--\r\n", 4) != 0) {
|
||||
debugMessage(LOG_FT, "{} File boundary seems invalid/miss matching. Expected --\\r\\n at {}.", client->log_prefix(), 2 + client->networking.http_boundary.size());
|
||||
goto callback_exit;
|
||||
}
|
||||
|
||||
if(memcmp(boundary_buffer->data + 2, client->networking.http_boundary.data(), client->networking.http_boundary.size()) != 0) {
|
||||
debugMessage(LOG_FT, "{} File upload has been completed but end boundary does not match ({} != {})! Ignoring miss match and finishing transfer", client->log_prefix(),
|
||||
std::string_view{boundary_buffer->data + 2, client->networking.http_boundary.size()},
|
||||
client->networking.http_boundary
|
||||
);
|
||||
|
||||
goto callback_exit;
|
||||
}
|
||||
|
||||
if(boundary_buffer->offset > expected_boundary_size) {
|
||||
debugMessage(LOG_FT, "{} File boundary has been received, but received {} more unexpected bytes. Ignoring these and finishing the upload.",
|
||||
client->log_prefix(), boundary_buffer->offset - expected_boundary_size);
|
||||
} else {
|
||||
debugMessage(LOG_FT, "{} File boundary has been received.", client->log_prefix());
|
||||
}
|
||||
|
||||
callback_exit:
|
||||
/* send a proper HTTP response */
|
||||
{
|
||||
http::HttpResponse response{};
|
||||
response.code = http::code::_200;
|
||||
this->send_http_response(client, response);
|
||||
};
|
||||
return TransferUploadHTTPResult::FINISH;
|
||||
} else {
|
||||
logWarning(0, "{} Received HTTP(S) data, for an invalid HTTP state ({}).", client->log_prefix(), (int) client->networking.http_state);
|
||||
return TransferUploadHTTPResult::MORE_DATA_TO_RECEIVE;
|
||||
@@ -1174,7 +1291,12 @@ inline void apply_cors_and_connection_headers(http::HttpResponse &response) {
|
||||
response.setHeader("Connection", {"close"}); /* close the connection instance, we dont want multiple requests */
|
||||
response.setHeader("Access-Control-Allow-Methods", {"GET, POST"});
|
||||
response.setHeader("Access-Control-Allow-Origin", {"*"});
|
||||
response.setHeader("Access-Control-Allow-Headers", response.findHeader("Access-Control-Request-Headers").values); //access-control-allow-headers
|
||||
|
||||
auto requestHeaders = response.findHeader("Access-Control-Request-Headers").values;
|
||||
if(requestHeaders.empty()) {
|
||||
requestHeaders.emplace_back("*");
|
||||
}
|
||||
response.setHeader("Access-Control-Allow-Headers", requestHeaders); //access-control-allow-headers
|
||||
response.setHeader("Access-Control-Max-Age", {"86400"});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user