Some bug fixes and final version before release

This commit is contained in:
WolverinDEV
2020-06-10 18:13:14 +02:00
parent bfdf940dbf
commit 9e964b3ea8
14 changed files with 577 additions and 262 deletions
+227 -105
View File
@@ -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"});
}