Skip to content

Commit b60da1d

Browse files
committed
Build-To-Use: 3.3.0-68
10912c0 Peer-to-peer mesh (#2262) 14e61a9 Keep pulled revs from being echoed back to the peer by the Pusher d3acebd Change c4repl_getResponseHeaders return to C4SliceResult. (#2272) CBL-6799: Race can cause _responseTimer to be reset before stop is called (#2268) :warning: c4repl_getResponseHeaders now returns C4SliceResult, in lieu of C4Slice.
2 parents 18b46fd + a4efc9d commit b60da1d

File tree

125 files changed

+4647
-3044
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+4647
-3044
lines changed

.github/workflows/xcodebuild.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ jobs:
3737
#### BUILD
3838

3939
- name: "Build C++ Tests"
40-
uses: sersoft-gmbh/xcodebuild-action@v1
40+
uses: sersoft-gmbh/xcodebuild-action@v3.2.0
4141
with:
4242
project: Xcode/LiteCore.xcodeproj
4343
scheme: LiteCore C++ Tests
4444
destination: platform=macOS
4545
configuration: $CONFIGURATION_CPP
4646
action: build
47+
output-formatter: "" # Experimental
4748

4849
- name: "Build C4Tests"
49-
uses: sersoft-gmbh/xcodebuild-action@v1
50+
uses: sersoft-gmbh/xcodebuild-action@v3.2.0
5051
with:
5152
project: Xcode/LiteCore.xcodeproj
5253
scheme: LiteCore C Tests

C/Cpp_include/c4Certificate.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ struct C4Cert final
5757

5858
bool isSelfSigned();
5959

60+
bool isSignedBy(C4Cert* issuer);
61+
6062
Retained<C4KeyPair> getPublicKey();
6163

6264
Retained<C4KeyPair> loadPersistentPrivateKey();

C/Cpp_include/c4Database.hh

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ struct C4Database
8080

8181
virtual alloc_slice getPath() const = 0;
8282

83-
const Config& getConfiguration() const noexcept FLPURE { return _config; }
83+
const Config& getConfiguration() const noexcept LIFETIMEBOUND FLPURE { return _config; }
8484

8585
virtual alloc_slice getSourceID() const = 0;
8686
virtual C4UUID getPublicUUID() const = 0;
@@ -279,8 +279,6 @@ inline bool operator==(const C4Database::CollectionSpec& a, const C4Database::Co
279279
return a.name == b.name && a.effectiveScope() == b.effectiveScope();
280280
}
281281

282-
inline bool operator!=(const C4Database::CollectionSpec& a, const C4Database::CollectionSpec& b) { return !(a == b); }
283-
284282
template <>
285283
struct std::hash<C4Database::CollectionSpec> {
286284
std::size_t operator()(C4Database::CollectionSpec const& spec) const {

C/Cpp_include/c4PeerDiscovery.hh

Lines changed: 452 additions & 0 deletions
Large diffs are not rendered by default.

C/Cpp_include/c4PeerSync.hh

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//
2+
// c4PeerSync.hh
3+
//
4+
// Copyright 2025-Present Couchbase, Inc.
5+
//
6+
// Use of this software is governed by the Business Source License included
7+
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
8+
// in that file, in accordance with the Business Source License, use of this
9+
// software will be governed by the Apache License, Version 2.0, included in
10+
// the file licenses/APL2.txt.
11+
//
12+
13+
#pragma once
14+
#include "c4Base.hh"
15+
#include "c4PeerSyncTypes.h"
16+
#include "c4Replicator.hh"
17+
#include "fleece/InstanceCounted.hh"
18+
#include <cstring> // for memcmp
19+
#include <span>
20+
21+
#ifdef COUCHBASE_ENTERPRISE
22+
C4_ASSUME_NONNULL_BEGIN
23+
24+
// ************************************************************************
25+
// This header is part of the LiteCore C++ API.
26+
// If you use this API, you must _statically_ link LiteCore;
27+
// the dynamic library only exports the C API.
28+
// ************************************************************************
29+
30+
31+
/** A peer-to-peer sync manager that automatically discovers and connects with its counterparts
32+
with matching `peerGroupID`s, and replicates with them to sync a database. */
33+
struct C4PeerSync
34+
: fleece::InstanceCounted
35+
, C4Base {
36+
public:
37+
/** API to receive notifications from C4PeerSync. */
38+
class Delegate {
39+
public:
40+
virtual ~Delegate() = default;
41+
42+
/// C4PeerSync has started or stopped, possibly with an error.
43+
virtual void peerSyncStatus(bool started, C4Error const&) {}
44+
45+
/// A peer has come online or gone offline.
46+
virtual void peerDiscovery(C4PeerID const&, bool online) {}
47+
48+
/// Authenticate a TLS connection to/from a peer, based on properties of its certificate.
49+
virtual bool authenticatePeer(C4PeerID const&, C4Cert*) = 0;
50+
51+
/// A peer's direct connections to other peers have changed.
52+
virtual void peerNeighborsChanged(C4PeerID const&, size_t count) {}
53+
54+
/// A replication with a peer has changed status.
55+
/// The `incoming` flag is true if this connection was made by the other peer.
56+
virtual void peerReplicationStatus(C4PeerID const&, C4ReplicatorStatus const&, bool incoming) {}
57+
58+
/// A replication with a peer has transferred documents.
59+
/// @note This will only be called if you configured Parameters::progressLevel accordingly.
60+
virtual void peerDocumentsEnded(C4PeerID const&, bool pushing, std::span<const C4DocumentEnded*>) {}
61+
62+
/// A replication with a peer is transferring a blob.
63+
/// @note This will only be called if you configured Parameters::progressLevel accordingly.
64+
virtual void peerBlobProgress(C4PeerID const&, bool pushing, C4BlobProgress const&) {}
65+
};
66+
67+
/** Configuration of a C4PeerSync. */
68+
struct Parameters {
69+
std::string_view peerGroupID; ///< App identifier for peer discovery
70+
std::span<const std::string_view> protocols; ///< Which protocols to use (empty = all)
71+
C4Cert* tlsCert; ///< My TLS certificate (server+client)
72+
C4KeyPair* tlsKeyPair; ///< Certificate's key-pair
73+
C4Database* database; ///< Database to sync
74+
std::span<C4PeerSyncCollection> collections; ///< Collections to sync
75+
slice optionsDictFleece; ///< Replicator options
76+
C4ReplicatorProgressLevel progressLevel; ///< Level of progress notifications
77+
Delegate* delegate; ///< Your object that receives notifications
78+
};
79+
80+
explicit C4PeerSync(Parameters const&);
81+
explicit C4PeerSync(C4PeerSyncParameters const*);
82+
83+
/** @note It is guaranteed that the delegate will not be called after the destructor returns. */
84+
~C4PeerSync() noexcept override;
85+
86+
/// Returns this instance's peer ID, as visible to other peers.
87+
/// (The ID is derived via \ref c4peerid_fromCert from the C4Cert given in the parameters.)
88+
/// @note This function is thread-safe.
89+
C4PeerID thisPeerID() const noexcept;
90+
91+
/// Starts a C4PeerSync, beginning peer discovery and replication.
92+
/// This call is asynchronous and returns immediately.
93+
/// When it succeeds or fails, the delegate's \ref peerSyncStatus method will be called.
94+
/// @note This function is thread-safe.
95+
void start() noexcept;
96+
97+
/// Stops all active replicators, stops the listener, and stops peer discovery and publishing.
98+
/// This call is asynchronous and returns immediately.
99+
/// When complete, the delegate's \ref peerSyncStatus method will be called.
100+
/// @note This function is thread-safe.
101+
void stop() noexcept;
102+
103+
/** Information about a peer, returned from \ref getPeerInfo. */
104+
struct PeerInfo {
105+
Retained<C4Cert> certificate; ///< Its identity; nullptr if unverified
106+
std::vector<C4PeerID> neighbors; ///< Peers it's directly connected to
107+
C4ReplicatorStatus replicatorStatus{}; ///< Status of my connection to it, if any
108+
bool online = false; ///< True if it's currently online/visible
109+
};
110+
111+
/// Returns a list of all peers currently online, including this one.
112+
/// @note This function is thread-safe.
113+
std::vector<C4PeerID> onlinePeers();
114+
115+
/// Returns information about a peer.
116+
/// - If the peer is not directly connected, the `replicatorStatus.level` will be `kC4Stopped`.
117+
/// @note This function is thread-safe.
118+
PeerInfo getPeerInfo(C4PeerID const&);
119+
120+
// Version number of c4PeerSync.hh API. Incremented on incompatible changes.
121+
static constexpr int kAPIVersion = 4;
122+
123+
private:
124+
class Impl;
125+
class CppImpl;
126+
class CImpl;
127+
std::unique_ptr<Impl> _impl;
128+
};
129+
130+
// Equality operator for C4PeerIDs.
131+
inline bool operator==(C4PeerID const& a, C4PeerID const& b) { return memcmp(a.bytes, b.bytes, sizeof(a.bytes)) == 0; }
132+
133+
// Hash code for C4PeerIDs, making them useable as std::unordered_map keys.
134+
template <>
135+
struct std::hash<C4PeerID> {
136+
std::size_t operator()(C4PeerID const&) const noexcept;
137+
};
138+
139+
C4_ASSUME_NONNULL_END
140+
#endif // COUCHBASE_ENTERPRISE

C/Cpp_include/c4Replicator.hh

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
#pragma once
1414
#include "c4Base.hh"
1515
#include "c4ReplicatorTypes.h"
16+
#include <functional>
17+
#include <memory>
18+
#include <span>
19+
#include <vector>
1620

1721
C4_ASSUME_NONNULL_BEGIN
1822

@@ -22,6 +26,10 @@ C4_ASSUME_NONNULL_BEGIN
2226
// the dynamic library only exports the C API.
2327
// ************************************************************************
2428

29+
namespace fleece {
30+
class Dict;
31+
class MutableDict;
32+
} // namespace fleece
2533

2634
struct C4Replicator
2735
: public fleece::RefCounted
@@ -52,8 +60,48 @@ struct C4Replicator
5260
bool isDocumentPending(slice docID, C4CollectionSpec) const;
5361

5462
#ifdef COUCHBASE_ENTERPRISE
55-
virtual C4Cert* C4NULLABLE getPeerTLSCertificate() const;
63+
using PeerTLSCertificateValidator = std::function<bool(slice certData, std::string_view hostname)>;
64+
65+
/// Registers a callback that can accept or reject a peer's certificate during the TLS handshake.
66+
virtual void setPeerTLSCertificateValidator(PeerTLSCertificateValidator) = 0;
67+
68+
virtual C4Cert* C4NULLABLE getPeerTLSCertificate() const = 0;
5669
#endif
70+
71+
/** Extended, memory-safe version of `C4ReplicatorParameters`.
72+
* The constructor copies all the pointed-to data into internal storage:
73+
* - `optionsDictFleece`
74+
* - `collections`
75+
* - each collection's `name`, `scope` and `optionsDictFleece` */
76+
struct Parameters : C4ReplicatorParameters {
77+
Parameters();
78+
explicit Parameters(C4ReplicatorParameters const&);
79+
80+
Parameters(Parameters const& params) : Parameters((C4ReplicatorParameters const&)params) {}
81+
82+
std::span<C4ReplicationCollection> collections() noexcept { return _collections; }
83+
84+
std::span<const C4ReplicationCollection> collections() const noexcept { return _collections; }
85+
86+
/// The highest push and pull modes of any collections.
87+
std::pair<C4ReplicatorMode, C4ReplicatorMode> maxModes() const;
88+
89+
C4ReplicationCollection& addCollection(C4ReplicationCollection const&);
90+
91+
C4ReplicationCollection& addCollection(C4CollectionSpec const&, C4ReplicatorMode pushMode,
92+
C4ReplicatorMode pullMode);
93+
94+
fleece::MutableDict copyOptions() const; ///< Returns copy of options (never null)
95+
void setOptions(fleece::Dict); ///< Updates options Dict
96+
void updateOptions(std::function<void(fleece::MutableDict)> const& callback);
97+
98+
private:
99+
void makeAllocated(C4ReplicationCollection&);
100+
101+
alloc_slice _options;
102+
std::vector<C4ReplicationCollection> _collections;
103+
std::vector<alloc_slice> _slices;
104+
};
57105
};
58106

59107
C4_ASSUME_NONNULL_END

C/Cpp_include/c4Socket.hh

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,22 @@ struct C4Socket // NOLINT(cppcoreguidelines-pro-type-member-init) - its okay fo
4848
@param factory The C4SocketFactory that will manage the socket.
4949
@param nativeHandle A value known to the factory that represents the underlying socket,
5050
such as a file descriptor or a native object pointer.
51-
@param address The address of the remote peer making the connection.
51+
@param address The address of the remote peer.
52+
@param incoming True if this is an incoming (server) connection, false for outgoing (client).
5253
@return A new C4Socket initialized with the `nativeHandle`. */
53-
static C4Socket* fromNative(const C4SocketFactory& factory, void* C4NULLABLE nativeHandle,
54-
const C4Address& address);
55-
54+
static C4Socket* fromNative(const C4SocketFactory& factory, void* C4NULLABLE nativeHandle, const C4Address& address,
55+
bool incoming = true);
56+
57+
/** Notification that a socket is making a TLS connection and has received the peer's (usually
58+
server's) certificate.
59+
This notification occurs only after any other TLS validation options have passed
60+
(`kC4ReplicatorOptionRootCerts`, `kC4ReplicatorOptionPinnedServerCert`,
61+
`kC4ReplicatorOptionOnlySelfSignedServerCert`).
62+
@param certData The DER-encoded form of the peer's TLS certificate.
63+
@param hostname The DNS hostname of the peer. (This may be different from the original
64+
Address given, if there were HTTP redirects.)
65+
@returns True to proceed, false to abort the connection. */
66+
virtual bool gotPeerCertificate(slice certData, std::string_view hostname) = 0;
5667

5768
/** Notification that a socket has received an HTTP response, with the given headers (encoded
5869
as a Fleece dictionary.) This should be called just before \ref opened() or \ref closed().
@@ -114,6 +125,11 @@ struct C4Socket // NOLINT(cppcoreguidelines-pro-type-member-init) - its okay fo
114125
void* C4NULLABLE nativeHandle; ///< for client's use
115126
};
116127

128+
// Glue to make Retained<C4Socket> work:
129+
inline C4Socket* retain(C4Socket* socket) { return c4socket_retain(socket); }
130+
131+
inline void release(C4Socket* socket) { c4socket_release(socket); }
132+
117133
/** @} */
118134

119135
C4_ASSUME_NONNULL_END

C/c4CAPI.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,8 @@ bool c4cert_isSigned(C4Cert* cert) noexcept { return cert->isSigned(); }
10441044

10451045
bool c4cert_isSelfSigned(C4Cert* cert) noexcept { return cert->isSelfSigned(); }
10461046

1047+
bool c4cert_isSignedBy(C4Cert* cert, C4Cert* issuer) noexcept { return cert->isSignedBy(issuer); }
1048+
10471049
C4Cert* c4cert_signRequest(C4Cert* c4Cert, const C4CertIssuerParameters* C4NULLABLE c4Params,
10481050
C4KeyPair* issuerPrivateKey, C4Cert* issuerC4Cert, C4Error* outError) noexcept {
10491051
return tryCatch<C4Cert*>(outError, [&]() -> C4Cert* {

C/c4Certificate.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ bool C4Cert::isSelfSigned() {
120120
return signedCert && signedCert->isSelfSigned();
121121
}
122122

123+
bool C4Cert::isSignedBy(C4Cert* issuer) {
124+
Cert* signedCert = asSignedCert();
125+
Cert* signedIssuer = issuer->asSignedCert();
126+
return signedCert && signedIssuer && signedCert->isSignedBy(signedIssuer);
127+
}
128+
123129
Retained<C4KeyPair> C4Cert::getPublicKey() {
124130
if ( auto signedCert = asSignedCert(); signedCert ) return new C4KeyPair(signedCert->subjectPublicKey().get());
125131
return nullptr;

0 commit comments

Comments
 (0)