librats 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/lib/index.d.ts +9 -22
- package/native-src/CMakeLists.txt +132 -50
- package/native-src/cmake/ratsConfig.cmake.in +8 -0
- package/native-src/src/bt_network.cpp +296 -183
- package/native-src/src/bt_network.h +25 -5
- package/native-src/src/crypto/sha256.c +1 -1
- package/native-src/src/dht.cpp +297 -47
- package/native-src/src/dht.h +70 -6
- package/native-src/src/file_transfer.cpp +1185 -1578
- package/native-src/src/file_transfer.h +240 -521
- package/native-src/src/io_poller.cpp +917 -0
- package/native-src/src/io_poller.h +138 -0
- package/native-src/src/krpc.cpp +161 -96
- package/native-src/src/krpc.h +18 -4
- package/native-src/src/librats.cpp +812 -1236
- package/native-src/src/librats.h +207 -208
- package/native-src/src/librats_bittorrent.cpp +1 -5
- package/native-src/src/librats_c.cpp +22 -39
- package/native-src/src/librats_discovery.cpp +377 -0
- package/native-src/src/librats_encryption.cpp +130 -283
- package/native-src/src/librats_file_transfer.cpp +27 -109
- package/native-src/src/librats_gossipsub.cpp +1 -5
- package/native-src/src/librats_ice.cpp +5 -1
- package/native-src/src/librats_log_macros.h +36 -0
- package/native-src/src/librats_logging.cpp +1 -7
- package/native-src/src/librats_mdns.cpp +1 -11
- package/native-src/src/librats_persistence.cpp +1 -11
- package/native-src/src/librats_reconnection.cpp +2 -13
- package/native-src/src/librats_statistic.cpp +105 -0
- package/native-src/src/socket.cpp +15 -3
- package/package.json +1 -1
- package/scripts/build-librats.js +3 -0
- package/scripts/prepare-package.js +10 -0
- package/src/librats_node.cpp +53 -64
|
@@ -1,567 +1,286 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// File / directory transfer for librats
|
|
5
|
+
// =============================================================================
|
|
6
|
+
//
|
|
7
|
+
// A transfer streams one file or a whole directory tree to a connected peer
|
|
8
|
+
// over the reliable (and, when enabled, encrypted) RatsClient connection.
|
|
9
|
+
//
|
|
10
|
+
// Wire protocol (see file_transfer.cpp for the exact framing):
|
|
11
|
+
// * Control messages travel on the named-message channel as JSON:
|
|
12
|
+
// ft_offer sender -> receiver : manifest of files to be sent
|
|
13
|
+
// ft_response receiver-> sender : accepted / rejected
|
|
14
|
+
// ft_file_end sender -> receiver : SHA-256 of a file once fully streamed
|
|
15
|
+
// ft_progress receiver-> sender : bytes received so far (drives backpressure)
|
|
16
|
+
// ft_complete receiver-> sender : transfer finished (success / failure)
|
|
17
|
+
// ft_control either -> either : pause / resume / cancel
|
|
18
|
+
// * File data travels on the binary channel as self-describing chunk frames.
|
|
19
|
+
//
|
|
20
|
+
// Design notes:
|
|
21
|
+
// * The transport is reliable and ordered, so chunks are streamed strictly
|
|
22
|
+
// sequentially - there is no per-chunk retransmission. Integrity is checked
|
|
23
|
+
// with a per-chunk CRC32 and a whole-file SHA-256.
|
|
24
|
+
// * Backpressure: the sender keeps at most `window_bytes` un-acknowledged and
|
|
25
|
+
// waits for `ft_progress` before sending more, so memory stays bounded.
|
|
26
|
+
// * Received data is written to a temp file and only moved to its final path
|
|
27
|
+
// after the SHA-256 matches, so a failed transfer never leaves a bad file.
|
|
28
|
+
//
|
|
29
|
+
// =============================================================================
|
|
30
|
+
|
|
4
31
|
#include "json.hpp"
|
|
5
|
-
|
|
6
|
-
#include <
|
|
32
|
+
|
|
33
|
+
#include <atomic>
|
|
34
|
+
#include <chrono>
|
|
35
|
+
#include <condition_variable>
|
|
36
|
+
#include <cstdint>
|
|
7
37
|
#include <functional>
|
|
8
38
|
#include <memory>
|
|
9
39
|
#include <mutex>
|
|
10
|
-
#include <unordered_map>
|
|
11
|
-
#include <atomic>
|
|
12
|
-
#include <chrono>
|
|
13
|
-
#include <thread>
|
|
14
40
|
#include <queue>
|
|
15
|
-
#include <
|
|
41
|
+
#include <string>
|
|
42
|
+
#include <thread>
|
|
43
|
+
#include <unordered_map>
|
|
44
|
+
#include <vector>
|
|
16
45
|
|
|
17
46
|
namespace librats {
|
|
18
47
|
|
|
19
|
-
// Forward declaration
|
|
20
48
|
class RatsClient;
|
|
49
|
+
struct Transfer; // internal per-transfer state, defined in file_transfer.cpp
|
|
50
|
+
|
|
51
|
+
// -----------------------------------------------------------------------------
|
|
52
|
+
// Public enums
|
|
53
|
+
// -----------------------------------------------------------------------------
|
|
21
54
|
|
|
22
|
-
/**
|
|
23
|
-
* File transfer status codes
|
|
24
|
-
*/
|
|
25
55
|
enum class FileTransferStatus {
|
|
26
|
-
PENDING,
|
|
27
|
-
STARTING,
|
|
28
|
-
IN_PROGRESS,
|
|
29
|
-
PAUSED,
|
|
30
|
-
COMPLETED,
|
|
31
|
-
FAILED,
|
|
32
|
-
CANCELLED,
|
|
33
|
-
RESUMING
|
|
56
|
+
PENDING, // incoming offer awaiting accept/reject, or outgoing offer awaiting response
|
|
57
|
+
STARTING, // accepted, about to move data
|
|
58
|
+
IN_PROGRESS, // actively transferring
|
|
59
|
+
PAUSED, // paused by either side
|
|
60
|
+
COMPLETED, // finished successfully
|
|
61
|
+
FAILED, // finished with an error
|
|
62
|
+
CANCELLED, // cancelled by either side
|
|
63
|
+
RESUMING // transient state between PAUSED and IN_PROGRESS
|
|
34
64
|
};
|
|
35
65
|
|
|
36
|
-
/**
|
|
37
|
-
* File transfer direction
|
|
38
|
-
*/
|
|
39
66
|
enum class FileTransferDirection {
|
|
40
|
-
SENDING,
|
|
41
|
-
RECEIVING
|
|
67
|
+
SENDING,
|
|
68
|
+
RECEIVING
|
|
42
69
|
};
|
|
43
70
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* File transfer chunk information
|
|
48
|
-
*/
|
|
49
|
-
struct FileChunk {
|
|
50
|
-
std::string transfer_id; // Unique transfer identifier
|
|
51
|
-
uint64_t chunk_index; // Sequential chunk number (0-based)
|
|
52
|
-
uint64_t total_chunks; // Total number of chunks in transfer
|
|
53
|
-
uint64_t chunk_size; // Size of this specific chunk
|
|
54
|
-
uint64_t file_offset; // Offset in the original file
|
|
55
|
-
std::vector<uint8_t> data; // Chunk data payload
|
|
56
|
-
std::string checksum; // SHA256 checksum of chunk data
|
|
57
|
-
|
|
58
|
-
FileChunk() : chunk_index(0), total_chunks(0), chunk_size(0),
|
|
59
|
-
file_offset(0) {}
|
|
60
|
-
};
|
|
71
|
+
// Human-readable name of a status, e.g. "IN_PROGRESS". Never returns null.
|
|
72
|
+
const char* file_transfer_status_name(FileTransferStatus status);
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
std::string checksum; // Full file checksum
|
|
72
|
-
|
|
73
|
-
FileMetadata() : file_size(0), last_modified(0) {}
|
|
74
|
+
// -----------------------------------------------------------------------------
|
|
75
|
+
// Public data structures
|
|
76
|
+
// -----------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
// One file inside a transfer. A single-file transfer has exactly one entry; a
|
|
79
|
+
// directory transfer has one per regular file.
|
|
80
|
+
struct FileInfo {
|
|
81
|
+
std::string relative_path; // POSIX-style path relative to the transfer root
|
|
82
|
+
uint64_t size = 0; // file size in bytes
|
|
74
83
|
};
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
std::string
|
|
81
|
-
std::string
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// Calculate total transfer size
|
|
86
|
-
uint64_t get_total_size() const;
|
|
87
|
-
|
|
88
|
-
// Get total file count
|
|
89
|
-
size_t get_total_file_count() const;
|
|
85
|
+
// Description of an incoming transfer, delivered to the offer callback so the
|
|
86
|
+
// application can decide whether to accept() or reject() it.
|
|
87
|
+
struct IncomingTransferOffer {
|
|
88
|
+
std::string transfer_id;
|
|
89
|
+
std::string peer_id;
|
|
90
|
+
std::string name; // file name, or directory name
|
|
91
|
+
bool is_directory = false;
|
|
92
|
+
uint64_t total_size = 0; // sum of all file sizes
|
|
93
|
+
std::vector<FileInfo> files; // full manifest
|
|
90
94
|
};
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*/
|
|
96
|
+
// Immutable snapshot of a transfer's progress. Returned by queries and passed
|
|
97
|
+
// to the progress callback.
|
|
95
98
|
struct FileTransferProgress {
|
|
96
|
-
std::string transfer_id;
|
|
97
|
-
std::string peer_id;
|
|
98
|
-
FileTransferDirection direction
|
|
99
|
-
FileTransferStatus
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
std::string
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
std::chrono::
|
|
114
|
-
std::chrono::
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Error information
|
|
120
|
-
std::string error_message; // Error details if failed
|
|
121
|
-
uint32_t retry_count; // Number of retries attempted
|
|
122
|
-
|
|
123
|
-
FileTransferProgress() : direction(FileTransferDirection::SENDING),
|
|
124
|
-
status(FileTransferStatus::PENDING),
|
|
125
|
-
file_size(0), bytes_transferred(0), total_bytes(0),
|
|
126
|
-
chunks_completed(0), total_chunks(0),
|
|
127
|
-
transfer_rate_bps(0.0), average_rate_bps(0.0),
|
|
128
|
-
retry_count(0) {
|
|
129
|
-
start_time = std::chrono::steady_clock::now();
|
|
130
|
-
last_update = start_time;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Calculate completion percentage (0.0 to 100.0)
|
|
99
|
+
std::string transfer_id;
|
|
100
|
+
std::string peer_id;
|
|
101
|
+
FileTransferDirection direction = FileTransferDirection::SENDING;
|
|
102
|
+
FileTransferStatus status = FileTransferStatus::PENDING;
|
|
103
|
+
|
|
104
|
+
std::string filename; // file name, or directory name
|
|
105
|
+
std::string local_path; // local source (sending) or destination (receiving)
|
|
106
|
+
bool is_directory = false;
|
|
107
|
+
|
|
108
|
+
uint64_t bytes_transferred = 0;
|
|
109
|
+
uint64_t total_bytes = 0;
|
|
110
|
+
uint32_t files_completed = 0;
|
|
111
|
+
uint32_t total_files = 0;
|
|
112
|
+
|
|
113
|
+
double transfer_rate_bps = 0.0; // recent throughput, bytes/second
|
|
114
|
+
double average_rate_bps = 0.0; // average throughput since start
|
|
115
|
+
|
|
116
|
+
std::chrono::milliseconds elapsed_time{0};
|
|
117
|
+
std::chrono::milliseconds estimated_time_remaining{0};
|
|
118
|
+
|
|
119
|
+
std::string error_message; // populated when status == FAILED
|
|
120
|
+
|
|
121
|
+
// Completion as a percentage in [0, 100].
|
|
134
122
|
double get_completion_percentage() const {
|
|
135
|
-
if (total_bytes == 0)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Calculate elapsed time
|
|
140
|
-
std::chrono::milliseconds get_elapsed_time() const {
|
|
141
|
-
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
142
|
-
std::chrono::steady_clock::now() - start_time);
|
|
123
|
+
if (total_bytes == 0) {
|
|
124
|
+
return status == FileTransferStatus::COMPLETED ? 100.0 : 0.0;
|
|
125
|
+
}
|
|
126
|
+
return (static_cast<double>(bytes_transferred) / static_cast<double>(total_bytes)) * 100.0;
|
|
143
127
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
void update_transfer_rates(uint64_t new_bytes_transferred);
|
|
128
|
+
|
|
129
|
+
std::chrono::milliseconds get_elapsed_time() const { return elapsed_time; }
|
|
147
130
|
};
|
|
148
131
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
132
|
+
// -----------------------------------------------------------------------------
|
|
133
|
+
// Configuration
|
|
134
|
+
// -----------------------------------------------------------------------------
|
|
135
|
+
|
|
152
136
|
struct FileTransferConfig {
|
|
153
|
-
uint32_t chunk_size;
|
|
154
|
-
uint32_t
|
|
155
|
-
uint32_t
|
|
156
|
-
uint32_t
|
|
157
|
-
|
|
158
|
-
bool
|
|
159
|
-
std::string temp_directory
|
|
160
|
-
|
|
161
|
-
FileTransferConfig()
|
|
162
|
-
: chunk_size(65536), // 64KB chunks
|
|
163
|
-
max_concurrent_chunks(4),
|
|
164
|
-
max_retries(3),
|
|
165
|
-
timeout_seconds(30),
|
|
166
|
-
verify_checksums(true),
|
|
167
|
-
allow_resume(true),
|
|
168
|
-
temp_directory("./temp_transfers") {}
|
|
137
|
+
uint32_t chunk_size = 64 * 1024; // payload bytes per network chunk
|
|
138
|
+
uint32_t window_bytes = 4 * 1024 * 1024; // max un-acknowledged bytes in flight
|
|
139
|
+
uint32_t progress_interval = 256 * 1024; // receiver sends an ack every N bytes
|
|
140
|
+
uint32_t transfer_timeout_secs = 60; // abort a transfer idle for this long
|
|
141
|
+
uint32_t worker_threads = 4; // concurrent outgoing transfers
|
|
142
|
+
bool verify_integrity = true; // per-chunk CRC32 + whole-file SHA-256
|
|
143
|
+
std::string temp_directory = "./rats_file_transfers"; // holds in-progress downloads
|
|
169
144
|
};
|
|
170
145
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
using
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
146
|
+
// -----------------------------------------------------------------------------
|
|
147
|
+
// Callback types
|
|
148
|
+
// -----------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
// Invoked when a peer offers a transfer. The handler should eventually call
|
|
151
|
+
// accept()/reject() (it may do so synchronously or later, from any thread).
|
|
152
|
+
// If no offer callback is registered, incoming offers are auto-rejected.
|
|
153
|
+
using TransferOfferCallback = std::function<void(const IncomingTransferOffer&)>;
|
|
154
|
+
|
|
155
|
+
// Invoked periodically with a progress snapshot, for both directions.
|
|
156
|
+
using TransferProgressCallback = std::function<void(const FileTransferProgress&)>;
|
|
157
|
+
|
|
158
|
+
// Invoked once when a transfer reaches a terminal state.
|
|
159
|
+
using TransferCompletedCallback =
|
|
160
|
+
std::function<void(const std::string& transfer_id, bool success, const std::string& error)>;
|
|
161
|
+
|
|
162
|
+
// -----------------------------------------------------------------------------
|
|
163
|
+
// FileTransferManager
|
|
164
|
+
// -----------------------------------------------------------------------------
|
|
165
|
+
|
|
185
166
|
class FileTransferManager {
|
|
186
167
|
public:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
* @param client Reference to RatsClient for communication
|
|
190
|
-
* @param config Transfer configuration settings
|
|
191
|
-
*/
|
|
192
|
-
FileTransferManager(RatsClient& client, const FileTransferConfig& config = FileTransferConfig());
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Destructor
|
|
196
|
-
*/
|
|
168
|
+
explicit FileTransferManager(RatsClient& client,
|
|
169
|
+
const FileTransferConfig& config = FileTransferConfig());
|
|
197
170
|
~FileTransferManager();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
*/
|
|
171
|
+
|
|
172
|
+
FileTransferManager(const FileTransferManager&) = delete;
|
|
173
|
+
FileTransferManager& operator=(const FileTransferManager&) = delete;
|
|
174
|
+
|
|
175
|
+
// --- configuration ---
|
|
204
176
|
void set_config(const FileTransferConfig& config);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Handle binary data that might be file transfer chunks
|
|
214
|
-
* @param peer_id Source peer ID
|
|
215
|
-
* @param binary_data Binary data received
|
|
216
|
-
* @return true if this was a file transfer chunk, false otherwise
|
|
217
|
-
*/
|
|
218
|
-
bool handle_binary_data(const std::string& peer_id, const std::vector<uint8_t>& binary_data);
|
|
219
|
-
|
|
220
|
-
// Callback registration
|
|
221
|
-
/**
|
|
222
|
-
* Set progress callback for transfer updates
|
|
223
|
-
* @param callback Function to call with progress updates
|
|
224
|
-
*/
|
|
225
|
-
void set_progress_callback(FileTransferProgressCallback callback);
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Set completion callback for transfer completion
|
|
229
|
-
* @param callback Function to call when transfers complete
|
|
230
|
-
*/
|
|
231
|
-
void set_completion_callback(FileTransferCompletedCallback callback);
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Set incoming transfer request callback
|
|
235
|
-
* @param callback Function to call when receiving transfer requests
|
|
236
|
-
*/
|
|
237
|
-
void set_request_callback(FileTransferRequestCallback callback);
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Set directory transfer progress callback
|
|
241
|
-
* @param callback Function to call with directory transfer progress
|
|
242
|
-
*/
|
|
243
|
-
void set_directory_progress_callback(DirectoryTransferProgressCallback callback);
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Set file request callback (called when receiving file requests)
|
|
247
|
-
* @param callback Function to call when receiving file requests
|
|
248
|
-
*/
|
|
249
|
-
void set_file_request_callback(FileRequestCallback callback);
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Set directory request callback (called when receiving directory requests)
|
|
253
|
-
* @param callback Function to call when receiving directory requests
|
|
254
|
-
*/
|
|
255
|
-
void set_directory_request_callback(DirectoryRequestCallback callback);
|
|
256
|
-
|
|
257
|
-
// File transfer operations
|
|
258
|
-
/**
|
|
259
|
-
* Send a file to a peer
|
|
260
|
-
* @param peer_id Target peer ID
|
|
261
|
-
* @param file_path Local file path to send
|
|
262
|
-
* @param remote_filename Optional remote filename (default: use local name)
|
|
263
|
-
* @return Transfer ID if successful, empty string if failed
|
|
264
|
-
*/
|
|
265
|
-
std::string send_file(const std::string& peer_id, const std::string& file_path,
|
|
266
|
-
const std::string& remote_filename = "");
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Send a file with custom metadata
|
|
270
|
-
* @param peer_id Target peer ID
|
|
271
|
-
* @param file_path Local file path to send
|
|
272
|
-
* @param metadata Custom file metadata
|
|
273
|
-
* @return Transfer ID if successful, empty string if failed
|
|
274
|
-
*/
|
|
275
|
-
std::string send_file_with_metadata(const std::string& peer_id, const std::string& file_path,
|
|
276
|
-
const FileMetadata& metadata);
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Send an entire directory to a peer
|
|
280
|
-
* @param peer_id Target peer ID
|
|
281
|
-
* @param directory_path Local directory path to send
|
|
282
|
-
* @param remote_directory_name Optional remote directory name
|
|
283
|
-
* @param recursive Whether to include subdirectories (default: true)
|
|
284
|
-
* @return Transfer ID if successful, empty string if failed
|
|
285
|
-
*/
|
|
177
|
+
FileTransferConfig get_config() const;
|
|
178
|
+
|
|
179
|
+
// --- starting transfers ---
|
|
180
|
+
// Returns a transfer id, or "" on immediate failure (e.g. missing file).
|
|
181
|
+
std::string send_file(const std::string& peer_id, const std::string& file_path,
|
|
182
|
+
const std::string& remote_name = "");
|
|
286
183
|
std::string send_directory(const std::string& peer_id, const std::string& directory_path,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
* @param remote_directory_path Path to directory on remote peer
|
|
303
|
-
* @param local_directory_path Local path where directory should be saved
|
|
304
|
-
* @param recursive Whether to include subdirectories (default: true)
|
|
305
|
-
* @return Transfer ID if successful, empty string if failed
|
|
306
|
-
*/
|
|
307
|
-
std::string request_directory(const std::string& peer_id, const std::string& remote_directory_path,
|
|
308
|
-
const std::string& local_directory_path, bool recursive = true);
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Accept an incoming file transfer
|
|
312
|
-
* @param transfer_id Transfer identifier from request
|
|
313
|
-
* @param local_path Local path where file should be saved
|
|
314
|
-
* @return true if accepted successfully
|
|
315
|
-
*/
|
|
316
|
-
bool accept_file_transfer(const std::string& transfer_id, const std::string& local_path);
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Reject an incoming file transfer
|
|
320
|
-
* @param transfer_id Transfer identifier from request
|
|
321
|
-
* @param reason Optional reason for rejection
|
|
322
|
-
* @return true if rejected successfully
|
|
323
|
-
*/
|
|
324
|
-
bool reject_file_transfer(const std::string& transfer_id, const std::string& reason = "");
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Accept an incoming directory transfer
|
|
328
|
-
* @param transfer_id Transfer identifier from request
|
|
329
|
-
* @param local_path Local path where directory should be saved
|
|
330
|
-
* @return true if accepted successfully
|
|
331
|
-
*/
|
|
332
|
-
bool accept_directory_transfer(const std::string& transfer_id, const std::string& local_path);
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Reject an incoming directory transfer
|
|
336
|
-
* @param transfer_id Transfer identifier from request
|
|
337
|
-
* @param reason Optional reason for rejection
|
|
338
|
-
* @return true if rejected successfully
|
|
339
|
-
*/
|
|
340
|
-
bool reject_directory_transfer(const std::string& transfer_id, const std::string& reason = "");
|
|
341
|
-
|
|
342
|
-
// Transfer control
|
|
343
|
-
/**
|
|
344
|
-
* Pause an active transfer
|
|
345
|
-
* @param transfer_id Transfer to pause
|
|
346
|
-
* @return true if paused successfully
|
|
347
|
-
*/
|
|
348
|
-
bool pause_transfer(const std::string& transfer_id);
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Resume a paused transfer
|
|
352
|
-
* @param transfer_id Transfer to resume
|
|
353
|
-
* @return true if resumed successfully
|
|
354
|
-
*/
|
|
355
|
-
bool resume_transfer(const std::string& transfer_id);
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Cancel an active or paused transfer
|
|
359
|
-
* @param transfer_id Transfer to cancel
|
|
360
|
-
* @return true if cancelled successfully
|
|
361
|
-
*/
|
|
362
|
-
bool cancel_transfer(const std::string& transfer_id);
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Retry a failed transfer
|
|
366
|
-
* @param transfer_id Transfer to retry
|
|
367
|
-
* @return true if retry initiated successfully
|
|
368
|
-
*/
|
|
369
|
-
bool retry_transfer(const std::string& transfer_id);
|
|
370
|
-
|
|
371
|
-
// Information and monitoring
|
|
372
|
-
/**
|
|
373
|
-
* Get progress information for a transfer
|
|
374
|
-
* @param transfer_id Transfer to query
|
|
375
|
-
* @return Progress information or nullptr if not found
|
|
376
|
-
*/
|
|
377
|
-
std::shared_ptr<FileTransferProgress> get_transfer_progress(const std::string& transfer_id) const;
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Get all active transfers
|
|
381
|
-
* @return Vector of transfer progress objects
|
|
382
|
-
*/
|
|
184
|
+
const std::string& remote_name = "");
|
|
185
|
+
|
|
186
|
+
// --- responding to an incoming offer ---
|
|
187
|
+
// For a single file, local_path is the destination file path.
|
|
188
|
+
// For a directory, local_path is the destination directory.
|
|
189
|
+
bool accept(const std::string& transfer_id, const std::string& local_path);
|
|
190
|
+
bool reject(const std::string& transfer_id, const std::string& reason = "");
|
|
191
|
+
|
|
192
|
+
// --- controlling an active transfer (works from either side) ---
|
|
193
|
+
bool pause(const std::string& transfer_id);
|
|
194
|
+
bool resume(const std::string& transfer_id);
|
|
195
|
+
bool cancel(const std::string& transfer_id);
|
|
196
|
+
|
|
197
|
+
// --- queries ---
|
|
198
|
+
std::shared_ptr<FileTransferProgress> get_progress(const std::string& transfer_id) const;
|
|
383
199
|
std::vector<std::shared_ptr<FileTransferProgress>> get_active_transfers() const;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
* @return JSON object with transfer statistics
|
|
400
|
-
*/
|
|
401
|
-
nlohmann::json get_transfer_statistics() const;
|
|
402
|
-
|
|
403
|
-
// Utility functions
|
|
404
|
-
/**
|
|
405
|
-
* Calculate file checksum
|
|
406
|
-
* @param file_path Path to file
|
|
407
|
-
* @param algorithm Hash algorithm ("md5", "sha256")
|
|
408
|
-
* @return Checksum string or empty if failed
|
|
409
|
-
*/
|
|
410
|
-
static std::string calculate_file_checksum(const std::string& file_path, const std::string& algorithm = "sha256");
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Get file metadata
|
|
414
|
-
* @param file_path Path to file
|
|
415
|
-
* @return File metadata structure
|
|
416
|
-
*/
|
|
417
|
-
static FileMetadata get_file_metadata(const std::string& file_path);
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Get directory metadata
|
|
421
|
-
* @param directory_path Path to directory
|
|
422
|
-
* @param recursive Whether to scan recursively
|
|
423
|
-
* @return Directory metadata structure
|
|
424
|
-
*/
|
|
425
|
-
static DirectoryMetadata get_directory_metadata(const std::string& directory_path, bool recursive = true);
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Validate file path and permissions
|
|
429
|
-
* @param file_path Path to validate
|
|
430
|
-
* @param check_write Whether to check write permissions
|
|
431
|
-
* @return true if valid and accessible
|
|
432
|
-
*/
|
|
433
|
-
static bool validate_file_path(const std::string& file_path, bool check_write = false);
|
|
200
|
+
nlohmann::json get_statistics() const;
|
|
201
|
+
|
|
202
|
+
// --- callbacks ---
|
|
203
|
+
void set_offer_callback(TransferOfferCallback callback);
|
|
204
|
+
void set_progress_callback(TransferProgressCallback callback);
|
|
205
|
+
void set_completed_callback(TransferCompletedCallback callback);
|
|
206
|
+
|
|
207
|
+
// --- hooks invoked by RatsClient (not part of the application API) ---
|
|
208
|
+
// Returns true if the binary data was a file-transfer chunk frame.
|
|
209
|
+
bool handle_binary_data(const std::string& peer_id, const std::vector<uint8_t>& data);
|
|
210
|
+
void on_peer_disconnected(const std::string& peer_id);
|
|
211
|
+
|
|
212
|
+
// --- utilities ---
|
|
213
|
+
// Hex-encoded SHA-256 of a file, or "" if it cannot be read.
|
|
214
|
+
static std::string compute_file_sha256(const std::string& path);
|
|
434
215
|
|
|
435
216
|
private:
|
|
217
|
+
// --- setup / teardown ---
|
|
218
|
+
void register_handlers();
|
|
219
|
+
void worker_loop();
|
|
220
|
+
void maintenance_loop();
|
|
221
|
+
|
|
222
|
+
// --- sending ---
|
|
223
|
+
std::string start_send(const std::string& peer_id, const std::shared_ptr<Transfer>& t,
|
|
224
|
+
const std::string& name);
|
|
225
|
+
void run_send(const std::shared_ptr<Transfer>& t);
|
|
226
|
+
void queue_send(const std::string& transfer_id);
|
|
227
|
+
|
|
228
|
+
// --- control message handlers ---
|
|
229
|
+
void on_offer(const std::string& peer_id, const nlohmann::json& msg);
|
|
230
|
+
void on_response(const std::string& peer_id, const nlohmann::json& msg);
|
|
231
|
+
void on_file_end(const std::string& peer_id, const nlohmann::json& msg);
|
|
232
|
+
void on_progress(const std::string& peer_id, const nlohmann::json& msg);
|
|
233
|
+
void on_complete(const std::string& peer_id, const nlohmann::json& msg);
|
|
234
|
+
void on_control(const std::string& peer_id, const nlohmann::json& msg);
|
|
235
|
+
|
|
236
|
+
// --- receiving ---
|
|
237
|
+
void on_chunk(const std::string& transfer_id, const std::string& peer_id,
|
|
238
|
+
uint32_t file_index, uint64_t offset,
|
|
239
|
+
const uint8_t* data, uint32_t len, uint32_t crc);
|
|
240
|
+
void try_finalize_file(const std::shared_ptr<Transfer>& t, size_t file_index);
|
|
241
|
+
|
|
242
|
+
// --- shared helpers ---
|
|
243
|
+
std::shared_ptr<Transfer> find(const std::string& transfer_id) const;
|
|
244
|
+
void finish(const std::shared_ptr<Transfer>& t, bool success, const std::string& error);
|
|
245
|
+
void emit_progress(const std::shared_ptr<Transfer>& t);
|
|
246
|
+
FileTransferProgress snapshot(const std::shared_ptr<Transfer>& t) const; // call with t->mtx held
|
|
247
|
+
void send_control(const std::shared_ptr<Transfer>& t, const std::string& action);
|
|
248
|
+
void cleanup_temp_files(const std::shared_ptr<Transfer>& t);
|
|
249
|
+
|
|
436
250
|
RatsClient& client_;
|
|
251
|
+
|
|
252
|
+
mutable std::mutex config_mutex_;
|
|
437
253
|
FileTransferConfig config_;
|
|
438
|
-
|
|
439
|
-
//
|
|
254
|
+
|
|
255
|
+
// All transfers, active and recently finished, keyed by transfer id.
|
|
440
256
|
mutable std::mutex transfers_mutex_;
|
|
441
|
-
std::unordered_map<std::string, std::shared_ptr<
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
std::
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
// Chunk management
|
|
462
|
-
mutable std::mutex chunks_mutex_;
|
|
463
|
-
std::unordered_map<std::string, std::queue<FileChunk>> outgoing_chunks_;
|
|
464
|
-
std::unordered_map<std::string, std::unordered_map<uint64_t, FileChunk>> received_chunks_;
|
|
465
|
-
|
|
466
|
-
// Active chunk transfers waiting for binary data
|
|
467
|
-
struct PendingChunk {
|
|
468
|
-
std::string transfer_id;
|
|
469
|
-
uint64_t chunk_index;
|
|
470
|
-
uint64_t total_chunks;
|
|
471
|
-
uint64_t chunk_size;
|
|
472
|
-
uint64_t file_offset;
|
|
473
|
-
std::string checksum;
|
|
474
|
-
std::chrono::steady_clock::time_point created_at;
|
|
475
|
-
};
|
|
476
|
-
std::unordered_map<std::string, PendingChunk> pending_chunks_; // key: peer_id
|
|
477
|
-
|
|
478
|
-
// Worker threads
|
|
479
|
-
std::vector<std::thread> worker_threads_;
|
|
480
|
-
std::atomic<bool> running_;
|
|
481
|
-
std::condition_variable work_condition_;
|
|
482
|
-
std::mutex work_mutex_;
|
|
483
|
-
std::queue<std::string> work_queue_; // Transfer IDs that need processing
|
|
484
|
-
|
|
485
|
-
// Cleanup thread synchronization
|
|
486
|
-
std::condition_variable cleanup_condition_;
|
|
487
|
-
std::mutex cleanup_mutex_;
|
|
488
|
-
|
|
489
|
-
// Throttling synchronization
|
|
490
|
-
std::condition_variable throttle_condition_;
|
|
491
|
-
std::mutex throttle_mutex_;
|
|
492
|
-
|
|
493
|
-
// Callbacks
|
|
494
|
-
FileTransferProgressCallback progress_callback_;
|
|
495
|
-
FileTransferCompletedCallback completion_callback_;
|
|
496
|
-
FileTransferRequestCallback request_callback_;
|
|
497
|
-
DirectoryTransferProgressCallback directory_progress_callback_;
|
|
498
|
-
FileRequestCallback file_request_callback_;
|
|
499
|
-
DirectoryRequestCallback directory_request_callback_;
|
|
500
|
-
|
|
501
|
-
// Statistics
|
|
257
|
+
std::unordered_map<std::string, std::shared_ptr<Transfer>> transfers_;
|
|
258
|
+
|
|
259
|
+
// Outgoing transfers ready to be streamed by a worker thread.
|
|
260
|
+
std::mutex queue_mutex_;
|
|
261
|
+
std::condition_variable queue_cv_;
|
|
262
|
+
std::queue<std::string> send_queue_;
|
|
263
|
+
|
|
264
|
+
std::vector<std::thread> workers_;
|
|
265
|
+
std::thread maintenance_thread_;
|
|
266
|
+
std::mutex maintenance_mutex_;
|
|
267
|
+
std::condition_variable maintenance_cv_;
|
|
268
|
+
std::atomic<bool> running_{true};
|
|
269
|
+
|
|
270
|
+
mutable std::mutex callbacks_mutex_;
|
|
271
|
+
TransferOfferCallback offer_callback_;
|
|
272
|
+
TransferProgressCallback progress_callback_;
|
|
273
|
+
TransferCompletedCallback completed_callback_;
|
|
274
|
+
|
|
275
|
+
// Aggregate statistics.
|
|
502
276
|
mutable std::mutex stats_mutex_;
|
|
503
|
-
uint64_t
|
|
504
|
-
uint64_t
|
|
505
|
-
uint64_t
|
|
506
|
-
uint64_t
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
void initialize();
|
|
511
|
-
void shutdown();
|
|
512
|
-
void worker_thread_loop();
|
|
513
|
-
void cleanup_thread_loop();
|
|
514
|
-
void process_transfer(const std::string& transfer_id);
|
|
515
|
-
|
|
516
|
-
// Transfer management
|
|
517
|
-
std::string generate_transfer_id() const;
|
|
518
|
-
void start_file_send(const std::string& transfer_id);
|
|
519
|
-
void start_file_receive(const std::string& transfer_id);
|
|
520
|
-
void start_directory_send(const std::string& transfer_id);
|
|
521
|
-
void start_directory_receive(const std::string& transfer_id);
|
|
522
|
-
void handle_chunk_received(const FileChunk& chunk);
|
|
523
|
-
void handle_chunk_ack(const std::string& transfer_id, uint64_t chunk_index, bool success);
|
|
524
|
-
|
|
525
|
-
// File operations
|
|
526
|
-
bool create_temp_file(const std::string& transfer_id, uint64_t file_size);
|
|
527
|
-
bool finalize_received_file(const std::string& transfer_id, const std::string& final_path);
|
|
528
|
-
|
|
529
|
-
// Checksum validation
|
|
530
|
-
bool verify_chunk_checksum(const FileChunk& chunk);
|
|
531
|
-
std::string calculate_chunk_checksum(const std::vector<uint8_t>& data);
|
|
532
|
-
|
|
533
|
-
// Network message handling
|
|
534
|
-
void handle_transfer_request(const std::string& peer_id, const nlohmann::json& message);
|
|
535
|
-
void handle_transfer_response(const std::string& peer_id, const nlohmann::json& message);
|
|
536
|
-
void handle_chunk_metadata_message(const std::string& peer_id, const nlohmann::json& message);
|
|
537
|
-
void handle_chunk_binary_message(const std::string& peer_id, const std::vector<uint8_t>& binary_data);
|
|
538
|
-
void handle_chunk_ack_message(const std::string& peer_id, const nlohmann::json& message);
|
|
539
|
-
void handle_transfer_control(const std::string& peer_id, const nlohmann::json& message);
|
|
540
|
-
void handle_file_request(const std::string& peer_id, const nlohmann::json& message);
|
|
541
|
-
void handle_directory_request(const std::string& peer_id, const nlohmann::json& message);
|
|
542
|
-
|
|
543
|
-
// Message creation
|
|
544
|
-
nlohmann::json create_transfer_request_message(const FileMetadata& metadata, const std::string& transfer_id);
|
|
545
|
-
nlohmann::json create_transfer_response_message(const std::string& transfer_id, bool accepted, const std::string& reason = "");
|
|
546
|
-
std::vector<uint8_t> create_chunk_binary_message(const FileChunk& chunk);
|
|
547
|
-
nlohmann::json create_chunk_metadata_message(const FileChunk& chunk);
|
|
548
|
-
nlohmann::json create_chunk_ack_message(const std::string& transfer_id, uint64_t chunk_index, bool success, const std::string& error = "");
|
|
549
|
-
nlohmann::json create_control_message(const std::string& transfer_id, const std::string& action, const nlohmann::json& data = nlohmann::json::object());
|
|
550
|
-
|
|
551
|
-
// Progress tracking
|
|
552
|
-
void update_transfer_progress(const std::string& transfer_id, uint64_t bytes_delta = 0);
|
|
553
|
-
void complete_transfer(const std::string& transfer_id, bool success, const std::string& error_message = "");
|
|
554
|
-
void move_to_completed(const std::string& transfer_id);
|
|
555
|
-
|
|
556
|
-
// File system utilities
|
|
557
|
-
static bool ensure_directory_exists(const std::string& directory_path);
|
|
558
|
-
static std::string get_temp_file_path(const std::string& transfer_id, const std::string& temp_dir);
|
|
559
|
-
static std::string extract_filename(const std::string& file_path);
|
|
560
|
-
static std::string get_mime_type(const std::string& file_path);
|
|
561
|
-
|
|
562
|
-
// Binary chunk transmission helper
|
|
563
|
-
bool parse_chunk_binary_header(const std::vector<uint8_t>& binary_data, FileChunk& chunk);
|
|
277
|
+
uint64_t stat_bytes_sent_ = 0;
|
|
278
|
+
uint64_t stat_bytes_received_ = 0;
|
|
279
|
+
uint64_t stat_files_sent_ = 0;
|
|
280
|
+
uint64_t stat_files_received_ = 0;
|
|
281
|
+
uint64_t stat_completed_ = 0;
|
|
282
|
+
uint64_t stat_failed_ = 0;
|
|
283
|
+
std::chrono::steady_clock::time_point started_at_;
|
|
564
284
|
};
|
|
565
285
|
|
|
566
286
|
} // namespace librats
|
|
567
|
-
|