| … | … | @@ -25,10 +25,13 @@ import os from '#os' |
| 25 | 25 | import ReadyResource from 'ready-resource' |
| 26 | 26 | import safetyCatch from 'safety-catch' |
| 27 | 27 | |
| 28 | import Protomux from 'protomux' |
| 29 | |
| 28 | 30 | import { FileIndex } from 'hyper-overlay/file-index' |
| 29 | 31 | import { TransferManager } from 'hyper-overlay/transfer' |
| 30 | 32 | import { hashChunk } from 'hyper-overlay/chunker' |
| 31 | 33 | import { walkTree } from 'hyper-overlay/tree' |
| 34 | import { OverlayProtocolV2 } from 'hyper-overlay/protocol-v2' |
| 32 | 35 | import { attachProtocol } from './lib/protocol.js' |
| 33 | 36 | |
| 34 | 37 | const CHUNK_THRESHOLD = 1048576 // 1 MB — files below this transfer as whole blobs |
| … | … | @@ -72,6 +75,12 @@ export class DeployServer extends ReadyResource { |
| 72 | 75 | this._discovery = null |
| 73 | 76 | this._sessions = new Set() |
| 74 | 77 | |
| 78 | // 0.5b tree publish + 0.5c tree serving: when trees are populated |
| 79 | // (which we do in _scan), also attach hyper-overlay/v2 to each |
| 80 | // connection so clients can request trees + content-by-hash. |
| 81 | this._enableTrees = opts.enableTrees !== false |
| 82 | this._overlayProtocol = null |
| 83 | |
| 75 | 84 | this.manifest = null |
| 76 | 85 | this.stats = { totalConnections: 0, filesServed: 0, bytesServed: 0 } |
| 77 | 86 | |
| … | … | @@ -93,6 +102,23 @@ export class DeployServer extends ReadyResource { |
| 93 | 102 | await this._fileIndex.ready() |
| 94 | 103 | this._transferManager = new TransferManager(this._fileIndex) |
| 95 | 104 | |
| 105 | // Overlay-v2 protocol for tree-request / content-request. Shares |
| 106 | // the fileIndex with the file-list flow; no separate storage. |
| 107 | if (this._enableTrees) { |
| 108 | const stubSyncEngine = { |
| 109 | _fileIndex: this._fileIndex, |
| 110 | feedKey: '0'.repeat(64), |
| 111 | feed: { length: 0, async get () { return null } }, |
| 112 | async markSynced () {}, |
| 113 | async logPut () {} |
| 114 | } |
| 115 | this._overlayProtocol = new OverlayProtocolV2( |
| 116 | stubSyncEngine, |
| 117 | this._transferManager, |
| 118 | { key: this.topic, autoSync: false } |
| 119 | ) |
| 120 | } |
| 121 | |
| 96 | 122 | // Index directory |
| 97 | 123 | await this._scan() |
| 98 | 124 | |
| … | … | @@ -216,6 +242,30 @@ export class DeployServer extends ReadyResource { |
| 216 | 242 | return |
| 217 | 243 | } |
| 218 | 244 | |
| 245 | // Also attach hyper-overlay/v2 on the same stream (protomux handles |
| 246 | // multiplexing). Populates contentHashPaths from the fileIndex so |
| 247 | // content-request can serve any file by its contentHash. |
| 248 | if (this._overlayProtocol) { |
| 249 | try { |
| 250 | const mux = Protomux.from(conn) |
| 251 | // Build content-hash → disk-path map so content-request works |
| 252 | const contentHashPaths = new Map() |
| 253 | const filePaths = new Map() |
| 254 | for (const f of (this.manifest && this.manifest.files) || []) { |
| 255 | if (f.hash) { |
| 256 | const diskPath = path.join(this.dir, f.path) |
| 257 | contentHashPaths.set(f.hash, diskPath) |
| 258 | filePaths.set(f.path, diskPath) |
| 259 | } |
| 260 | } |
| 261 | this._overlayProtocol._contentHashPaths = contentHashPaths |
| 262 | this._overlayProtocol._filePaths = filePaths |
| 263 | this._overlayProtocol.attach(mux) |
| 264 | } catch (err) { |
| 265 | this.emit('error', new Error('failed to attach overlay-v2 protocol: ' + err.message)) |
| 266 | } |
| 267 | } |
| 268 | |
| 219 | 269 | const session = { peerKey, destroyed: false, filesServed: 0, bytesServed: 0 } |
| 220 | 270 | this._sessions.add(session) |
| 221 | 271 | this.emit('peer', { key: peerKey, state: 'connected' }) |