-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathblocks.js
159 lines (147 loc) · 4.58 KB
/
blocks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/* eslint-env serviceworker */
import { base58btc } from 'multiformats/bases/base58'
import { DenyingBlockStore } from './deny.js'
import * as BatchingFetcher from '@web3-storage/blob-fetcher/fetcher/batching'
import * as ContentClaimsLocator from '@web3-storage/blob-fetcher/locator/content-claims'
import * as Location from './location.js'
/**
* @typedef {import('multiformats').UnknownLink} UnknownLink
* @typedef {import('./worker.js').Env} Env
*
* @typedef {object} Blockstore
* @prop {(cid: UnknownLink) => Promise<boolean>} has
* @prop {(cid: UnknownLink) => Promise<Uint8Array | undefined>} get
*/
/**
* @param {Env} env
* @param {ExecutionContext} ctx
* @param {import('./metrics.js').Metrics} metrics
*/
export async function getBlockstore (env, ctx, metrics) {
const locator = ContentClaimsLocator.create({ serviceURL: env.CONTENT_CLAIMS_URL ? new URL(env.CONTENT_CLAIMS_URL) : undefined })
const cachingLocator = new CachingLocator(locator, await caches.open('index'), ctx, metrics)
const blocks = new BlockStore(cachingLocator, metrics)
const cached = new CachingBlockStore(blocks, await caches.open('blockstore:bytes'), ctx, metrics)
return env.DENYLIST ? new DenyingBlockStore(env.DENYLIST, cached) : cached
}
export class CachingLocator {
/**
* @param {import('@web3-storage/blob-fetcher').Locator} locator
* @param {Cache} cache
* @param {ExecutionContext} ctx
* @param {import('./metrics.js').Metrics} metrics
*/
constructor (locator, cache, ctx, metrics) {
this.locator = locator
this.cache = cache
this.ctx = ctx
this.metrics = metrics
}
/** @param {import('multiformats').MultihashDigest} digest */
async locate (digest) {
const key = this.toCacheKey(digest)
const cached = await this.cache.match(key)
if (cached) {
this.metrics.indexes++
this.metrics.indexesCached++
return { ok: Location.decode(new Uint8Array(await cached.arrayBuffer())) }
}
const res = await this.locator.locate(digest)
if (res.ok) {
this.metrics.indexes++
this.ctx.waitUntil(this.cache.put(key, new Response(Location.encode(res.ok))))
}
return res
}
/** @param {import('multiformats').MultihashDigest} digest */
toCacheKey (digest) {
const key = base58btc.encode(digest.bytes)
const cacheUrl = new URL(key, 'https://index.web3.storage')
return new Request(cacheUrl.toString(), {
method: 'GET',
headers: new Headers({ 'content-type': 'application/octet-stream' })
})
}
}
/**
* Cache block bytes for a CID
*/
export class CachingBlockStore {
/**
* @param {Blockstore} blockstore
* @param {Cache} cache
* @param {ExecutionContext} ctx
* @param {import('./metrics.js').Metrics} metrics
*/
constructor (blockstore, cache, ctx, metrics) {
this.blockstore = blockstore
this.cache = cache
this.ctx = ctx
this.metrics = metrics
}
/**
* @param {UnknownLink} cid
*/
async has (cid) {
const key = this.toCacheKey(cid)
const cached = await this.cache.match(key)
if (cached) return true
return this.blockstore.has(cid)
}
/**
* @param {UnknownLink} cid
*/
async get (cid) {
const key = this.toCacheKey(cid)
const cached = await this.cache.match(key)
if (cached) {
const buff = await cached.arrayBuffer()
const bytes = new Uint8Array(buff)
this.metrics.blocks++
this.metrics.blocksCached++
this.metrics.blockBytes += bytes.byteLength
this.metrics.blockBytesCached += bytes.byteLength
return bytes
}
const res = await this.blockstore.get(cid)
if (res) {
this.ctx.waitUntil(this.cache.put(key, new Response(res)))
return res
}
}
/**
* @param {UnknownLink} cid
*/
toCacheKey (cid) {
const cacheUrl = new URL(`/ipfs/${cid}?format=raw`, 'https://ipfs.io')
return new Request(cacheUrl.toString(), {
method: 'GET'
})
}
}
export class BlockStore {
/**
* @param {import('@web3-storage/blob-fetcher').Locator} locator
* @param {import('./metrics.js').Metrics} metrics
*/
constructor (locator, metrics) {
this.fetcher = BatchingFetcher.create(locator)
this.locator = locator
this.metrics = metrics
}
/** @param {UnknownLink} cid */
async has (cid) {
const res = await this.locator.locate(cid.multihash)
return Boolean(res.ok)
}
/** @param {UnknownLink} cid */
async get (cid) {
const res = await this.fetcher.fetch(cid.multihash)
if (res.ok) {
const bytes = await res.ok.bytes()
this.metrics.blocksFetched++
this.metrics.blockBytesFetched += bytes.byteLength
return bytes
}
}
}