@helia/block-brokers 5.1.4 → 5.2.1

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.
@@ -1,8 +1,17 @@
1
+ import { peerIdFromCID } from '@libp2p/peer-id'
2
+ import { uriToMultiaddr } from '@multiformats/uri-to-multiaddr'
1
3
  import { base64 } from 'multiformats/bases/base64'
4
+ import { CID } from 'multiformats/cid'
5
+ import { identity } from 'multiformats/hashes/identity'
6
+ import { CustomProgressEvent } from 'progress-events'
7
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
2
8
  import { DEFAULT_MAX_SIZE } from './index.ts'
3
9
  import { limitedResponse } from './utils.ts'
4
- import type { ComponentLogger, Logger } from '@libp2p/interface'
5
- import type { CID } from 'multiformats/cid'
10
+ import type { BlockBrokerConnectedProgressEvent, BlockBrokerConnectProgressEvent, BlockBrokerGetBlockProgressEvents, BlockBrokerReceiveBlockProgressEvent, BlockBrokerRequestBlockProgressEvent } from '@helia/interface'
11
+ import type { ComponentLogger, Logger, PeerId } from '@libp2p/interface'
12
+ import type { ProgressOptions } from 'progress-events'
13
+
14
+ const TRANSPORT_IPFS_GATEWAY_HTTP_CODE = 0x0920
6
15
 
7
16
  export interface TrustlessGatewayStats {
8
17
  attempts: number
@@ -22,7 +31,7 @@ export interface TrustlessGatewayComponents {
22
31
  routing: string
23
32
  }
24
33
 
25
- export interface GetRawBlockOptions {
34
+ export interface GetRawBlockOptions extends ProgressOptions<BlockBrokerGetBlockProgressEvents> {
26
35
  signal?: AbortSignal
27
36
 
28
37
  /**
@@ -41,6 +50,8 @@ export interface GetRawBlockOptions {
41
50
  */
42
51
  export class TrustlessGateway {
43
52
  public readonly url: URL
53
+ private readonly peer: PeerId
54
+
44
55
  /**
45
56
  * The number of times this gateway has been attempted to be used to fetch a
46
57
  * block. This includes successful, errored, and aborted attempts. By counting
@@ -86,6 +97,7 @@ export class TrustlessGateway {
86
97
  this.transformRequestInit = transformRequestInit
87
98
  this.log = logger.forComponent(`helia:trustless-gateway-block-broker:${this.url.host}`)
88
99
  this.routing = routing
100
+ this.peer = peerIdFromCID(CID.createV1(TRANSPORT_IPFS_GATEWAY_HTTP_CODE, identity.digest(uint8ArrayFromString(this.url.toString()))))
89
101
  }
90
102
 
91
103
  /**
@@ -106,15 +118,16 @@ export class TrustlessGateway {
106
118
  * Fetch a raw block from `this.url` following the specification defined at
107
119
  * https://specs.ipfs.tech/http-gateways/trustless-gateway/
108
120
  */
109
- async getRawBlock (cid: CID, { signal, maxSize = DEFAULT_MAX_SIZE }: GetRawBlockOptions = {}): Promise<Uint8Array> {
121
+ async getRawBlock (cid: CID, options: GetRawBlockOptions = {}): Promise<Uint8Array> {
110
122
  const gwUrl = new URL(this.url.toString())
111
123
  gwUrl.pathname = `/ipfs/${cid.toString()}`
124
+ const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE
112
125
 
113
126
  // necessary as not every gateway supports dag-cbor, but every should support
114
127
  // sending raw block as-is
115
128
  gwUrl.search = '?format=raw'
116
129
 
117
- if (signal?.aborted === true) {
130
+ if (options.signal?.aborted === true) {
118
131
  throw new Error(`Signal to fetch raw block for CID ${cid} from gateway ${this.url} was aborted prior to fetch`)
119
132
  }
120
133
 
@@ -125,7 +138,7 @@ export class TrustlessGateway {
125
138
  const abortInnerSignal = (): void => {
126
139
  innerController.abort()
127
140
  }
128
- signal?.addEventListener('abort', abortInnerSignal)
141
+ options.signal?.addEventListener('abort', abortInnerSignal)
129
142
 
130
143
  try {
131
144
  let pendingResponse: Promise<Uint8Array> | undefined = this.#pendingResponses.get(blockId)
@@ -147,6 +160,13 @@ export class TrustlessGateway {
147
160
  %s
148
161
  `, reqInit.method ?? 'GET', gwUrl, [...headers.entries()].map(([key, value]) => `${key}: ${value}`).join('\n'))
149
162
 
163
+ options.onProgress?.(new CustomProgressEvent<BlockBrokerConnectProgressEvent>('helia:block-broker:connect', {
164
+ broker: 'trustless-gateway',
165
+ type: 'connect',
166
+ provider: this.peer,
167
+ cid
168
+ }))
169
+
150
170
  pendingResponse = fetch(gwUrl.toString(), reqInit).then(async (res) => {
151
171
  this.log(`received response
152
172
  HTTP/1.1 %d %s
@@ -157,9 +177,33 @@ HTTP/1.1 %d %s
157
177
  this.#errors++
158
178
  throw new Error(`Unable to fetch raw block for CID ${cid} from gateway ${this.url}, received ${res.status} ${res.statusText}`)
159
179
  }
180
+
181
+ options.onProgress?.(new CustomProgressEvent<BlockBrokerConnectedProgressEvent>('helia:block-broker:connected', {
182
+ broker: 'trustless-gateway',
183
+ type: 'connected',
184
+ provider: this.peer,
185
+ address: uriToMultiaddr(gwUrl.toString()),
186
+ cid
187
+ }))
188
+
189
+ options.onProgress?.(new CustomProgressEvent<BlockBrokerRequestBlockProgressEvent>('helia:block-broker:request-block', {
190
+ broker: 'trustless-gateway',
191
+ type: 'request-block',
192
+ provider: this.peer,
193
+ cid
194
+ }))
195
+
160
196
  // limited Response ensures the body is less than 2MiB (or configurable maxSize)
161
197
  // see https://github.com/ipfs/helia/issues/790
162
198
  const body = await limitedResponse(res, maxSize, { signal: innerController.signal, log: this.log })
199
+
200
+ options.onProgress?.(new CustomProgressEvent<BlockBrokerReceiveBlockProgressEvent>('helia:block-broker:receive-block', {
201
+ broker: 'trustless-gateway',
202
+ type: 'receive-block',
203
+ provider: this.peer,
204
+ cid
205
+ }))
206
+
163
207
  this.#successes++
164
208
  return body
165
209
  })
@@ -175,7 +219,7 @@ HTTP/1.1 %d %s
175
219
  this.#errors++
176
220
  throw new Error(`Unable to fetch raw block for CID ${cid} - ${cause.message}`)
177
221
  } finally {
178
- signal?.removeEventListener('abort', abortInnerSignal)
222
+ options.signal?.removeEventListener('abort', abortInnerSignal)
179
223
  this.#pendingResponses.delete(blockId)
180
224
  }
181
225
  }