@@ -2,6 +2,15 @@ import path from 'path';
22import { existsSync } from 'fs' ;
33import { base64Decode } from '../libs_drpy/crypto-util.js' ;
44import * as drpy from '../libs/drpyS.js' ;
5+ // 创建 Agent 实例以复用 TCP 连接
6+ import http from 'http' ;
7+ import https from 'https' ;
8+
9+ // const AgentOption = { keepAlive: true, maxSockets: 100,timeout: 60000 }; // 最大连接数100,60秒定期清理空闲连接
10+ const AgentOption = { keepAlive : true } ;
11+ const httpAgent = new http . Agent ( AgentOption ) ;
12+ const httpsAgent = new https . Agent ( AgentOption ) ;
13+
514
615export default ( fastify , options , done ) => {
716 // 动态加载模块并根据 query 执行不同逻辑
@@ -117,6 +126,7 @@ export default (fastify, options, done) => {
117126 }
118127 const proxyPath = request . params [ '*' ] ; // 捕获整个路径
119128 fastify . log . info ( `try proxy for ${ moduleName } -> ${ proxyPath } : ${ JSON . stringify ( query ) } ` ) ;
129+ const rangeHeader = request . headers . range ; // 获取客户端的 Range 请求头
120130 const protocol = request . protocol ;
121131 const hostname = request . hostname ;
122132 const proxyUrl = `${ protocol } ://${ hostname } ${ request . url } ` . split ( '?' ) [ 0 ] . replace ( proxyPath , '' ) + '?do=js' ;
@@ -133,8 +143,8 @@ export default (fastify, options, done) => {
133143 let content = backRespList [ 2 ] || '' ;
134144 const headers = backRespList . length > 3 ? backRespList [ 3 ] : null ;
135145 const toBytes = backRespList . length > 4 ? backRespList [ 4 ] : null ;
136- // 如果需要转换为字节内容
137- if ( toBytes ) {
146+ // 如果需要转换为字节内容(尝试base64转bytes)
147+ if ( toBytes === 1 ) {
138148 try {
139149 if ( content . includes ( 'base64,' ) ) {
140150 content = unescape ( content . split ( "base64," ) [ 1 ] ) ;
@@ -144,6 +154,14 @@ export default (fastify, options, done) => {
144154 fastify . log . error ( `Local Proxy toBytes error: ${ e } ` ) ;
145155 }
146156 }
157+ // 流代理
158+ else if ( toBytes === 2 && content . startsWith ( 'http' ) ) {
159+ const new_headers = {
160+ ...( headers ? headers : { } ) ,
161+ ...( rangeHeader ? { Range : rangeHeader } : { } ) , // 添加 Range 请求头
162+ }
163+ return proxyStreamMedia ( content , new_headers , reply ) ; // 走 流式代理
164+ }
147165
148166 // 根据媒体类型来决定如何设置字符编码
149167 if ( typeof content === 'string' ) {
@@ -233,3 +251,65 @@ export default (fastify, options, done) => {
233251 } ) ;
234252 done ( ) ;
235253} ;
254+
255+ // 媒体文件 流式代理
256+ function proxyStreamMedia ( videoUrl , headers , reply ) {
257+ console . log ( `进入了流式代理: ${ videoUrl } | headers: ${ JSON . stringify ( headers ) } ` ) ;
258+
259+ const protocol = videoUrl . startsWith ( 'https' ) ? https : http ;
260+ const agent = videoUrl . startsWith ( 'https' ) ? httpsAgent : httpAgent ;
261+
262+ // 发起请求
263+ const proxyRequest = protocol . request ( videoUrl , { headers, agent} , ( videoResponse ) => {
264+ console . log ( 'videoResponse.statusCode:' , videoResponse . statusCode ) ;
265+ console . log ( 'videoResponse.headers:' , videoResponse . headers ) ;
266+
267+ if ( videoResponse . statusCode === 200 || videoResponse . statusCode === 206 ) {
268+ const resp_headers = {
269+ 'Content-Type' : videoResponse . headers [ 'content-type' ] || 'application/octet-stream' ,
270+ 'Content-Length' : videoResponse . headers [ 'content-length' ] ,
271+ ...( videoResponse . headers [ 'content-range' ] ? { 'Content-Range' : videoResponse . headers [ 'content-range' ] } : { } ) ,
272+ } ;
273+ console . log ( 'Response headers:' , resp_headers ) ;
274+ reply . headers ( resp_headers ) . status ( videoResponse . statusCode ) ;
275+
276+ // 将响应流直接管道传输给客户端
277+ videoResponse . pipe ( reply . raw ) ;
278+
279+ videoResponse . on ( 'data' , ( chunk ) => {
280+ console . log ( 'Data chunk received, size:' , chunk . length ) ;
281+ } ) ;
282+
283+ videoResponse . on ( 'end' , ( ) => {
284+ console . log ( 'Video data transmission complete.' ) ;
285+ } ) ;
286+
287+ videoResponse . on ( 'error' , ( err ) => {
288+ console . error ( 'Error during video response:' , err . message ) ;
289+ reply . code ( 500 ) . send ( { error : 'Error streaming video' , details : err . message } ) ;
290+ } ) ;
291+
292+ reply . raw . on ( 'finish' , ( ) => {
293+ console . log ( 'Data fully sent to client' ) ;
294+ } ) ;
295+
296+ // 监听关闭事件,销毁视频响应流
297+ reply . raw . on ( 'close' , ( ) => {
298+ console . log ( 'Response stream closed.' ) ;
299+ videoResponse . destroy ( ) ;
300+ } ) ;
301+ } else {
302+ console . error ( `Unexpected status code: ${ videoResponse . statusCode } ` ) ;
303+ reply . code ( videoResponse . statusCode ) . send ( { error : 'Failed to fetch video' } ) ;
304+ }
305+ } ) ;
306+
307+ // 监听错误事件
308+ proxyRequest . on ( 'error' , ( err ) => {
309+ console . error ( 'Proxy request error:' , err . message ) ;
310+ reply . code ( 500 ) . send ( { error : 'Error fetching video' , details : err . message } ) ;
311+ } ) ;
312+
313+ // 必须调用 .end() 才能发送请求
314+ proxyRequest . end ( ) ;
315+ }
0 commit comments