From 69a029190dfd11c851a448e52d7d3b123014b9d5 Mon Sep 17 00:00:00 2001 From: Shigma Date: Tue, 22 Aug 2023 00:58:12 +0800 Subject: [PATCH] feat(axios): support experimental ctx.http.url() --- packages/axios/src/index.ts | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/axios/src/index.ts b/packages/axios/src/index.ts index 426b7cf0..50c78522 100644 --- a/packages/axios/src/index.ts +++ b/packages/axios/src/index.ts @@ -1,6 +1,6 @@ import { Context } from 'cordis' import { Agent } from 'agent-base' -import { base64ToArrayBuffer, Dict, pick, trimSlash } from 'cosmokit' +import { arrayBufferToBase64, base64ToArrayBuffer, Dict, pick, trimSlash } from 'cosmokit' import { ClientRequestArgs } from 'http' import mimedb from 'mime-db' import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios' @@ -18,6 +18,41 @@ declare module 'cordis' { } } +/* eslint-disable no-multi-spaces */ +const ranges = [ + '0.0.0.0/8', // RFC 1122 'this' network + '10.0.0.0/8', // RFC 1918 private space + '100.64.0.0/10', // RFC 6598 Carrier grade nat space + '127.0.0.0/8', // RFC 1122 localhost + '169.254.0.0/16', // RFC 3927 link local + '172.16.0.0/12', // RFC 1918 private space + '192.0.2.0/24', // RFC 5737 TEST-NET-1 + '192.88.99.0/24', // RFC 7526 6to4 anycast relay + '192.168.0.0/16', // RFC 1918 private space + '198.18.0.0/15', // RFC 2544 benchmarking + '198.51.100.0/24', // RFC 5737 TEST-NET-2 + '203.0.113.0/24', // RFC 5737 TEST-NET-3 + '224.0.0.0/4', // multicast + '240.0.0.0/4', // reserved +] +/* eslint-enable no-multi-spaces */ + +function addressToNumber(address: string) { + return address.split('.').reduce((a, b) => (a << 8n) + BigInt(b), 0n) +} + +function isPrivate(hostname: string) { + if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.test(hostname)) return false + for (const cidr of ranges) { + const [address, length] = cidr.split('/') + const mask = -1n << BigInt(32 - +length) + const value1 = addressToNumber(hostname) & mask + const value2 = addressToNumber(address) & mask + if (value1 === value2) return true + } + return false +} + export interface Quester { (method: Method, url: string, config?: AxiosRequestConfig): Promise axios(config?: AxiosRequestConfig): Promise> @@ -111,6 +146,17 @@ export class Quester { } return { mime, filename: name, data } } + + async url(url: string) { + const { hostname } = new URL(url) + if (!isPrivate(hostname)) return url + const { headers, data } = await this.axios(url, { + method: 'GET', + responseType: 'arraybuffer', + }) + const mime = headers['content-type'] + return `data:${mime};base64,${arrayBufferToBase64(data)}` + } } export namespace Quester {