background.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. "use strict";
  9. let config;
  10. let started = 'on';
  11. let debug_mode = true;
  12. const isChrome = false;
  13. let config_read_type = 'websocket';//local,file,websocket,http
  14. const wsUrl = 'ws://127.0.0.1:9002?name=vtm_modify_header';
  15. let socket = null;
  16. loadConfigurationFromLocalStorage();
  17. addListener();
  18. // listen for change in configuration or start/stop
  19. chrome.runtime.onMessage.addListener(notify);
  20. function connectWebSocket(callback_function) {
  21. let socket = null; // 初始化 socket 变量
  22. let timeoutId = setTimeout(() => {
  23. socket.close();
  24. callback_function(null); // 超时回调 null
  25. }, 3000); // 5秒 = 5000 毫秒
  26. let connectionOpened = false; // 标记连接是否已经打开
  27. try {
  28. socket = new WebSocket(wsUrl);
  29. } catch (error) {
  30. log('WebSocket connection creation error: ' + error);
  31. callback_function(null); // 连接创建失败,回调 null
  32. return;
  33. }
  34. socket.onmessage = function (event) {
  35. log('Received message:' + event.data);
  36. clearTimeout(timeoutId); // 清除定时器
  37. if (socket && socket.readyState === WebSocket.OPEN) { // 检查连接状态
  38. socket.close();
  39. }
  40. callback_function(event.data);
  41. return;
  42. };
  43. socket.onopen = function () {
  44. log('WebSocket connection established');
  45. connectionOpened = true; // 设置连接已打开标志
  46. // 发送配置请求
  47. let requestData = '{"messageType":131073}';
  48. log('Send message:' + requestData);
  49. socket.send(requestData);
  50. };
  51. socket.onerror = function (error) {
  52. log('WebSocket connection error:' + error);
  53. clearTimeout(timeoutId); // 清除定时器
  54. if (socket && socket.readyState === WebSocket.OPEN) { // 检查连接状态
  55. socket.close();
  56. }
  57. callback_function(null); // 错误回调 null
  58. };
  59. socket.onclose = function (event) {
  60. log('WebSocket connection closed:' + event.reason);
  61. clearTimeout(timeoutId); // 清除定时器
  62. connectionOpened = false; // 重置连接已打开标志
  63. if (event.reason !== 'Connection timed out') { // 如果不是超时关闭
  64. callback_function(null); // 连接意外关闭,回调 null
  65. }
  66. };
  67. }
  68. function loadConfigurationFromLocalStorage() {
  69. if (config_read_type == 'local') {
  70. let headers = [];
  71. headers.push({ url_contains: "", action: "add", header_name: "terminalno", header_value: "7555980178", comment: "test", apply_on: "req", status: "on" });
  72. config = { format_version: "1.1", target_page: "", headers: headers, debug_mode: true, use_url_excepts: false };
  73. log("current new config" + JSON.stringify(config));
  74. storeInBrowserStorage({ config: JSON.stringify(config) });
  75. started = 'on';
  76. }
  77. else if (config_read_type == 'file') {
  78. log('loadConfigurationFromLocalStorage');
  79. const filePath = 'file:///D:/Run/runinfo/runcfg/config.json'; // 指定本地文件路径
  80. fetch(filePath)
  81. .then(response => response.json())
  82. .then(data => {
  83. console.log('Current new config:', JSON.stringify(data));
  84. config = data;
  85. storeInBrowserStorage({ config: JSON.stringify(data) }); // 如果不需要存储到浏览器存储中,可以注释掉这行
  86. started = 'on';
  87. })
  88. .catch(error => {
  89. console.error('Error reading file:', error);
  90. });
  91. }
  92. else if (config_read_type == 'websocket') {
  93. connectWebSocket(function (dataStr) {
  94. if (dataStr == null)
  95. return;
  96. let data = JSON.parse(dataStr);
  97. log("current new config:" + JSON.stringify(data));
  98. config = data
  99. storeInBrowserStorage({ config: JSON.stringify(data) });
  100. started = 'on';
  101. });
  102. }
  103. }
  104. function loadFromBrowserStorage(item, callback_function) {
  105. chrome.storage.local.get(item, callback_function);
  106. }
  107. function storeInBrowserStorage(item, callback_function) {
  108. chrome.storage.local.set(item, callback_function);
  109. }
  110. function cookie_keyvalues_set(original_cookies, key, value) {
  111. let new_element = " " + key + "=" + value; // not used if value is undefined.
  112. let cookies_ar = original_cookies.split(";").filter(e => e.trim().length > 0);
  113. let selected_cookie_index = cookies_ar.findIndex(kv => kv.trim().startsWith(key + "="));
  114. if ((selected_cookie_index == -1) && (value != undefined)) cookies_ar.push(new_element);
  115. else {
  116. if (value === undefined)
  117. cookies_ar.splice(selected_cookie_index, 1);
  118. else
  119. cookies_ar.splice(selected_cookie_index, 1, new_element);
  120. }
  121. return cookies_ar.join(";");
  122. }
  123. function set_cookie_modify_cookie_value(original_set_cookie_header_content, key, new_value) {
  124. let trimmed = original_set_cookie_header_content.trimStart();
  125. let original_attributes = trimmed.indexOf(";") === -1 ? "" : trimmed.substring(trimmed.indexOf(";"))
  126. return key + "=" + new_value + original_attributes;
  127. }
  128. /*
  129. * Standard function to log messages
  130. *
  131. */
  132. function log(message) {
  133. console.log(new Date() + " SimpleModifyHeader : " + message);
  134. }
  135. /*
  136. * Rewrite the request header (add , modify or delete)
  137. *
  138. */
  139. function rewriteRequestHeader(e) {
  140. if (config.debug_mode)
  141. log("Start modify request headers for url " + e.url + "use_url_excepts:" + config.use_url_excepts)
  142. for (let to_modify of config.headers) {
  143. if (config.debug_mode)
  144. log("to_modify.url_contains:" + to_modify.url_contains + " e.url:" + e.url + ", result:" + !e.url.includes(to_modify.url_contains.trim()));
  145. const shouldApplyModification = (to_modify, config, e) => {
  146. const isStatusOn = to_modify.status === "on";
  147. const isApplyOnReq = to_modify.apply_on === "req";
  148. const isUrlExcepted = config.use_url_excepts
  149. ? to_modify.url_contains.length > 0 && e.url.includes(to_modify.url_contains.trim())
  150. : false;
  151. return isStatusOn && isApplyOnReq && !isUrlExcepted;
  152. };
  153. if (shouldApplyModification(to_modify, config, e)) {
  154. if (to_modify.action === "add") {
  155. let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
  156. e.requestHeaders.push(new_header);
  157. if (config.debug_mode) log("Add request header : name=" + to_modify.header_name +
  158. ",value=" + to_modify.header_value + " for url " + e.url + " url_contains " + to_modify.url_contains);
  159. }
  160. else if (to_modify.action === "modify") {
  161. for (let header of e.requestHeaders) {
  162. if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
  163. if (config.debug_mode) log("Modify request header : name= " + to_modify.header_name +
  164. ",old value=" + header.value + ",new value=" + to_modify.header_value +
  165. " for url " + e.url);
  166. header.value = to_modify.header_value;
  167. }
  168. }
  169. }
  170. else if (to_modify.action === "delete") {
  171. let index = -1;
  172. for (let i = 0; i < e.requestHeaders.length; i++) {
  173. if (e.requestHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
  174. }
  175. if (index !== -1) {
  176. e.requestHeaders.splice(index, 1);
  177. if (config.debug_mode) log("Delete request header : name=" + to_modify.header_name.toLowerCase() +
  178. " for url " + e.url);
  179. }
  180. }
  181. else if (to_modify.action === "cookie_add_or_modify") {
  182. let header_cookie = e.requestHeaders.find(header => header.name.toLowerCase() === "cookie");
  183. let new_cookie = cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
  184. if (header_cookie === undefined) {
  185. e.requestHeaders.push({ "name": "Cookie", "value": new_cookie });
  186. if (config.debug_mode) log("cookie_add_or_modify.req new_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  187. }
  188. else {
  189. header_cookie.value = new_cookie;
  190. if (config.debug_mode) log("cookie_add_or_modify.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  191. }
  192. }
  193. else if (to_modify.action === "cookie_delete") {
  194. let header_cookie = e.requestHeaders.find(header => header.name.toLowerCase() === "cookie");
  195. let new_cookie = cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, undefined);
  196. if (header_cookie === undefined) {
  197. if (config.debug_mode) log("cookie_delete.req: no cookie header found. doing nothing for url " + e.url);
  198. }
  199. else {
  200. header_cookie.value = new_cookie;
  201. if (config.debug_mode) log("cookie_delete.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
  202. }
  203. }
  204. }
  205. }
  206. if (config.debug_mode) log("End modify request headers for url " + e.url);
  207. return { requestHeaders: e.requestHeaders };
  208. }
  209. /*
  210. * Rewrite the response header (add , modify or delete)
  211. *
  212. */
  213. function rewriteResponseHeader(e) {
  214. //if (config.debug_mode) log("Start modify response headers for url " + e.url);
  215. for (let to_modify of config.headers) {
  216. 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())))) {
  217. if (to_modify.action === "add") {
  218. let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
  219. e.responseHeaders.push(new_header);
  220. if (config.debug_mode) log("Add response header : name=" + to_modify.header_name
  221. + ",value=" + to_modify.header_value + " for url " + e.url);
  222. }
  223. else if (to_modify.action === "modify") {
  224. for (let header of e.responseHeaders) {
  225. if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
  226. if (config.debug_mode) log("Modify response header : name= " + to_modify.header_name + ",old value="
  227. + header.value + ",new value=" + to_modify.header_value + " for url " + e.url);
  228. header.value = to_modify.header_value;
  229. }
  230. }
  231. }
  232. else if (to_modify.action === "delete") {
  233. let index = -1;
  234. for (let i = 0; i < e.responseHeaders.length; i++) {
  235. if (e.responseHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
  236. }
  237. if (index !== -1) {
  238. e.responseHeaders.splice(index, 1);
  239. if (config.debug_mode) log("Delete response header : name=" + to_modify.header_name.toLowerCase()
  240. + " for url " + e.url);
  241. }
  242. }
  243. else if (to_modify.action === "cookie_add_or_modify") {
  244. let header_cookie = e.responseHeaders.find(header =>
  245. header.name.toLowerCase() === "set-cookie" &&
  246. header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase() + "=")
  247. );
  248. let new_header_value = set_cookie_modify_cookie_value(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
  249. if (header_cookie === undefined) {
  250. 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. ");
  251. e.responseHeaders.push({ "name": "Set-Cookie", "value": new_header_value });
  252. if (config.debug_mode) log("cookie_add_or_modify.resp new_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
  253. }
  254. else {
  255. header_cookie.value = new_header_value;
  256. if (config.debug_mode) log("cookie_add_or_modify.resp modify_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
  257. }
  258. }
  259. else if (to_modify.action === "cookie_delete") {
  260. let index = e.responseHeaders.findIndex(header =>
  261. header.name.toLowerCase() === "set-cookie" &&
  262. header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase() + "=")
  263. );
  264. if (index === -1) {
  265. if (config.debug_mode) log("cookie_delete.resp: no matching set-cookie header. doing nothing for url " + e.url);
  266. }
  267. else {
  268. e.responseHeaders.splice(index, 1);
  269. if (config.debug_mode) log("cookie_delete.resp delete_header : name=" + to_modify.header_name + " for url " + e.url);
  270. }
  271. }
  272. }
  273. }
  274. //if (config.debug_mode) log("End modify response headers for url " + e.url);
  275. return { responseHeaders: e.responseHeaders };
  276. }
  277. /*
  278. * Listen for message form config.js
  279. * if message is reload : reload the configuration
  280. * if message is on : start the modify header
  281. * if message is off : stop the modify header
  282. *
  283. **/
  284. function notify(message) {
  285. if (message === "reload") {
  286. if (config.debug_mode) log("Reload configuration");
  287. loadFromBrowserStorage(['config'], function (result) {
  288. config = JSON.parse(result.config);
  289. if (started === "on") {
  290. removeListener();
  291. addListener();
  292. }
  293. });
  294. }
  295. else if (message === "off") {
  296. removeListener();
  297. started = "off";
  298. if (config.debug_mode) log("Stop modifying headers");
  299. }
  300. else if (message === "on") {
  301. addListener();
  302. started = "on";
  303. if (config.debug_mode) log("Start modifying headers");
  304. }
  305. }
  306. /*
  307. * Add rewriteRequestHeader as a listener to onBeforeSendHeaders, only for the target pages.
  308. * Add rewriteResponseHeader as a listener to onHeadersReceived, only for the target pages.
  309. * Make it "blocking" so we can modify the headers.
  310. */
  311. function addListener() {
  312. //return;
  313. let target = "<all_urls>";
  314. // need to had "extraHeaders" option for chrome https://developer.chrome.com/extensions/webRequest#life_cycle_footnote
  315. if (isChrome) {
  316. chrome.webRequest.onBeforeSendHeaders.addListener(rewriteRequestHeader,
  317. { urls: target.split(";") },
  318. ["blocking", "requestHeaders", "extraHeaders"]);
  319. chrome.webRequest.onHeadersReceived.addListener(rewriteResponseHeader,
  320. { urls: target.split(";") },
  321. ["blocking", "responseHeaders", "extraHeaders"]);
  322. }
  323. else {
  324. chrome.webRequest.onBeforeSendHeaders.addListener(rewriteRequestHeader,
  325. { urls: target.split(";") },
  326. ["blocking", "requestHeaders"]);
  327. chrome.webRequest.onHeadersReceived.addListener(rewriteResponseHeader,
  328. { urls: target.split(";") },
  329. ["blocking", "responseHeaders"]);
  330. }
  331. }
  332. /*
  333. * Remove the two listener
  334. *
  335. */
  336. function removeListener() {
  337. chrome.webRequest.onBeforeSendHeaders.removeListener(rewriteRequestHeader);
  338. chrome.webRequest.onHeadersReceived.removeListener(rewriteResponseHeader);
  339. }