File size: 3,036 Bytes
ddce7e8 | 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 | import axios from 'axios';
import dns from 'dns';
import http from 'http';
import https from 'https';
import { Readable } from 'stream';
import config from '../config/config.js';
// ==================== DNS & 代理统一配置 ====================
// 自定义 DNS 解析:优先 IPv4,失败则回退 IPv6
function customLookup(hostname, options, callback) {
dns.lookup(hostname, { ...options, family: 4 }, (err4, address4, family4) => {
if (!err4 && address4) {
return callback(null, address4, family4);
}
dns.lookup(hostname, { ...options, family: 6 }, (err6, address6, family6) => {
if (!err6 && address6) {
return callback(null, address6, family6);
}
callback(err4 || err6);
});
});
}
// 使用自定义 DNS 解析的 Agent(优先 IPv4,失败则 IPv6)
const httpAgent = new http.Agent({
lookup: customLookup,
keepAlive: true
});
const httpsAgent = new https.Agent({
lookup: customLookup,
keepAlive: true
});
// 统一构建代理配置
function buildProxyConfig() {
if (!config.proxy) return false;
try {
const proxyUrl = new URL(config.proxy);
return {
protocol: proxyUrl.protocol.replace(':', ''),
host: proxyUrl.hostname,
port: parseInt(proxyUrl.port, 10)
};
} catch {
return false;
}
}
// 将数据转换为流以启用 chunked 编码
function createChunkedStream(data) {
const jsonStr = typeof data === 'string' ? data : JSON.stringify(data);
return Readable.from([jsonStr]);
}
// 为 axios 构建统一请求配置
export function buildAxiosRequestConfig({
method = 'POST',
url,
headers,
data = null,
timeout = config.timeout,
responseType,
useChunked = false
}) {
const axiosConfig = {
method,
url,
headers: { ...headers },
timeout,
httpAgent,
httpsAgent,
proxy: buildProxyConfig(),
// 禁用自动设置 Content-Length,让 axios 使用 Transfer-Encoding: chunked
maxContentLength: Infinity,
maxBodyLength: Infinity
};
if (responseType) axiosConfig.responseType = responseType;
if (data !== null) {
if (useChunked) {
// 使用流式数据以启用 chunked 编码
axiosConfig.data = createChunkedStream(data);
// 删除 Content-Length 头,强制使用 chunked
delete axiosConfig.headers['Content-Length'];
} else {
axiosConfig.data = data;
}
}
return axiosConfig;
}
// 简单封装 axios 调用,方便后续统一扩展(重试、打点等)
export async function httpRequest(configOverrides) {
// 默认启用 chunked 编码以匹配官方客户端行为
const axiosConfig = buildAxiosRequestConfig({ ...configOverrides, useChunked: true });
return axios(axiosConfig);
}
// 流式请求封装
export async function httpStreamRequest(configOverrides) {
// 默认启用 chunked 编码以匹配官方客户端行为
const axiosConfig = buildAxiosRequestConfig({ ...configOverrides, useChunked: true });
axiosConfig.responseType = 'stream';
return axios(axiosConfig);
}
|