Serve provider-owned content chunks directly
This commit is contained in:
parent
b18fed412e
commit
c1c1792dc5
2 changed files with 28 additions and 7 deletions
|
|
@ -210,7 +210,7 @@ namespace GameCult.Mesh
|
|||
public sealed class CultMeshTcpContentServer : IDisposable
|
||||
{
|
||||
private readonly TcpListener _listener;
|
||||
private readonly CultCache _content;
|
||||
private readonly Func<string, CultMeshCdnArtifactChunk?> _resolveChunk;
|
||||
private readonly Func<string, bool> _canServeHash;
|
||||
private readonly CancellationTokenSource _shutdown = new();
|
||||
private readonly ConcurrentDictionary<TcpClient, byte> _clients = new();
|
||||
|
|
@ -222,9 +222,22 @@ namespace GameCult.Mesh
|
|||
TcpListener listener,
|
||||
CultCache content,
|
||||
Func<string, bool>? canServeHash = null)
|
||||
: this(listener, CreateCacheResolver(content), canServeHash)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a dedicated TCP content server over provider-owned immutable chunks.
|
||||
/// The resolver receives a normalized SHA-256 hash and may return <see langword="null"/>
|
||||
/// when that chunk is unavailable.
|
||||
/// </summary>
|
||||
public CultMeshTcpContentServer(
|
||||
TcpListener listener,
|
||||
Func<string, CultMeshCdnArtifactChunk?> resolveChunk,
|
||||
Func<string, bool>? canServeHash = null)
|
||||
{
|
||||
_listener = listener ?? throw new ArgumentNullException(nameof(listener));
|
||||
_content = content ?? throw new ArgumentNullException(nameof(content));
|
||||
_resolveChunk = resolveChunk ?? throw new ArgumentNullException(nameof(resolveChunk));
|
||||
_canServeHash = canServeHash ?? (_ => true);
|
||||
_listener.Start();
|
||||
_acceptLoop = AcceptLoopAsync(_shutdown.Token);
|
||||
|
|
@ -306,7 +319,7 @@ namespace GameCult.Mesh
|
|||
if (!string.IsNullOrWhiteSpace(request.RecordKey) &&
|
||||
!string.Equals(request.RecordKey, key.Value, StringComparison.Ordinal))
|
||||
throw new InvalidDataException("Content chunk record key disagrees with its content hash.");
|
||||
chunk = _content.Get<CultMeshCdnArtifactChunk>(key)
|
||||
chunk = _resolveChunk(hash)
|
||||
?? throw new FileNotFoundException("Content chunk is not available.", key.Value);
|
||||
CultMeshCdn.ValidateChunkPayload(new CultMeshCdnChunkRef
|
||||
{
|
||||
|
|
@ -335,6 +348,12 @@ namespace GameCult.Mesh
|
|||
await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static Func<string, CultMeshCdnArtifactChunk?> CreateCacheResolver(CultCache content)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
return hash => content.Get<CultMeshCdnArtifactChunk>(CultMeshCdnArtifactChunk.CreateRecordKey(hash));
|
||||
}
|
||||
}
|
||||
|
||||
internal static class CultMeshTcpContentWire
|
||||
|
|
|
|||
|
|
@ -34,12 +34,14 @@ public sealed class CultMeshContentSessionTests
|
|||
{
|
||||
var payload = Enumerable.Range(0, 700_000).Select(value => (byte)(value % 251)).ToArray();
|
||||
var artifact = CultMesh.PackCdnArtifact("aetheria/world/windows", payload);
|
||||
using var providerCache = new CultCache(CultMesh.CreateCultCacheDocumentRegistry(
|
||||
typeof(CultMeshCdnArtifactManifest), typeof(CultMeshCdnArtifactChunk)));
|
||||
await CultMeshCdn.PublishAsync(providerCache, artifact);
|
||||
var providerChunks = artifact.Chunks.ToDictionary(
|
||||
chunk => (chunk.ChunkHash.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase)
|
||||
? chunk.ChunkHash.Substring("sha256:".Length)
|
||||
: chunk.ChunkHash).Trim().ToLowerInvariant(),
|
||||
StringComparer.Ordinal);
|
||||
using var contentServer = new CultMeshTcpContentServer(
|
||||
new TcpListener(IPAddress.Loopback, 0),
|
||||
providerCache);
|
||||
hash => providerChunks.TryGetValue(hash, out var chunk) ? chunk : null);
|
||||
var endpoint = $"{CultMeshTcpContentTransportConnector.Scheme}://127.0.0.1:{contentServer.LocalEndPoint.Port}";
|
||||
var connector = new CultMeshTcpContentTransportConnector();
|
||||
using var discovery = new CultMeshDiscoveryService(new[] { new RouteSource(endpoint) });
|
||||
|
|
|
|||
Loading…
Reference in a new issue