Edge Functions 用例
以下用例仅作为示例提供,并非旨在实现环境中的完全重复。
测试以下任何代码时请谨慎操作,因为这可能会导致服务中断。
A/B 测试
您可以创建 CIS Edge Function 以控制 A/B 测试。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
const name = 'experiment-0'
let group // 'control' or 'test', set below
let isNew = false // is the group newly-assigned?
// Determine which group this request is in.
const cookie = request.headers.get('Cookie')
if (cookie && cookie.includes(`${name}=control`)) {
group = 'control'
} else if (cookie && cookie.includes(`${name}=test`)) {
group = 'test'
} else {
// 50/50 Split
group = Math.random() < 0.5 ? 'control' : 'test'
isNew = true
}
// We'll prefix the request path with the experiment name. This way,
// the origin server merely has to have two copies of the site under
// top-level directories named "control" and "test".
let url = new URL(request.url)
// Note that `url.pathname` always begins with a `/`, so we don't
// need to explicitly add one after `${group}`.
url.pathname = `/${group}${url.pathname}`
const modifiedRequest = new Request(url, {
method: request.method,
headers: request.headers
})
const response = await fetch(modifiedRequest)
if (isNew) {
// The experiment was newly-assigned, so add a Set-Cookie header
// to the response.
const newHeaders = new Headers(response.headers)
newHeaders.append('Set-Cookie', `${name}=${group}; path=/`)
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
} else {
// Return response unmodified.
return response
}
}
添加响应头
要修改响应头,请先生成响应的副本,以便可以使其可变。 然后,您可以使用标题界面添加、更改或删除标题。
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Set the `x-my-header` header
* @param {Request} request
*/
async function handleRequest(request) {
let response = await fetch(request);
// Make the headers mutable by re-constructing the Response.
response = new Response(response.body, response);
response.headers.set('x-my-header', 'custom value');
return response;
}
聚集多个请求
该示例向不同的API端点发出多个请求,汇总响应,并将其作为单个响应发送回来。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Make multiple requests,
* aggregate the responses and
* send it back as a single response
*/
async function fetchAndApply(request) {
const init = {
method: 'GET',
headers: {'Authorization': 'XXXXXX'}
}
const [btcResp, ethResp, ltcResp] = await Promise.all([
fetch('https://api.coinbase.com/v2/prices/BTC-USD/spot', init),
fetch('https://api.coinbase.com/v2/prices/ETH-USD/spot', init),
fetch('https://api.coinbase.com/v2/prices/LTC-USD/spot', init)
])
const btc = await btcResp.json()
const eth = await ethResp.json()
const ltc = await ltcResp.json()
let combined = {}
combined['btc'] = btc['data'].amount
combined['ltc'] = ltc['data'].amount
combined['eth'] = eth['data'].amount
const responseInit = {
headers: {'Content-Type': 'application/json'}
}
return new Response(JSON.stringify(combined), responseInit)
}
条件路由
根据所使用的设备提供不同内容的最简单方法是,根据您所关心的条件重写请求的 URL。 请参阅以下示例。
设备类型
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
let uaSuffix = ''
const ua = request.headers.get('user-agent')
if (ua.match(/iphone/i) || ua.match(/ipod/i)) {
uaSuffix = '/mobile'
} else if (ua.match(/ipad/i)) {
uaSuffix = '/tablet'
}
return fetch(request.url + uaSuffix, request)
}
定制标头
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
let suffix = ''
//Assuming that the client is sending a custom header
const cryptoCurrency = request.headers.get('X-Crypto-Currency')
if (cryptoCurrency === 'BTC') {
suffix = '/btc'
} else if (cryptoCurrency === 'XRP') {
suffix = '/xrp'
} else if (cryptoCurrency === 'ETH') {
suffix = '/eth'
}
return fetch(request.url + suffix, request)
}
热链接保护
您可以使用 CIS Edge Functions 来保护 Web 属性上的热链接。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* If the browser is requesting an image and
* the referer does not match your host
* we redirect the request to your page
*/
async function fetchAndApply(request) {
// Fetch the response.
let response = await fetch(request)
// If it's an image, engage hotlink protection based on the
// Referer header.
let referer = request.headers.get('Referer')
let contentType = response.headers.get('Content-Type') || ''
if (referer && contentType.startsWith('image/')) {
// It's an image and there's a Referer. Verify that the
// hostnames match.
if (new URL(referer).hostname !==
new URL(request.url).hostname) {
// Hosts don't match. This is a hotlink. Redirect the
// user to our homepage.
return new Response('', {
status: 302,
headers: {
'Location': '/'
}
})
}
}
// Everything is fine, return the response normally.
return response
}
无源响应
您可以直接从 Edge 返回响应。 无需向您的源发送请求。
忽略 POST 和 PUT HTTP 请求
忽略 POST 和 PUT HTTP 请求。 此片段允许将所有其他请求传递到源。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.method === 'POST' || request.method === 'PUT') {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
拒绝 Spider 或搜寻器
保护源避免不想要的 spider 或搜寻器。 在此情况下,如果用户代理程序为“annoying-robot”,那么 Edge 函数会返回响应,而不是将请求发送到源。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.headers.get('user-agent').includes('annoying_robot')) {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
阻止特定 IP 的连接
阻止列表 IP 地址。 这段代码阻止特定IP(此处为 225.0.0.1
)连接到源站。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
async function fetchAndApply(request) {
if (request.headers.get('cf-connecting-ip') === '225.0.0.1') {
return new Response('Sorry, this page is not available.',
{ status: 403, statusText: 'Forbidden' })
}
return fetch(request)
}
Post 请求
从 HTTP POST请求中读取内容:
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Making a curl request that looks like
* curl -X POST --data 'key=world' example.com
* or
* curl -X POST --form 'key=world' example.com
*/
async function fetchAndApply(request) {
try {
const postData = await request.formData();
return new Response(`hello ${postData.get('key')}`)
} catch (err) {
return new Response('could not unbundle post data')
}
}
从边缘函数创建一个 HTTP POST请求:
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Create a POST request with body 'key=world'
* Here, we are assuming that example.com acknowledges the POST request with body key=world
*/
async function fetchAndApply(request) {
let content = 'key=world'
let headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
const init = {
method: 'POST',
headers: headers,
body: content
}
const response = await fetch('https://example.com', init)
console.log('Got response', response)
return response
}
已签名请求
一种名为请求签名的通用 URL 认证方法可以在Edge函数中借助Web Crypto API实现。
在此示例中,CIS 使用基于哈希的消息认证码(HMAC)和 SHA-256 摘要算法,对 URL 的路径以及随附的过期时间戳进行验证。 要成功访存已认证的资源,用户代理程序需要使用查询参数提供正确的路径,到期时间戳记和 HMAC。 如果这三个参数中的任何一个被篡改,那么请求将失败。
过期时间戳的真实性由 HMAC 保证,这意味着如果 HMAC 正确,则可以相信用户提供的过期时间戳是正确的,并且当 URL 过期时。 您还可以确定他们持有的 URL 是否已过期。
验证已签名请求
此示例验证了路径名称以 /verify/
开头的任何请求 URL 的HMAC。
为方便调试,如果 URL 或HMAC无效,或者 URL 过期,则此边缘函数将返回403消息。 在实际应用中,你可能需要返回404。
addEventListener('fetch', event => {
event.respondWith(verifyAndFetch(event.request))
})
async function verifyAndFetch(request) {
const url = new URL(request.url)
// If the path doesn't begin with our protected prefix, just pass the request
// through.
if (!url.pathname.startsWith("/verify/")) {
return fetch(request)
}
// Make sure we have the minimum necessary query parameters.
if (!url.searchParams.has("mac") || !url.searchParams.has("expiry")) {
return new Response("Missing query parameter", { status: 403 })
}
// We'll need some super-secret data to use as a symmetric key.
const encoder = new TextEncoder()
const secretKeyData = encoder.encode("my secret symmetric key")
const key = await crypto.subtle.importKey(
"raw", secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false, [ "verify" ]
)
// Extract the query parameters we need and run the HMAC algorithm on the
// parts of the request we're authenticating: the path and the expiration
// timestamp.
const expiry = Number(url.searchParams.get("expiry"))
const dataToAuthenticate = url.pathname + expiry
// The received MAC is Base64-encoded, so we have to go to some trouble to
// get it into a buffer type that crypto.subtle.verify() can read.
const receivedMacBase64 = url.searchParams.get("mac")
const receivedMac = byteStringToUint8Array(atob(receivedMacBase64))
// Use crypto.subtle.verify() to guard against timing attacks. Since HMACs use
// symmetric keys, we could implement this by calling crypto.subtle.sign() and
// then doing a string comparison -- this is insecure, as string comparisons
// bail out on the first mismatch, which leaks information to potential
// attackers.
const verified = await crypto.subtle.verify(
"HMAC", key,
receivedMac,
encoder.encode(dataToAuthenticate)
)
if (!verified) {
const body = "Invalid MAC"
return new Response(body, { status: 403 })
}
if (Date.now() > expiry) {
const body = `URL expired at ${new Date(expiry)}`
return new Response(body, { status: 403 })
}
// We've verified the MAC and expiration time; we're good to pass the request
// through.
return fetch(request)
}
// Convert a ByteString (a string whose code units are all in the range
// [0, 255]), to a Uint8Array. If you pass in a string with code units larger
// than 255, their values overflow!
function byteStringToUint8Array(byteString) {
const ui = new Uint8Array(byteString.length)
for (let i = 0; i < byteString.length; ++i) {
ui[i] = byteString.charCodeAt(i)
}
return ui
}
生成已签名请求
通常,已签名的请求会以某种频带外方式 (例如,电子邮件) 传递给您,或者您自己生成一个请求 (如果您具有对称密钥)。 您还可以在 Edge 函数中生成已签名的请求。
对于任何以 /generate/
开头的请求 URL,CIS 会用 /verify/
替换 /generate/
,并在生成的路径上签名,同时用时间戳标记路径,最后在响应正文中返回完整的签名 URL。
addEventListener('fetch', event => {
const url = new URL(event.request.url)
const prefix = "/generate/"
if (url.pathname.startsWith(prefix)) {
// Replace the "/generate/" path prefix with "/verify/", which we
// use in the first example to recognize authenticated paths.
url.pathname = `/verify/${url.pathname.slice(prefix.length)}`
event.respondWith(generateSignedUrl(url))
} else {
event.respondWith(fetch(event.request))
}
})
async function generateSignedUrl(url) {
// We'll need some super-secret data to use as a symmetric key.
const encoder = new TextEncoder()
const secretKeyData = encoder.encode("my secret symmetric key")
const key = await crypto.subtle.importKey(
"raw", secretKeyData,
{ name: "HMAC", hash: "SHA-256" },
false, [ "sign" ]
)
// Signed requests expire after one minute. Note that you could choose
// expiration durations dynamically, depending on, e.g. the path or a query
// parameter.
const expirationMs = 60000
const expiry = Date.now() + expirationMs
const dataToAuthenticate = url.pathname + expiry
const mac = await crypto.subtle.sign(
"HMAC", key,
encoder.encode(dataToAuthenticate)
)
// `mac` is an ArrayBuffer, so we need to jump through a couple hoops to get
// it into a ByteString, then a Base64-encoded string.
const base64Mac = btoa(String.fromCharCode(...new Uint8Array(mac)))
url.searchParams.set("mac", base64Mac)
url.searchParams.set("expiry", expiry)
return new Response(url)
}
以流式方法传递响应
在向 event.respondWith()
发送响应之前,边缘函数脚本无需准备整个响应主体。 通过使用 TransformStream,,您可以在发送响应的前置信息(例如,HTTP 状态行和标题)后,流式传输响应正文。 这种精简有助于 CIS 将访问者首次访问所需的时间以及Edge功能脚本中必须进行的缓冲量降到最低。
如果必须处理或变换大于 Edge 函数内存限制的响应主体,那么最大程度地减少缓冲尤为重要。 在这些情况下,流式方法是唯一可行的实现策略。
缺省情况下,CIS Edge Function 服务已尽可能以流式方式进行传递。 仅当您希望以某种方式修改响应主体,同时保持流式方法行为时,才需要这些 API。 如果 Edge 函数脚本将子请求响应逐字传递回客户机,而不读取其主体,那么主体处理已处于最佳状态。
流式传递
开始使用以下最小传递示例。
addEventListener("fetch", event => {
event.respondWith(fetchAndStream(event.request))
})
async function fetchAndStream(request) {
// Fetch from origin server.
let response = await fetch(request)
// Create an identity TransformStream (a.k.a. a pipe).
// The readable side becomes our new response body.
let { readable, writable } = new TransformStream()
// Start pumping the body. NOTE: No await!
streamBody(response.body, writable)
// ... and deliver our Response while that's running.
return new Response(readable, response)
}
async function streamBody(readable, writable) {
let reader = readable.getReader()
let writer = writable.getWriter()
while (true) {
const { done, value } = await reader.read()
if (done) break
// Optionally transform value's bytes here.
await writer.write(value)
}
await writer.close()
}
需要注意的一些重要细节:
- 虽然
streamBody()
是异步函数,但您不希望对其调用await
,以便它不会阻止调用fetchAndStream()
函数的进度。 此函数在其具有未完成的reader.read()
或writer.write()
操作的时间段内继续异步运行。 - 背压:
await
在调用写操作之前执行读操作。 同样,在执行下一次读取操作之前,请await
写入操作。 遵循此模式会将反压传播到源。 - 完成:最后请致电
writer.close()
,这表示您已完成此响应正文的编写,并通知Edge功能运行时。 调用后,streamBody()
将终止-如果此行为不理想,请将其返回的 promise 传递给FetchEvent.waitUntil()
。 如果您的脚本从未调用writer.close()
,则运行时显示的正文会截断,但可能仍能按预期运行。
聚集和以流式方法传递多个请求
这个用例与聚合多个请求的方案类似,但这次你会在验证每个子请求成功后立即开始编写响应——无需等待响应正文。
addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request))
})
/**
* Make multiple requests,
* aggregate the responses and
* stream it back as a single response.
*/
async function fetchAndApply(request) {
const requestInit = {
headers: { "Authorization": "XXXXXX" }
}
const fetches = [
"https://api.coinbase.com/v2/prices/BTC-USD/spot",
"https://api.coinbase.com/v2/prices/ETH-USD/spot",
"https://api.coinbase.com/v2/prices/LTC-USD/spot"
].map(url => fetch(url, requestInit))
// Wait for each fetch() to complete.
let responses = await Promise.all(fetches)
// Make sure every subrequest succeeded.
if (!responses.every(r => r.ok)) {
return new Response(null, { status: 502 })
}
// Create a pipe and stream the response bodies out
// as a JSON array.
let { readable, writable } = new TransformStream()
streamJsonBodies(responses.map(r => r.body), writable)
return new Response(readable)
}
async function streamJsonBodies(bodies, writable) {
// We're presuming these bodies are JSON, so we
// concatenate them into a JSON array. Since we're
// streaming, we can't use JSON.stringify(), but must
// instead manually write an initial '[' before the
// bodies, interpolate ',' between them, and write a
// terminal ']' after them.
let writer = writable.getWriter()
let encoder = new TextEncoder()
await writer.write(encoder.encode("[\n"))
for (let i = 0; i < bodies.length; ++i) {
if (i > 0) {
await writer.write(encoder.encode(",\n"))
}
writer.releaseLock()
await bodies[i].pipeTo(writable, { preventClose: true })
writer = writable.getWriter()
}
await writer.write(encoder.encode("]"))
await writer.close()
}
运行时期望在 TransformStream 的可读端接收 TypedArray。 因此,您从不将字符串传递到 writer.write()
,仅传递到 Uint 8Arrays。 如果需要编写字符串,请使用 TextEncoder。
具有 Edge 函数的定制负载均衡器
负载均衡可帮助您保持所托管 Web 站点的可伸缩性和可靠性。 您可以使用 Edge 函数来创建定制负载均衡器,这些负载均衡器旨在满足您的特定需求。
const US_HOSTS = [
"0.us.example.com",
"1.us.example.com",
"2.us.example.com"
];
const IN_HOSTS = [
"0.in.example.com",
"1.in.example.com",
"2.in.example.com"
];
var COUNTRIES_MAP = {
IN: IN_HOSTS,
PK: IN_HOSTS,
BD: IN_HOSTS,
SL: IN_HOSTS,
NL: IN_HOSTS
}
addEventListener('fetch', event => {
var url = new URL(event.request.url);
var countryCode = event.request.headers.get('CF-IPCountry');
var hostnames = US_HOSTS;
if (COUNTRIES_MAP[countryCode]) {
hostnames = COUNTRIES_MAP[countryCode];
}
// Randomly pick the next host
var primary = hostnames[getRandomInt(hostnames.length)];
var primaryUrl = new URL(event.request.url);
primaryUrl.hostname = hostnames[primary];
// Fallback if there is no response within timeout
var timeoutId = setTimeout(function() {
var backup;
do {
// Naive solution to pick a backup host
backup = getRandomInt(hostnames.length);
} while(backup === primary);
var backupUrl = new URL(event.request.url);
backupUrl.hostname = hostnames[backup];
event.respondWith(fetch(backupUrl));
}, 2000 /* 2 seconds */);
fetch(primaryUrl)
.then(function(response) {
clearTimeout(timeoutId);
event.respondWith(response);
});
});
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
使用访存进行高速缓存
通过在访存请求中设置 TTL,定制高速缓存密钥和高速缓存头,确定如何对资源进行高速缓存。
async function handleRequest(request) {
const url = new URL(request.url)
// Only use the path for the cache key, removing query strings
// and always store using HTTPS, for example, https://www.example.com/file-uri-here
const someCustomKey = `https://${url.hostname}${url.pathname}`
let response = await fetch(request, {
cf: {
// Always cache this fetch regardless of content type
// for a max of 5 seconds before revalidating the resource
cacheTtl: 5,
cacheEverything: true,
//Enterprise only feature, see Cache API for other plans
cacheKey: someCustomKey,
},
})
// Reconstruct the Response object to make its headers mutable.
response = new Response(response.body, response)
//Set cache control headers to cache on browser for 25 minutes
response.headers.set("Cache-Control", "max-age=1500")
return response
}
addEventListener("fetch", event => {
return event.respondWith(handleRequest(event.request))
})
高速缓存 HTML 资源
// Force CIS to cache an asset
fetch(event.request, { cf: { cacheEverything: true } })
将高速缓存级别设置为“高速缓存所有内容”将覆盖资产的缺省“可高速缓存性”。 对于 TTL,CIS 仍依赖于源设置的头。
定制高速缓存密钥
此功能仅适用于企业客户。
请求的高速缓存键用于确定两个请求是否“相同”以进行高速缓存。 如果请求具有与先前某个请求相同的高速缓存密钥,那么我们可以为这两个请求提供相同的高速缓存响应。
// Set cache key for this request to "some-string".
fetch(event.request, { cf: { cacheKey: "some-string" } })
CIS 根据请求的 计算缓存密钥,但出于缓存目的,您可能希望将不同的URL视为相同的URL。URL 例如,如果您的网站内容同时托管在 Amazon S3 和 Google Cloud Storage (您在两个地方都有相同的内容),然后您使用边缘函数在两者之间随机平衡。 但是,您不希望高速缓存内容的两个副本。 您可以使用自定义缓存键,根据原始请求 URL 而不是子请求 URL 进行缓存。
addEventListener("fetch", (event) => {
let url = new URL(event.request.url)
if (Math.random() < 0.5) {
url.hostname = "example.s3.amazonaws.com"
}
else {
url.hostname = "example.storage.googleapis.com"
}
let request = new Request(url, event.request)
event.respondWith(
fetch(request, {
cf: { cacheKey: event.request.url },
})
)
})
请记住,代表不同区域运行的边缘功能不会影响彼此的缓存。 仅当您在自己的区域中发出请求 (在先前的示例中,event.request.url
是存储的密钥) 或向不在 CIS上的主机发出请求时,才能覆盖高速缓存密钥。 当您向另一个 CIS 区域(例如,属于不同 CIS 客户的区域)提出请求时,该区域将完全控制其内容在 CIS 中的缓存方式;您无法覆盖它。
基于源响应代码的覆盖
此功能仅适用于企业客户。
// Force response to be cached for 86400 seconds for 200 status
// codes, 1 second for 404, and do not cache 500 errors.
fetch(request, {
cf: { cacheTtlByStatus: { "200-299": 86400, 404: 1, "500-599": 0 } },
})
此选项是 cacheTtl
功能部件的版本,该功能部件根据响应的状态码选择 TTL,并且不会自动设置 cacheEverything: true
。 如果对此请求的响应具有匹配的状态码,那么 CIS 将在指示的时间进行高速缓存,并覆盖由源发送的高速缓存伪指令。
TTL 解释
以下 TTL 值由 CIS解释。
- 正值: 指示 CIS 应该为其高速缓存资产的时间长度 (以秒计)。
0
: 资产已高速缓存但立即到期 (每次从源重新验证)。-1
或任何负值: 指示 CIS 完全不高速缓存。
高速缓存 API
使用 CIS 高速缓存 API 进行高速缓存。 此示例还可以对 POST 请求进行高速缓存。
const someOtherHostname = "my.herokuapp.com"
async function handleRequest(event) {
const request = event.request
const cacheUrl = new URL(request.url)
// Hostname for a different zone
cacheUrl.hostname = someOtherHostname
const cacheKey = new Request(cacheUrl.toString(), request)
const cache = caches.default
// Get this request from this zone's cache
let response = await cache.match(cacheKey)
if (!response) {
//If not in cache, get it from origin
response = await fetch(request)
// Must use Response constructor to inherit all of response's fields
response = new Response(response.body, response)
// Cache API respects Cache-Control headers. Setting max-age to 10
// will limit the response to be in cache for 10 seconds max
response.headers.append("Cache-Control", "max-age=10")
// Store the fetched response as cacheKey
// Use waitUntil so computational expensive tasks don"t delay the response
event.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
async function sha256(message) {
// encode as UTF-8
const msgBuffer = new TextEncoder().encode(message)
// hash the message
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer)
// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer))
// convert bytes to hex string
const hashHex = hashArray.map(b => ("00" + b.toString(16)).slice(-2)).join("")
return hashHex
}
async function handlePostRequest(event) {
const request = event.request
const body = await request.clone().text()
const hash = await sha256(body)
const cacheUrl = new URL(request.url)
// Store the URL in cache by prepending the body's hash
cacheUrl.pathname = "/posts" + cacheUrl.pathname + hash
// Convert to a GET to be able to cache
const cacheKey = new Request(cacheUrl.toString(), {
headers: request.headers,
method: "GET",
})
const cache = caches.default
//Find the cache key in the cache
let response = await cache.match(cacheKey)
// Otherwise, fetch response to POST request from origin
if (!response) {
response = await fetch(request)
event.waitUntil(cache.put(cacheKey, response.clone()))
}
return response
}
addEventListener("fetch", event => {
try {
const request = event.request
if (request.method.toUpperCase() === "POST")
return event.respondWith(handlePostRequest(event))
return event.respondWith(handleRequest(event))
} catch (e) {
return event.respondWith(new Response("Error thrown " + e.message))
}
})