ちゅんラヂの壁抜け

 
ちゅんラヂ
Booskanium's
ちゅんラヂ Tips.

壁抜けで確認すること

Web 3rdパーティアプリに対して排他的な壁を作っているラジオ局が聞けるかな?

ブラウザ拡張のsimple-modify-headersを入れる。

当該ブラウザ拡張を入れて確認したブラウザ
・MS Edge(Android環境)
・Firefox(Android環境)
・Google Chrome(Windows環境)
・Firefox(Windows環境)

simple-modify-headers拡張の所在
Chromium系Mozilla系

※simple-modify-headersとちゅんラヂは無関係です。
 simple-modify-headers作者にちゅんラヂに関する問い合わせはお控えください。

simple-modify-headersの設定手順

下記のコンフィグファイルをsimple-modify-headersのconfigureにimportする。
SimpleModifyHeader.conf
当該configureによる動作説明は推測してください。

※simple-modify-headersの操作方法
 ちょっと猛者向けで専門知識を要するかもしれません。

simple-modify-headersをstartする。

startしたままにすると、他サイトのブラウジングに影響があるかもしれません。
ちゅんラヂで排他的なラジオ局が聞けるかを試すとき以外は、simple-modify-headersはstopしておくことをお勧めいたします。

ちゅんラヂを下記のURLで開く。

http://7design.jp/tr/?aid&list=all

httpスキーマ(平文)で開くのは混在コンテンツ回避
配信がまだ平文の局があるからです。
なお強制的にhttpsに切り替えられる一部ブラウザの場合は混在コンテンツになり聞けない放送局があります。

ソースコード公開

ちゅんラヂ専用ブラウザ「ちゅんブラ」は開発環境であるElectron制限を回避しています。
その部分はsimple-modify-headersをフォークして作成したJavaScriptのモジュールです。
フォーク元simple-modify-headers のライセンスはMPL 2.0( Mozilla Public License 2.0)です。
MPL 2.0ライセンスはソースコードを改変した場合には、そのファイル単位での公開義務があります。
そのライセンス要件にしたがい、改変したソースコードを公開します。

/*--------------------------------------------------------------------------------
 Electronのウェブ リクエストの割り込みに仕込む 
 フォーク元:https://github.com/didierfred/SimpleModifyHeaders/
 フォークで変更した事項
 ・Electronのウェブリクエスト割り込み用に書き換え
 ・HTTP Headerの値に誤動作を誘発するダブルコーティション等を含む場合にエスケープ
-------------------------------------------------------------------------------- */
export const track = {

	//監視設定
	config: {
		~
		:※書式は SimpleModifyHeaders のままです。
		:※ちゅんブラ用の設定はTipsの壁抜けで公開している設定のとおりです。
		~
	},

	/* リクエストヘッダの置換 */
	rewiteRequestHeader: function(e) {
		//if (args.debug) console.log("/* Begin of rewiteRequestHeader  url  */ \n" + e.url);

		//requestHeaders オブジェクトを{"name":"~,"value":"~"}な配列に変換
		//if (args.debug) console.log('/* Begin of rewiteRequestHeader 1 */ \n'+ JSON.stringify(e.requestHeaders));
		let arrHeaders = Object.keys(e.requestHeaders).map(function(key) {
			//return JSON.parse('{"name":"' + key +'", "value": "' + e.requestHeaders[key] + '"}');


			try {
				return JSON.parse('{"name":"' + key +'", "value": "' + e.requestHeaders[key] + '"}');
			} catch (error) {
				//ダブルコーテーションを含む場合エスケープが必要
				return  JSON.parse('{"name":"' + key +'", "value": "' + escape(e.requestHeaders[key]) + '"}');;
			}




		});
		//if (args.debug) console.log('/* Begin of rewiteRequestHeader 2 */ \n'+ JSON.stringify(arrHeaders));

		//headerを書き換え
		for (let to_modify of this.config.headers) {
			if ((to_modify.status === "on") && (to_modify.apply_on === "req") && (to_modify.url_filter === '' || (to_modify.url_filter && e.url.includes(to_modify.url_filter.trim())))) {
				if (to_modify.action === "add") {
					let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
					arrHeaders.push(new_header);
					//if (args.debug) console.log("Add request header : name=" + to_modify.header_name +
					//	",value=" + to_modify.header_value + " for url " + e.url);
				} else if (to_modify.action === "modify") {
					for (let header of arrHeaders) {
						if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
							//if (args.debug) console.log("Modify request header :  name= " + to_modify.header_name +
							//	",old value=" + header.value + ",new value=" + to_modify.header_value +
							//	" for url " + e.url);
							header.value = to_modify.header_value;
						}
					}
				} else if (to_modify.action === "delete") {
					let index = -1;
					for (let i = 0; i < arrHeaders.length; i++) {
						if (arrHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
					}
					if (index !== -1) {
						arrHeaders.splice(index, 1);
						//if (args.debug) console.log("Delete request header :  name=" + to_modify.header_name.toLowerCase() +
						//" for url " + e.url);
					}
				} else if (to_modify.action === "cookie_add_or_modify") {
					let header_cookie = arrHeaders.find(header => header.name.toLowerCase() === "cookie");
					let new_cookie = this.cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
					if (header_cookie === undefined) {
						arrHeaders.push({"name": "Cookie", "value": new_cookie});
						//if (args.debug) console.log("cookie_add_or_modify.req new_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
					} else {
						header_cookie.value = new_cookie;
						//if (args.debug) console.log("cookie_add_or_modify.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
					}
				} else if (to_modify.action === "cookie_delete") {
					let header_cookie = e.requestHeaders.find(header => header.name.toLowerCase() === "cookie");
					let new_cookie = this.cookie_keyvalues_set(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, undefined);
					if (header_cookie === undefined) {
						//if (args.debug) console.log("cookie_delete.req: no cookie header found. doing nothing for url " + e.url);
					} else {
						header_cookie.value = new_cookie;
						//if (args.debug) console.log("cookie_delete.req modify_header : name=Cookie,value=" + new_cookie + " for url " + e.url);
					}
				}
			}
		}
		//if (args.debug) console.log('/* End of rewiteRequestHeader 1 */ \n'+ JSON.stringify(arrHeaders));
		let  requestHeaders = {};
		for (let header of arrHeaders) {
			requestHeaders[header.name] = header.value;
		}
		//if (args.debug) console.log('/* End of rewiteRequestHeader 2 */ \n'+ JSON.stringify(requestHeaders));
		return requestHeaders;
	},

	/* レスポンス ヘッダを置換 */
	rewiteResponseHeader: function(e) {
		//if (args.debug) console.log("/* Begin of rewiteResponseHeader url */ \n" + e.url);

		//if (args.debug) console.log("/* Begin of rewiteResponseHeader 1 */ \n " + JSON.stringify(e.responseHeaders));
		let arrHeaders = Object.keys(e.responseHeaders).map(function(key) {
			try {
				return JSON.parse('{"name":"' + key +'", "value": "' + e.responseHeaders[key] + '"}');
			} catch (error) {
				//ETagのvalueの様にダブルコーテーションを含む場合エスケープが必要
				//if (args.debug) console.log("/* responseHeaders error */ key=" + key + " value=" + e.responseHeaders[key]);
				return  JSON.parse('{"name":"' + key +'", "value": "' + escape(e.responseHeaders[key]) + '"}');;
			}
		});
		//if (args.debug) console.log("/* Begin of rewiteResponseHeader 2 */ \n " + JSON.stringify(arrHeaders));
		for (let to_modify of this.config.headers) {
			if ((to_modify.status === "on") && (to_modify.apply_on === "res") && (to_modify.url_filter === '' || (to_modify.url_filter && e.url.includes(to_modify.url_filter.trim())))) {
				 if (to_modify.action === "add") {
					let new_header = { "name": to_modify.header_name, "value": to_modify.header_value };
					arrHeaders.push(new_header);
					//if (args.debug) console.log("Add response header : name=" + to_modify.header_name
					//+ ",value=" + to_modify.header_value + " for url " + e.url);
				} else if (to_modify.action === "modify") {
					//if (args.debug) condole.log('responseHeaders : ' + arrHeaders);
					for (let header of e.responseHeaders) {
						if (header.name.toLowerCase() === to_modify.header_name.toLowerCase()) {
							if (args.debug) console.log("Modify response header :  name= " + to_modify.header_name + ",old value="
							+ header.value + ",new value=" + to_modify.header_value + " for url " + e.url);
							header.value = to_modify.header_value;
						}
					}
				} else if (to_modify.action === "delete") {
					let index = -1;
					for (let i = 0; i < arrHeaders.length; i++) {
						if (arrHeaders[i].name.toLowerCase() === to_modify.header_name.toLowerCase()) index = i;
					}
					if (index !== -1) {
						arrHeaders.splice(index, 1);
						//if (args.debug) console.log("Delete response header :  name=" + to_modify.header_name.toLowerCase()
						//+ " for url " + e.url);
					}
				} else if (to_modify.action === "cookie_add_or_modify") {
					let header_cookie = arrHeaders.find(header => 
						header.name.toLowerCase() === "set-cookie" && 
						header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase()+"=")
					);
					let new_header_value = this.set_cookie_modify_cookie_value(header_cookie === undefined ? "" : header_cookie.value, to_modify.header_name, to_modify.header_value);
					if (header_cookie === undefined) {
						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. ");
						arrHeaders.push({"name": "Set-Cookie", "value": new_header_value});
						//if (args.debug) console.log("cookie_add_or_modify.resp new_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
					} else {
						header_cookie.value = new_header_value;
						//if (args.debug) console.log("cookie_add_or_modify.resp modify_header : name=Cookie,value=" + new_header_value + " for url " + e.url);
					}
				} else if (to_modify.action === "cookie_delete") {
					let index = arrHeaders.findIndex(header => 
						header.name.toLowerCase() === "set-cookie" && 
						header.value.toLowerCase().trim().startsWith(to_modify.header_name.toLowerCase()+"=")
					);
					if (index === -1) {
						//if (args.debug) console.log("cookie_delete.resp: no matching set-cookie header. doing nothing for url " + e.url);
					} else {
						arrHeaders.splice(index, 1);
						//if (args.debug) console.log("cookie_delete.resp delete_header : name=" + to_modify.header_name + " for url " + e.url);
					}
				}
			}
		}
		//if (args.debug) console.log('/* End of rewiteResponseHeader 1 */ \n'+ JSON.stringify(arrHeaders));
		let  responseHeaders = {};
		for (let header of arrHeaders) {
			responseHeaders[header.name] = [ header.value ];
		}
		//if (args.debug) console.log('/* End of rewiteResponseHeader 2 */ \n'+ JSON.stringify(responseHeaders));

		return responseHeaders;
	},

	/*  */
	cookie_keyvalues_set: function(original_cookies, key, value) {
		let new_element = key + "=" + value; // not used if value is undefined. 
		let cookies_ar = original_cookies.split(";").filter(e => e.trim().length > 0);
		let selected_cookie_index = cookies_ar.findIndex(kv => kv.trim().startsWith(key+"="));
		if (selected_cookie_index == -1) {
			cookies_ar.push(new_element);
		} else {
			if (value === undefined) {
				cookies_ar.splice(selected_cookie_index, 1);
			} else {
				cookies_ar.splice(selected_cookie_index, 1, new_element);
			}
	  	}
		return cookies_ar.join(";");
	},
  
	/*  */
	set_cookie_modify_cookie_value: function(original_set_cookie_header_content, key, new_value) {
		let trimmed = original_set_cookie_header_content.trimStart();
		let original_attributes = trimmed.indexOf(";") === -1 ? "" : trimmed.substring(trimmed.indexOf(";"))
		return key + "=" + new_value + original_attributes;
	}

}
					

当該Electron用のモジュール部分は、フォーク元のMPL 2.0ライセンスを引き継ぐものとします。
© 2026 booskanium