background.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * @author didierfred@gmail.com
  6. * @version 0.4
  7. */
  8. //C:\Program Files\Google\Chrome\Application\chrome.exe --auto-open-devtools-for-tabs --load-extension=D:\mayun\LR04.02_RVCTerminalPlus_uos\addin\res\VTMModifyHeaders "file:///D:/redirect.html?url=http://oa.cmbchina.com"
  9. //"C:\Program Files\Google\Chrome\Application\chrome.exe" --auto-open-devtools-for-tabs --load-extension=D:\mayun\LR04.02_RVCTerminalPlus_uos\addin\res\VTMModifyHeaders "file:///D:/mayun/LR04.02_RVCTerminalPlus_uos/addin/res/ManagerDesktop/redirect.html?redirect_open=http://oa.cmbchina.com&redirect_type=ad"
  10. "use strict";
  11. // 创建自定义事件发射器
  12. const eventBus = new EventTarget();
  13. // 定义事件名
  14. const WS_CALLBACK_COMPLETE = 'wsCallbackComplete';
  15. let config;
  16. let started = 'on';
  17. let debug_mode = true;
  18. const isChrome = true;
  19. let config_read_type = 'websocket';//local,file,websocket,http
  20. const wsUrl = 'ws://127.0.0.1:9002?name=vtm_modify_header';
  21. const wsLoggerUrl = 'ws://127.0.0.1:9002?name=header_';
  22. let socket = null;
  23. let isExtensionReady = false;
  24. let isSuc = false;
  25. let dstWs_logger = wsLoggerUrl;
  26. let currentUrl = "";
  27. let urlParam = "";
  28. let data = "";
  29. function delay(ms) {
  30. return new Promise(resolve => setTimeout(resolve, ms));
  31. }
  32. class WebSocketLogger {
  33. constructor(options) {
  34. // 默认配置
  35. const defaults = {
  36. url: wsLoggerUrl,
  37. reconnectInterval: 5000,
  38. maxReconnectAttempts: 3,
  39. logging: true
  40. };
  41. this.config = { ...defaults, ...options };
  42. this.ws = null;
  43. this.reconnectAttempts = 0;
  44. this.isConnected = false;
  45. this.messageQueue = [];
  46. }
  47. // 初始化WebSocket连接
  48. connect() {
  49. this.ws = new WebSocket(this.config.url);
  50. this.ws.onopen = () => {
  51. this.isConnected = true;
  52. this.reconnectAttempts = 0;
  53. console.log('WebSocket connected');
  54. this._flushMessageQueue(); // 发送积压的消息
  55. };
  56. this.ws.onclose = () => {
  57. this.isConnected = false;
  58. console.log('WebSocket disconnected');
  59. this._attemptReconnect();
  60. };
  61. this.ws.onerror = (error) => {
  62. console.error('WebSocket error:', error);
  63. };
  64. }
  65. // 尝试重新连接
  66. _attemptReconnect() {
  67. if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
  68. this.reconnectAttempts++;
  69. console.log(`Reconnecting attempt ${this.reconnectAttempts}...`);
  70. setTimeout(() => this.connect(), this.config.reconnectInterval);
  71. } else {
  72. console.error('Max reconnection attempts reached');
  73. }
  74. }
  75. // 发送日志消息
  76. log(payload) {
  77. const defaultPayload = {
  78. messageType: 3080198,
  79. EntityName: 'header_log',
  80. ResultMsg: '',
  81. LogType: 'VTMSys',
  82. CostTime: 0,
  83. ResultCode: 'SUC0000',
  84. LogCode: '',
  85. API: 'header_log',
  86. SourceType: '',
  87. BussID: '',
  88. TipMsg: ''
  89. };
  90. const message = { ...defaultPayload, ...payload };
  91. if (this.isConnected) {
  92. this._sendMessage(message);
  93. } else {
  94. this.messageQueue.push(message); // 先存入队列
  95. if (this.messageQueue.length === 1) {
  96. this.connect(); // 首次断连时尝试重连
  97. }
  98. }
  99. }
  100. // 实际发送消息
  101. _sendMessage(message) {
  102. try {
  103. this.ws.send(JSON.stringify(message));
  104. if (this.config.logging) {
  105. console.log('Log sent:', message);
  106. }
  107. } catch (error) {
  108. console.error('Failed to send log:', error);
  109. this.messageQueue.push(message); // 失败后重新入队
  110. }
  111. }
  112. // 发送队列中的积压消息
  113. _flushMessageQueue() {
  114. while (this.messageQueue.length > 0 && this.isConnected) {
  115. const message = this.messageQueue.shift();
  116. this._sendMessage(message);
  117. }
  118. }
  119. // 关闭连接
  120. close() {
  121. if (this.ws) {
  122. this.ws.close();
  123. this.isConnected = false;
  124. }
  125. }
  126. }
  127. loadConfigurationFromLocalStorage();
  128. chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
  129. if (tabs.length > 0 && tabs[0].url) {
  130. console.log("Current URL2:", tabs[0].url);
  131. } else {
  132. console.error("No active tab found or URL is unavailable.");
  133. }
  134. });
  135. function connectWebSocket(callback_function) {
  136. let socket = null; // 初始化 socket 变量
  137. let timeoutId = setTimeout(() => {
  138. socket.close();
  139. callback_function(null); // 超时回调 null
  140. }, 3000); // 5秒 = 5000 毫秒
  141. let connectionOpened = false; // 标记连接是否已经打开
  142. try {
  143. socket = new WebSocket(wsUrl);
  144. } catch (error) {
  145. console.log('WebSocket connection creation error: ' + error);
  146. callback_function(null); // 连接创建失败,回调 null
  147. return;
  148. }
  149. socket.onmessage = function (event) {
  150. console.log('Received message:' + event.data);
  151. clearTimeout(timeoutId); // 清除定时器
  152. if (socket && socket.readyState === WebSocket.OPEN) { // 检查连接状态
  153. socket.close();
  154. }
  155. callback_function(event.data);
  156. return;
  157. };
  158. socket.onopen = function () {
  159. console.log('WebSocket connection established');
  160. connectionOpened = true; // 设置连接已打开标志
  161. // 发送配置请求
  162. let requestData = '{"messageType":131073}';
  163. console.log('Send message:' + requestData);
  164. socket.send(requestData);
  165. };
  166. socket.onerror = function (error) {
  167. console.log('WebSocket connection error:' + error);
  168. clearTimeout(timeoutId); // 清除定时器
  169. if (socket && socket.readyState === WebSocket.OPEN) { // 检查连接状态
  170. socket.close();
  171. }
  172. callback_function(null); // 错误回调 null
  173. };
  174. socket.onclose = function (event) {
  175. console.log('WebSocket connection closed:' + event.reason);
  176. clearTimeout(timeoutId); // 清除定时器
  177. connectionOpened = false; // 重置连接已打开标志
  178. if (event.reason !== 'Connection timed out') { // 如果不是超时关闭
  179. callback_function(null); // 连接意外关闭,回调 null
  180. }
  181. };
  182. }
  183. function extractUrlFromFileProtocol(fileUrl) {
  184. try {
  185. const urlObj = new URL(fileUrl);
  186. const params = new URLSearchParams(urlObj.search);
  187. const targetUrl = params.get('redirect_open');
  188. // 验证提取的 URL 是否合法
  189. if (targetUrl && (targetUrl.startsWith('http://') || targetUrl.startsWith('https://'))) {
  190. return targetUrl;
  191. }
  192. } catch (e) {
  193. console.error('Failed to parse file:// URL:', e);
  194. }
  195. return null;
  196. }
  197. function extractTypeFromFileProtocol(fileUrl) {
  198. try {
  199. const urlObj = new URL(fileUrl);
  200. const params = new URLSearchParams(urlObj.search);
  201. const targetUrl = params.get('redirect_type');
  202. // 验证提取的 URL 是否合法
  203. if (targetUrl) {
  204. return targetUrl;
  205. }
  206. else
  207. return "log";
  208. } catch (e) {
  209. console.error('Failed to parse file:// URL:', e);
  210. }
  211. return null;
  212. }
  213. function getCurrentTabUrlWithRetry(maxRetries = 40, retryDelay = 50) {
  214. chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
  215. if (tabs[0] && tabs[0].url && tabs[0].url.length > 0) {
  216. // 成功获取有效 URL
  217. currentUrl = tabs[0].url;
  218. console.log("当前 URL:", currentUrl);
  219. handleUrlLogic(currentUrl, tabs); // 处理你的业务逻辑
  220. } else {
  221. // URL 无效,触发重试
  222. if (maxRetries > 0) {
  223. console.warn(`URL 为空,剩余重试次数: ${maxRetries}`);
  224. setTimeout(() => {
  225. getCurrentTabUrlWithRetry(maxRetries - 1, retryDelay);
  226. }, retryDelay);
  227. } else {
  228. console.error("无法获取有效 URL,重试次数用尽");
  229. eventBus.dispatchEvent(new CustomEvent(WS_CALLBACK_COMPLETE, {
  230. detail: { success: false, data: "无法获取标签页 URL" }
  231. }));
  232. }
  233. }
  234. });
  235. }
  236. function handleUrlLogic(currentUrl, tabs) {
  237. const urlType = "log";
  238. dstWs_logger = dstWs_logger + urlType;
  239. if (currentUrl.startsWith('file:///')) {
  240. console.log("begin with file:///");
  241. urlParam = extractUrlFromFileProtocol(currentUrl);
  242. console.log("解析的url为:" + urlParam);
  243. if (urlParam) {
  244. // 使插件生效
  245. config = data; // modify config which make
  246. storeInBrowserStorage({ config: JSON.stringify(data) });
  247. started = 'on';
  248. addListener();
  249. isSuc = true;
  250. console.log("重载为:", urlParam);
  251. chrome.tabs.update(tabs[0].id, { url: urlParam });
  252. eventBus.dispatchEvent(new CustomEvent(WS_CALLBACK_COMPLETE, {
  253. detail: { success: true, data: "操作完成" }
  254. }));
  255. }
  256. } else {
  257. started = 'off';
  258. eventBus.dispatchEvent(new CustomEvent(WS_CALLBACK_COMPLETE, {
  259. detail: { success: false, data: "操作失败" }
  260. }));
  261. }
  262. }
  263. function loadConfigurationFromLocalStorage() {
  264. if (config_read_type == 'websocket') {
  265. connectWebSocket(function (dataStr) {
  266. if (dataStr == null)
  267. return;
  268. data = JSON.parse(dataStr);
  269. log("current new config:" + JSON.stringify(data));
  270. getCurrentTabUrlWithRetry();
  271. /*
  272. chrome.tabs.query({ active: true }, (tabs) => {
  273. chrome.tabs.update(tabs[0].id, { url: "https://oa.cmbchina.com" });
  274. });
  275. */
  276. });
  277. }
  278. eventBus.addEventListener(WS_CALLBACK_COMPLETE, async (e) => {
  279. const logger = new WebSocketLogger({
  280. url: dstWs_logger,
  281. logging: true // 开启调试日志
  282. });
  283. if (isSuc) {
  284. logger.log({
  285. ResultMsg: "重载为:" + urlParam
  286. });
  287. } else {
  288. logger.log({
  289. ResultMsg: "不运行插件, 当前url地址为:" + currentUrl
  290. });
  291. }
  292. logger.close();
  293. });
  294. }
  295. function loadFromBrowserStorage(item, callback_function) {
  296. chrome.storage.local.get(item, callback_function);
  297. }
  298. function storeInBrowserStorage(item, callback_function) {
  299. chrome.storage.local.set(item, callback_function);
  300. }
  301. function cookie_keyvalues_set(original_cookies, key, value) {
  302. let new_element = " " + key + "=" + value; // not used if value is undefined.
  303. let cookies_ar = original_cookies.split(";").filter(e => e.trim().length > 0);
  304. let selected_cookie_index = cookies_ar.findIndex(kv => kv.trim().startsWith(key + "="));
  305. if ((selected_cookie_index == -1) && (value != undefined)) cookies_ar.push(new_element);
  306. else {
  307. if (value === undefined)
  308. cookies_ar.splice(selected_cookie_index, 1);
  309. else
  310. cookies_ar.splice(selected_cookie_index, 1, new_element);
  311. }
  312. return cookies_ar.join(";");
  313. }
  314. function set_cookie_modify_cookie_value(original_set_cookie_header_content, key, new_value) {
  315. let trimmed = original_set_cookie_header_content.trimStart();
  316. let original_attributes = trimmed.indexOf(";") === -1 ? "" : trimmed.substring(trimmed.indexOf(";"))
  317. return key + "=" + new_value + original_attributes;
  318. }
  319. /*
  320. * Standard function to log messages
  321. *
  322. */
  323. function log(message) {
  324. console.log(new Date() + " SimpleModifyHeader : " + message);
  325. }
  326. /*
  327. * Rewrite the request header (add , modify or delete)
  328. *
  329. */
  330. function rewriteRequestHeader(e) {
  331. if (config.debug_mode)
  332. log("Start modify request headers for url " + e.url + "use_url_excepts:" + config.use_url_excepts)
  333. for (let to_modify of config.headers) {
  334. if (config.debug_mode)
  335. log("to_modify.url_contains:" + to_modify.url_contains + " e.url:" + e.url + ", result:" + !e.url.includes(to_modify.url_contains.trim()));
  336. const shouldApplyModification = (to_modify, config, e) => {
  337. const isStatusOn = to_modify.status === "on";
  338. const isApplyOnReq = to_modify.apply_on === "req";
  339. const isUrlExcepted = config.use_url_excepts
  340. ? to_modify.url_contains.length > 0 && e.url.includes(to_modify.url_contains.trim())
  341. : false;
  342. return isStatusOn && isApplyOnReq && !isUrlExcepted;
  343. };
  344. if (shouldApplyModification(to_modify, config, e)) {
  345. if (to_modify.action === "add") {
  346. let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
  347. e.requestHeaders.push(new_header);
  348. if (config.debug_mode) log("Add request header : name=" + to_modify.header_name +
  349. ",value=" + to_modify.header_value + " for url " + e.url + " url_contains " + to_modify.url_contains);
  350. }
  351. else if (to_modify.action === "modify") {
  352. log("modify request header");
  353. for (let header of e.requestHeaders) {
  354. if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
  355. if (config.debug_mode) log("Modify request header : name= " + to_modify.header_name +
  356. ",old value=" + header.value + ",new value=" + to_modify.header_value +
  357. " for url " + e.url);
  358. header.value = to_modify.header_value;
  359. }
  360. }
  361. }
  362. else if (to_modify.action === "delete") {
  363. log("Delete request header");
  364. let index = -1;
  365. for (let i = 0; i < e.requestHeaders.length; i++) {
  366. if (e.requestHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
  367. }
  368. if (index !== -1) {
  369. e.requestHeaders.splice(index, 1);
  370. if (config.debug_mode) log("Delete request header : name=" + to_modify.header_name.toLowerCase() +
  371. " for url " + e.url);
  372. }
  373. }
  374. else if (to_modify.action === "cookie_add_or_modify") {
  375. log("cookie_add_or_modify.req");
  376. let header_cookie = e.requestHeaders.find(header => header.name.toLowerCase() === "cookie");
  377. let new_cookie = cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
  378. if (header_cookie === undefined) {
  379. e.requestHeaders.push({ "name": "Cookie", "value": new_cookie });
  380. if (config.debug_mode) log("cookie_add_or_modify.req new_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  381. }
  382. else {
  383. header_cookie.value = new_cookie;
  384. if (config.debug_mode) log("cookie_add_or_modify.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  385. }
  386. }
  387. else if (to_modify.action === "cookie_delete") {
  388. log("cookie_delete.req");
  389. let header_cookie = e.requestHeaders.find(header => header.name.toLowerCase() === "cookie");
  390. let new_cookie = cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, undefined);
  391. if (header_cookie === undefined) {
  392. if (config.debug_mode) log("cookie_delete.req: no cookie header found. doing nothing for url " + e.url);
  393. }
  394. else {
  395. header_cookie.value = new_cookie;
  396. if (config.debug_mode) log("cookie_delete.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  397. }
  398. }
  399. }
  400. }
  401. if (config.debug_mode) log("End modify request headers for url " + e.url);
  402. return { requestHeaders: e.requestHeaders };
  403. }
  404. /*
  405. * Rewrite the response header (add , modify or delete)
  406. *
  407. */
  408. function rewriteResponseHeader(e) {
  409. //if (config.debug_mode) log("Start modify response headers for url " + e.url);
  410. for (let to_modify of config.headers) {
  411. if ((to_modify.status === "on") && (to_modify.apply_on === "res") && (!config.use_url_excepts || (config.use_url_excepts && e.url.includes(to_modify.url_contains.trim())))) {
  412. if (to_modify.action === "add") {
  413. let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
  414. e.responseHeaders.push(new_header);
  415. if (config.debug_mode) log("Add response header : name=" + to_modify.header_name
  416. + ",value=" + to_modify.header_value + " for url " + e.url);
  417. }
  418. else if (to_modify.action === "modify") {
  419. for (let header of e.responseHeaders) {
  420. if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
  421. if (config.debug_mode) log("Modify response header : name= " + to_modify.header_name + ",old value="
  422. + header.value + ",new value=" + to_modify.header_value + " for url " + e.url);
  423. header.value = to_modify.header_value;
  424. }
  425. }
  426. }
  427. else if (to_modify.action === "delete") {
  428. let index = -1;
  429. for (let i = 0; i < e.responseHeaders.length; i++) {
  430. if (e.responseHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
  431. }
  432. if (index !== -1) {
  433. e.responseHeaders.splice(index, 1);
  434. if (config.debug_mode) log("Delete response header : name=" + to_modify.header_name.toLowerCase()
  435. + " for url " + e.url);
  436. }
  437. }
  438. else if (to_modify.action === "cookie_add_or_modify") {
  439. let header_cookie = e.responseHeaders.find(header =>
  440. header.name.toLowerCase() === "set-cookie" &&
  441. header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase() + "=")
  442. );
  443. let new_header_value = set_cookie_modify_cookie_value(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
  444. if (header_cookie === undefined) {
  445. log("SimpleModifyHeaders.Warning: you're using cookie_add_or_modify in Response. While adding new cookie in response, this plugin only generates `Set-Cookie: cookie-name=cookie-value `, without ANY additional attributes. Add a `Set-Cookie` header if you need them. ");
  446. e.responseHeaders.push({ "name": "Set-Cookie", "value": new_header_value });
  447. if (config.debug_mode) log("cookie_add_or_modify.resp new_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
  448. }
  449. else {
  450. header_cookie.value = new_header_value;
  451. if (config.debug_mode) log("cookie_add_or_modify.resp modify_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
  452. }
  453. }
  454. else if (to_modify.action === "cookie_delete") {
  455. let index = e.responseHeaders.findIndex(header =>
  456. header.name.toLowerCase() === "set-cookie" &&
  457. header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase() + "=")
  458. );
  459. if (index === -1) {
  460. if (config.debug_mode) log("cookie_delete.resp: no matching set-cookie header. doing nothing for url " + e.url);
  461. }
  462. else {
  463. e.responseHeaders.splice(index, 1);
  464. if (config.debug_mode) log("cookie_delete.resp delete_header : name=" + to_modify.header_name + " for url " + e.url);
  465. }
  466. }
  467. }
  468. }
  469. //if (config.debug_mode) log("End modify response headers for url " + e.url);
  470. return { responseHeaders: e.responseHeaders };
  471. }
  472. /*
  473. * Listen for message form config.js
  474. * if message is reload : reload the configuration
  475. * if message is on : start the modify header
  476. * if message is off : stop the modify header
  477. *
  478. **/
  479. function notify(message) {
  480. if (message === "reload") {
  481. if (config.debug_mode) log("Reload configuration");
  482. loadFromBrowserStorage(['config'], function (result) {
  483. config = JSON.parse(result.config);
  484. if (started === "on") {
  485. removeListener();
  486. addListener();
  487. }
  488. });
  489. }
  490. else if (message === "off") {
  491. removeListener();
  492. started = "off";
  493. if (config.debug_mode) log("Stop modifying headers");
  494. }
  495. else if (message === "on") {
  496. addListener();
  497. started = "on";
  498. if (config.debug_mode) log("Start modifying headers");
  499. }
  500. }
  501. /*
  502. * Add rewriteRequestHeader as a listener to onBeforeSendHeaders, only for the target pages.
  503. * Add rewriteResponseHeader as a listener to onHeadersReceived, only for the target pages.
  504. * Make it "blocking" so we can modify the headers.
  505. */
  506. function addListener() {
  507. //return;
  508. let target = "<all_urls>";
  509. // need to had "extraHeaders" option for chrome https://developer.chrome.com/extensions/webRequest#life_cycle_footnote
  510. if (isChrome) {
  511. chrome.webRequest.onBeforeSendHeaders.addListener(rewriteRequestHeader,
  512. { urls: target.split(";") },
  513. ["blocking", "requestHeaders", "extraHeaders"]);
  514. chrome.webRequest.onHeadersReceived.addListener(rewriteResponseHeader,
  515. { urls: target.split(";") },
  516. ["blocking", "responseHeaders", "extraHeaders"]);
  517. }
  518. else {
  519. chrome.webRequest.onBeforeSendHeaders.addListener(rewriteRequestHeader,
  520. { urls: target.split(";") },
  521. ["blocking", "requestHeaders"]);
  522. chrome.webRequest.onHeadersReceived.addListener(rewriteResponseHeader,
  523. { urls: target.split(";") },
  524. ["blocking", "responseHeaders"]);
  525. }
  526. }
  527. /*
  528. * Remove the two listener
  529. *
  530. */
  531. function removeListener() {
  532. chrome.webRequest.onBeforeSendHeaders.removeListener(rewriteRequestHeader);
  533. chrome.webRequest.onHeadersReceived.removeListener(rewriteResponseHeader);
  534. }