@@ -2,6 +2,15 @@ import path from 'path';
2
2
import { existsSync } from 'fs' ;
3
3
import { base64Decode } from '../libs_drpy/crypto-util.js' ;
4
4
import * 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
+
5
14
6
15
export default ( fastify , options , done ) => {
7
16
// 动态加载模块并根据 query 执行不同逻辑
@@ -117,6 +126,7 @@ export default (fastify, options, done) => {
117
126
}
118
127
const proxyPath = request . params [ '*' ] ; // 捕获整个路径
119
128
fastify . log . info ( `try proxy for ${ moduleName } -> ${ proxyPath } : ${ JSON . stringify ( query ) } ` ) ;
129
+ const rangeHeader = request . headers . range ; // 获取客户端的 Range 请求头
120
130
const protocol = request . protocol ;
121
131
const hostname = request . hostname ;
122
132
const proxyUrl = `${ protocol } ://${ hostname } ${ request . url } ` . split ( '?' ) [ 0 ] . replace ( proxyPath , '' ) + '?do=js' ;
@@ -133,8 +143,8 @@ export default (fastify, options, done) => {
133
143
let content = backRespList [ 2 ] || '' ;
134
144
const headers = backRespList . length > 3 ? backRespList [ 3 ] : null ;
135
145
const toBytes = backRespList . length > 4 ? backRespList [ 4 ] : null ;
136
- // 如果需要转换为字节内容
137
- if ( toBytes ) {
146
+ // 如果需要转换为字节内容(尝试base64转bytes)
147
+ if ( toBytes === 1 ) {
138
148
try {
139
149
if ( content . includes ( 'base64,' ) ) {
140
150
content = unescape ( content . split ( "base64," ) [ 1 ] ) ;
@@ -144,6 +154,14 @@ export default (fastify, options, done) => {
144
154
fastify . log . error ( `Local Proxy toBytes error: ${ e } ` ) ;
145
155
}
146
156
}
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
+ }
147
165
148
166
// 根据媒体类型来决定如何设置字符编码
149
167
if ( typeof content === 'string' ) {
@@ -233,3 +251,65 @@ export default (fastify, options, done) => {
233
251
} ) ;
234
252
done ( ) ;
235
253
} ;
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