CloudFlare · 2023年7月27日 0

【CloudFlare】worker系列第二讲

在第一讲中我们介绍了worker的优点,接下来我们就来实现一个api proxy的功能,主要是为了熟悉worker的开发和部署流程。

功能介绍

由于一些原因呢,我们无法访问一些网站。假设我们无法访问https://api.google.com/v1/user/info,那么我们可以给worker发送一个如下的请求

GET http://xxx.worker.dev/v1/user/info
Host: xxx.worker.dev
Proxy-Host: api.google.com
Proxy-Proto: https

Worker会提取Header头里面的Proxy-Host和Proxy-Proto来构造请求https://api.google.com/v1/user/info,并将获得的请求返回给我们。

创建worker

访问你的worker页面https://dash.cloudflare.com/xxxxxxspaceid/workers-and-pages/create/workers/new

代码

addEventListener("fetch", (event) => {
	event.respondWith(handleRequest(event.request));
});


async function handleRequest(request) {
	// 提取原来请求的headers
	var hds = {};
	for (let k of request.headers.keys()){
		if (typeof k === "string"){
			// 不需要原来的host,发送代理请求时候,会用新的Host添加上去
			if(k === "host")continue;
			// 不需要cf开头的,不需要proxy-开头的
			if(k.startsWith("cf-") || k.startsWith("proxy-"))continue
			hds[k] = request.headers.get(k) || "";
		}
	}
	// 提取需要代理的协议和Host
	const proxy_host = request.headers.get('Proxy-Host') || "";
	let proxy_proto = request.headers.get('Proxy-Proto') || "";
	if (proxy_host ==="" || proxy_proto === ""){
		return new Response(
			JSON.stringify(hds), {status:401, headers: {"content-type": "application/json"}}
		)
	}
	if (!proxy_proto.endsWith(":")){
		proxy_proto += ":";
	}
	// 替换到真实的url
	const url = new URL(request.url);
	const fetchAPI = request.url.replace(url.host, proxy_host).replace(url.protocol, proxy_proto);
	// 支持option method,用来跨站
	const corsHeaders = {
	'Access-Control-Allow-Origin': '*',
	'Access-Control-Allow-Methods': 'OPTIONS',
	'Access-Control-Allow-Headers': '*',
	};
	if(request.method === "OPTIONS"){
		return new Response(null, {headers: corsHeaders});
	}

	// 构造代理请求,使用新的url,用原来的method,原来的headers,原来的body
	let areq = new Request(
		fetchAPI,
		{
			method: request.method,
			headers: new Headers(hds),
			body: request.body,
		}
	)
	console.log('method: '+ request.method + ' , url: ' + fetchAPI)
	// worker发送请求
	const response = await fetch(areq)
	// 将代理的response,发送给客户端
	return new Response(response.body, {
		status: response.status,
		statusText: response.statusText,
		headers: response.headers,
	});
}

CloudFlare有个比较好的地方,就是这个playground,既能编辑代码,又能测试,还能看到输入和输出,非常方便。

本次例子使用的是api-proxy,主要因为api的response一般都是json格式,因此只需要原封不动返回给客户端。而如果代理的是网页html的话,需要将Response里面的外链和资源的src都进行一个替换,所以会更复杂一些,我们就下一次再讲解。