This tutorial is only for learning to communicate
Cloudflare Workers
Official Introduction:
Running code on the edge provides strong Web scalability
Enhance security by applying custom security rules and filtering logic to edges to detect malicious Bots viruses and prevent them from consuming resources.
Incorporate more personalization and interactivity into static HTML pages and run dynamic requests on the edge to improve the user experience.
Move more operations and request processing to the edge to improve cache hit rates and reduce bandwidth costs, thereby reducing operating costs.
Simply put, you can run your code without a server.
Without worrying about server costs, zombie traffic attacks, or deploying server resources and architectures, you can write code directly and develop program services.
Tools available:
- Run any JavaScript code, using the latest standard language features;
- Intercept and modify HTTP requests in response to URL s, status, header information, and body;
- Response to requests directly from Worker or forward to other places;
- Send HTTP requests to third-party servers;
- Send multiple requests serially or in parallel, combining the responses of these requests into the final response of the original request;
- Send an asynchronous request (for example, log or analysis) after the response is returned to the client;
- Control other Cloudflare features, such as caching behavior.
- Wait...
The free version supports 100,000 free requests per day, which is almost enough for daily use.
Using tutorials
Earlier registration without a Cloudflare account
open https://workers.cloudflare.com , log in to your Cloudflare account to activate the Workers service
Then click Create a Worker to create one, which illustrates the picture
In the code area on the left side of the script, once you've finished writing, click Save and Deploy to save and deploy, and you'll be able to view your services.
Domain name format:https:// Custom name. Cloudflare username. workers.dev
jsproxy
jsproxy is an online proxy based on browser-side JS implementation
Deployment Agent
We need to implement the proxy with Cloudflare Workers created above
We need to copy jsproxy's code into the script you created Cloudflare Workers and save the deployment.
Code address: https://github.com/EtherDream/jsproxy/blob/master/cf-worker/index.js (There is a code backup below the article)
Preview below:
Custom domain name
If you use a custom domain name, you need to configure a domain name CNAME resolution binding: xxx.workers.dev and turn on the CDN, which lights up the Yellow cloud Icon
Finally: When browsing the website, sometimes you are prompted to load unsafe scripts, just click Allow!jsproxy can also use github pages to implement online proxies, which are not covered here.
Code Backup
'use strict' /** * static files (404.html, sw.js, conf.js) */ const ASSET_URL = 'https://etherdream.github.io/jsproxy' const JS_VER = 10 const MAX_RETRY = 1 /** @type {RequestInit} */ const PREFLIGHT_INIT = { status: 204, headers: new Headers({ 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS', 'access-control-max-age': '1728000', }), } /** * @param {any} body * @param {number} status * @param {Object<string, string>} headers */ function makeRes(body, status = 200, headers = {}) { headers['--ver'] = JS_VER headers['access-control-allow-origin'] = '*' return new Response(body, {status, headers}) } /** * @param {string} urlStr */ function newUrl(urlStr) { try { return new URL(urlStr) } catch (err) { return null } } addEventListener('fetch', e => { const ret = fetchHandler(e) .catch(err => makeRes('cfworker error:\n' + err.stack, 502)) e.respondWith(ret) }) /** * @param {FetchEvent} e */ async function fetchHandler(e) { const req = e.request const urlStr = req.url const urlObj = new URL(urlStr) const path = urlObj.href.substr(urlObj.origin.length) if (urlObj.protocol === 'http:') { urlObj.protocol = 'https:' return makeRes('', 301, { 'strict-transport-security': 'max-age=99999999; includeSubDomains; preload', 'location': urlObj.href, }) } if (path.startsWith('/http/')) { return httpHandler(req, path.substr(6)) } switch (path) { case '/http': return makeRes('Please update cfworker To the latest version!') case '/ws': return makeRes('not support', 400) case '/works': return makeRes('it works') default: // static files return fetch(ASSET_URL + path) } } /** * @param {Request} req * @param {string} pathname */ function httpHandler(req, pathname) { const reqHdrRaw = req.headers if (reqHdrRaw.has('x-jsproxy')) { return Response.error() } // preflight if (req.method === 'OPTIONS' && reqHdrRaw.has('access-control-request-headers') ) { return new Response(null, PREFLIGHT_INIT) } let acehOld = false let rawSvr = '' let rawLen = '' let rawEtag = '' const reqHdrNew = new Headers(reqHdrRaw) reqHdrNew.set('x-jsproxy', '1') // The logic here is roughly the same as http-dec-req-hdr.lua // https://github.com/EtherDream/jsproxy/blob/master/lua/http-dec-req-hdr.lua const refer = reqHdrNew.get('referer') const query = refer.substr(refer.indexOf('?') + 1) if (!query) { return makeRes('missing params', 403) } const param = new URLSearchParams(query) for (const [k, v] of Object.entries(param)) { if (k.substr(0, 2) === '--') { // system information switch (k.substr(2)) { case 'aceh': acehOld = true break case 'raw-info': [rawSvr, rawLen, rawEtag] = v.split('|') break } } else { // Restore HTTP Request Header if (v) { reqHdrNew.set(k, v) } else { reqHdrNew.delete(k) } } } if (!param.has('referer')) { reqHdrNew.delete('referer') } // cfworker combines `//'in the path into `/` const urlStr = pathname.replace(/^(https?):\/+/, '$1://') const urlObj = newUrl(urlStr) if (!urlObj) { return makeRes('invalid proxy url: ' + urlStr, 403) } /** @type {RequestInit} */ const reqInit = { method: req.method, headers: reqHdrNew, redirect: 'manual', } if (req.method === 'POST') { reqInit.body = req.body } return proxy(urlObj, reqInit, acehOld, rawLen, 0) } /** * * @param {URL} urlObj * @param {RequestInit} reqInit * @param {number} retryTimes */ async function proxy(urlObj, reqInit, acehOld, rawLen, retryTimes) { const res = await fetch(urlObj.href, reqInit) const resHdrOld = res.headers const resHdrNew = new Headers(resHdrOld) let expose = '*' for (const [k, v] of resHdrOld.entries()) { if (k === 'access-control-allow-origin' || k === 'access-control-expose-headers' || k === 'location' || k === 'set-cookie' ) { const x = '--' + k resHdrNew.set(x, v) if (acehOld) { expose = expose + ',' + x } resHdrNew.delete(k) } else if (acehOld && k !== 'cache-control' && k !== 'content-language' && k !== 'content-type' && k !== 'expires' && k !== 'last-modified' && k !== 'pragma' ) { expose = expose + ',' + k } } if (acehOld) { expose = expose + ',--s' resHdrNew.set('--t', '1') } // verify if (rawLen) { const newLen = resHdrOld.get('content-length') || '' const badLen = (rawLen !== newLen) if (badLen) { if (retryTimes < MAX_RETRY) { urlObj = await parseYtVideoRedir(urlObj, newLen, res) if (urlObj) { return proxy(urlObj, reqInit, acehOld, rawLen, retryTimes + 1) } } return makeRes(res.body, 400, { '--error': `bad len: ${newLen}, except: ${rawLen}`, 'access-control-expose-headers': '--error', }) } if (retryTimes > 1) { resHdrNew.set('--retry', retryTimes) } } let status = res.status resHdrNew.set('access-control-expose-headers', expose) resHdrNew.set('access-control-allow-origin', '*') resHdrNew.set('--s', status) resHdrNew.set('--ver', JS_VER) resHdrNew.delete('content-security-policy') resHdrNew.delete('content-security-policy-report-only') resHdrNew.delete('clear-site-data') if (status === 301 || status === 302 || status === 303 || status === 307 || status === 308 ) { status = status + 10 } return new Response(res.body, { status, headers: resHdrNew, }) } /** * @param {URL} urlObj */ function isYtUrl(urlObj) { return ( urlObj.host.endsWith('.googlevideo.com') && urlObj.pathname.startsWith('/videoplayback') ) } /** * @param {URL} urlObj * @param {number} newLen * @param {Response} res */ async function parseYtVideoRedir(urlObj, newLen, res) { if (newLen > 2000) { return null } if (!isYtUrl(urlObj)) { return null } try { const data = await res.text() urlObj = new URL(data) } catch (err) { return null } if (!isYtUrl(urlObj)) { return null } return urlObj }