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.
Files changed (35) hide show
  1. package/README.md +9 -4
  2. package/lib/index.d.ts +9 -22
  3. package/native-src/CMakeLists.txt +132 -50
  4. package/native-src/cmake/ratsConfig.cmake.in +8 -0
  5. package/native-src/src/bt_network.cpp +296 -183
  6. package/native-src/src/bt_network.h +25 -5
  7. package/native-src/src/crypto/sha256.c +1 -1
  8. package/native-src/src/dht.cpp +297 -47
  9. package/native-src/src/dht.h +70 -6
  10. package/native-src/src/file_transfer.cpp +1185 -1578
  11. package/native-src/src/file_transfer.h +240 -521
  12. package/native-src/src/io_poller.cpp +917 -0
  13. package/native-src/src/io_poller.h +138 -0
  14. package/native-src/src/krpc.cpp +161 -96
  15. package/native-src/src/krpc.h +18 -4
  16. package/native-src/src/librats.cpp +812 -1236
  17. package/native-src/src/librats.h +207 -208
  18. package/native-src/src/librats_bittorrent.cpp +1 -5
  19. package/native-src/src/librats_c.cpp +22 -39
  20. package/native-src/src/librats_discovery.cpp +377 -0
  21. package/native-src/src/librats_encryption.cpp +130 -283
  22. package/native-src/src/librats_file_transfer.cpp +27 -109
  23. package/native-src/src/librats_gossipsub.cpp +1 -5
  24. package/native-src/src/librats_ice.cpp +5 -1
  25. package/native-src/src/librats_log_macros.h +36 -0
  26. package/native-src/src/librats_logging.cpp +1 -7
  27. package/native-src/src/librats_mdns.cpp +1 -11
  28. package/native-src/src/librats_persistence.cpp +1 -11
  29. package/native-src/src/librats_reconnection.cpp +2 -13
  30. package/native-src/src/librats_statistic.cpp +105 -0
  31. package/native-src/src/socket.cpp +15 -3
  32. package/package.json +1 -1
  33. package/scripts/build-librats.js +3 -0
  34. package/scripts/prepare-package.js +10 -0
  35. package/src/librats_node.cpp +53 -64
@@ -1,567 +1,286 @@
1
1
  #pragma once
2
2
 
3
- #include "socket.h"
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
- #include <string>
6
- #include <vector>
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 <condition_variable>
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, // Transfer queued but not started
27
- STARTING, // Transfer initialization in progress
28
- IN_PROGRESS, // Transfer actively sending/receiving chunks
29
- PAUSED, // Transfer temporarily paused
30
- COMPLETED, // Transfer completed successfully
31
- FAILED, // Transfer failed due to error
32
- CANCELLED, // Transfer cancelled by user
33
- RESUMING // Transfer resuming from interruption
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, // We are sending the file
41
- RECEIVING // We are receiving the file
67
+ SENDING,
68
+ RECEIVING
42
69
  };
43
70
 
44
- // Note: Compression removed as requested - only binary chunks needed
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
- * File metadata for transfers
64
- */
65
- struct FileMetadata {
66
- std::string filename; // Original filename
67
- std::string relative_path; // Relative path within directory structure
68
- uint64_t file_size; // Total file size in bytes
69
- uint64_t last_modified; // Last modification timestamp
70
- std::string mime_type; // MIME type of the file
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
- * Directory transfer metadata
78
- */
79
- struct DirectoryMetadata {
80
- std::string directory_name; // Directory name
81
- std::string relative_path; // Relative path
82
- std::vector<FileMetadata> files; // Files in this directory level
83
- std::vector<DirectoryMetadata> subdirectories; // Nested directories
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
- * File transfer progress information
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; // Transfer identifier
97
- std::string peer_id; // Peer we're transferring with
98
- FileTransferDirection direction; // Send or receive
99
- FileTransferStatus status; // Current status
100
-
101
- // File information
102
- std::string filename; // File being transferred
103
- std::string local_path; // Local file path
104
- uint64_t file_size; // Total file size
105
-
106
- // Progress tracking
107
- uint64_t bytes_transferred; // Bytes completed
108
- uint64_t total_bytes; // Total bytes to transfer
109
- uint32_t chunks_completed; // Chunks successfully transferred
110
- uint32_t total_chunks; // Total chunks in transfer
111
-
112
- // Performance metrics
113
- std::chrono::steady_clock::time_point start_time; // Transfer start time
114
- std::chrono::steady_clock::time_point last_update; // Last progress update
115
- double transfer_rate_bps; // Current transfer rate (bytes/second)
116
- double average_rate_bps; // Average transfer rate since start
117
- std::chrono::milliseconds estimated_time_remaining; // ETA
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) return 0.0;
136
- return (static_cast<double>(bytes_transferred) / total_bytes) * 100.0;
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
- // Update transfer rate calculations
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
- * File transfer configuration
151
- */
132
+ // -----------------------------------------------------------------------------
133
+ // Configuration
134
+ // -----------------------------------------------------------------------------
135
+
152
136
  struct FileTransferConfig {
153
- uint32_t chunk_size; // Size of each chunk (default: 64KB)
154
- uint32_t max_concurrent_chunks; // Max chunks in flight (default: 4)
155
- uint32_t max_retries; // Max retry attempts per chunk (default: 3)
156
- uint32_t timeout_seconds; // Timeout per chunk (default: 30)
157
- bool verify_checksums; // Verify chunk checksums (default: true)
158
- bool allow_resume; // Allow resuming interrupted transfers (default: true)
159
- std::string temp_directory; // Temporary directory for incomplete files
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
- * Callback function types for file transfer events
173
- */
174
- using FileTransferProgressCallback = std::function<void(const FileTransferProgress&)>;
175
- using FileTransferCompletedCallback = std::function<void(const std::string& transfer_id, bool success, const std::string& error_message)>;
176
- using FileTransferRequestCallback = std::function<bool(const std::string& peer_id, const FileMetadata& metadata, const std::string& transfer_id)>;
177
- using DirectoryTransferProgressCallback = std::function<void(const std::string& transfer_id, const std::string& current_file, uint64_t files_completed, uint64_t total_files, uint64_t bytes_completed, uint64_t total_bytes)>;
178
- using FileRequestCallback = std::function<bool(const std::string& peer_id, const std::string& file_path, const std::string& transfer_id)>;
179
- using DirectoryRequestCallback = std::function<bool(const std::string& peer_id, const std::string& directory_path, bool recursive, const std::string& transfer_id)>;
180
-
181
- /**
182
- * File transfer manager class
183
- * Handles efficient chunked file transfers with resume capability
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
- * Constructor
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
- // Configuration
200
- /**
201
- * Update transfer configuration
202
- * @param config New configuration settings
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
- * Get current configuration
208
- * @return Current configuration settings
209
- */
210
- const FileTransferConfig& get_config() const;
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
- const std::string& remote_directory_name = "", bool recursive = true);
288
-
289
- /**
290
- * Request a file from a remote peer
291
- * @param peer_id Target peer ID
292
- * @param remote_file_path Path to file on remote peer
293
- * @param local_path Local path where file should be saved
294
- * @return Transfer ID if successful, empty string if failed
295
- */
296
- std::string request_file(const std::string& peer_id, const std::string& remote_file_path,
297
- const std::string& local_path);
298
-
299
- /**
300
- * Request a directory from a remote peer
301
- * @param peer_id Target peer ID
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
- * Get transfer history
387
- * @param limit Maximum number of entries to return (0 for all)
388
- * @return Vector of completed transfer progress objects
389
- */
390
- std::vector<std::shared_ptr<FileTransferProgress>> get_transfer_history(size_t limit = 0) const;
391
-
392
- /**
393
- * Clear transfer history
394
- */
395
- void clear_transfer_history();
396
-
397
- /**
398
- * Get statistics about transfers
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
- // Transfer tracking
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<FileTransferProgress>> active_transfers_;
442
- std::unordered_map<std::string, std::shared_ptr<FileTransferProgress>> completed_transfers_;
443
-
444
- // Pending transfers (not yet accepted/rejected)
445
- mutable std::mutex pending_mutex_;
446
- struct PendingFileTransfer {
447
- FileMetadata metadata;
448
- std::string peer_id;
449
- };
450
- struct PendingDirectoryTransfer {
451
- DirectoryMetadata metadata;
452
- std::string peer_id;
453
- };
454
- std::unordered_map<std::string, PendingFileTransfer> pending_transfers_;
455
- std::unordered_map<std::string, PendingDirectoryTransfer> pending_directory_transfers_;
456
-
457
- // Active directory transfers
458
- mutable std::mutex directory_transfers_mutex_;
459
- std::unordered_map<std::string, DirectoryMetadata> active_directory_transfers_;
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 total_bytes_sent_;
504
- uint64_t total_bytes_received_;
505
- uint64_t total_files_sent_;
506
- uint64_t total_files_received_;
507
- std::chrono::steady_clock::time_point start_time_;
508
-
509
- // Private methods
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
-