-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
151 lines (129 loc) · 4.55 KB
/
index.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
// ENV VARS
EDGE_CACHE_TTL = EDGE_CACHE_TTL || 30
BROWSER_CACHE_TTL = BROWSER_CACHE_TTL || 0
PROVIDERS = JSON.parse(PROVIDERS)
PROVIDER_TIMEOUT = PROVIDER_TIMEOUT || 5000
async function sha256(message) {
const messageBuffer = new TextEncoder().encode(message)
const hashBuffer = await crypto.subtle.digest("SHA-256", messageBuffer)
const hashArr = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArr.map(b => ("00" + b.toString(16)).slice(-2)).join("")
return hashHex
}
async function fetchWithTimeout(url, request, timeout) {
return Promise.race([
fetch(url, request),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
])
}
async function tryProvider (url, request) {
const provider = new URL(PROVIDERS[Math.floor(Math.random() * PROVIDERS.length)])
url.hostname = provider.hostname
url.pathname = provider.pathname
let response
try {
response = await fetchWithTimeout(url, request.clone(), PROVIDER_TIMEOUT)
if (!response.ok) throw new Error(`${url.toString()} RPC ERROR`)
} catch (e) {
console.error(e)
response = await tryProvider(url, request)
}
return response
}
async function getOriginResponse (url, event, cacheKey) {
const cache = caches.default
let response = await tryProvider(url, event.request)
if (!cacheKey) return response
const headers = { 'Cache-Control': `public, max-age=${EDGE_CACHE_TTL}` }
response = new Response(response.body, { ...response, headers })
event.waitUntil(cache.put(cacheKey, response.clone()))
return response
}
async function formatResponse (response, body) {
let formattedResponse
if (body) {
const originalBody = await response.json()
const fullBody = JSON.stringify({ ...originalBody, ...body })
formattedResponse = new Response(fullBody, response)
} else {
formattedResponse = new Response(response.body, response)
}
formattedResponse.headers.set('Cache-Control', `max-age=${BROWSER_CACHE_TTL}`)
formattedResponse.headers.set('Access-Control-Allow-Method', '*')
formattedResponse.headers.set('Access-Control-Allow-Origin', '*')
formattedResponse.headers.set('Access-Control-Allow-Headers', '*')
return formattedResponse
}
async function handleOptions(event) {
const request = event.request
let headers = request.headers
if (
headers.get("Origin") !== null &&
headers.get("Access-Control-Request-Method") !== null &&
headers.get("Access-Control-Request-Headers") !== null
){
// Handle CORS
let corsHead = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS",
"Access-Control-Max-Age": "86400",
"Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers"),
}
return new Response(null, {
headers: corsHead,
})
} else {
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS",
},
})
}
}
async function handleGet(event) {
const request = event.request
const url = new URL(request.url)
const cache = caches.default
let response
response = await cache.match(request)
if (!response) {
response = await getOriginResponse(url, request, request)
}
return formatResponse(response)
}
async function handlePost(event) {
const request = event.request
const url = new URL(request.url)
const cache = caches.default
const body = await request.clone().json()
const { method, id, jsonrpc } = body
const bypassCache = method === 'eth_blockNumber'
if (bypassCache) {
const response = await getOriginResponse(url, event)
return formatResponse(response, { id })
}
const cacheable = { method, jsonrpc, ...body.params }
const cacheableBody = JSON.stringify(body.params)
const hash = await sha256(cacheableBody)
url.pathname = "/posts" + url.pathname + hash
const cacheKey = new Request(url.toString(), {
headers: request.headers,
method: "GET",
})
let response = await cache.match(cacheKey)
if (!response) {
response = await getOriginResponse(url, event, cacheKey)
}
return formatResponse(response, { id })
}
addEventListener("fetch", event => {
const request = event.request
if (request.method.toUpperCase() === "OPTIONS") {
return event.respondWith(handleOptions(event))
} else if (request.method.toUpperCase() === "POST") {
return event.respondWith(handlePost(event))
}
return event.respondWith(handleGet(event))
})