アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

ブラウザ上で自作javascriptを走らせたい!

こんにちは!!こんにちは!! moriyamaです。

早速ですが、特定WEBサイトのアクセス時に自作scriptを走らせたい時ってありませんか?
私はあります。読者の皆さんもありますよね?

いつかは実現したいと常日頃から考えており、今回ついに実装できたので記しておきます。


拡張機能では駄目なのか?

bodyタグ内で走って問題ないなら、ぶっちゃけ駄目じゃないと思います。

ChromeやFireFoxなどのブラウザは拡張機能でjavascriptを組み込めたり出来ますが、
headタグに組み込めるものは見たことがありません。

例えば純粋なjavascriptで書かれたWEBページでDOM操作したい際に、
面倒臭がってjQueryでscriptを書いても、jQuery本体を先に読み込ませる必要がありますよね?
bodyタグで読み込んでも、実行順序で不都合が起きたこともあったため、
headタグにscriptを埋め込めないかと常々考えていました。


どう実現したのか?

ローカルにnginxでプロキシサーバを立てました!

完!!!で締めたいところですが、もう少し詳しく説明します。

nginx の proxy_passを使ってリクエストを流し、Luaのスクリプトを走らせてレスポンスを改変します。
そのためluaモジュールが組み込まれたnginx(OpenResty)を使いました。

フォルダ構成は、シンプルにこんな感じです↓

.
├── docker-compose.yml
└── nginx
    ├── default.conf
    └── src
        ├── body_filter.lua
        └── script.js

ファイルの中身

■ docker-compose.yml

version: '3'

services:
  nginx:
    image: openresty/openresty:centos
    ports:
      - 8880:80
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./nginx/src:/etc/nginx/src

■ nginx/default.conf

server {
  listen  80;

  location / {
    # プロキシ設定
    proxy_pass 'https://iko-yo.net/';
    proxy_set_header Accept-Encoding "";

    # luaコードのキャッシュ設定
    #lua_code_cache off;

    # コンテンツ長が変動するため、ヘッダ書き換え
    header_filter_by_lua_block {
      ngx.header.content_length = nil
    }

    # コンテンツ内容の改変
    body_filter_by_lua_file '/etc/nginx/src/body_filter.lua';
  }
}

■ nginx/src/body_filter.lua

-- jsファイルを読み込み
function load_script()
  local file = io.open("/etc/nginx/src/script.js", "r")
  local script = file:read("*a")
  file:close()

  return script
end

-- 置換する文字列生成
local replace = "<head><script>" .. load_script() .. "</script>"

-- body書き換え
ngx.arg[1] = ngx.re.sub(ngx.arg[1], "<head>", replace)

■ nginx/src/script.js

if (confirm('動いた?')) {
  console.log('動いたらしい');
}

苦労した箇所

実装時はLua上でレスポンス内容を出力しながら試していましたが、
文字化けが酷く、内容が全く読めませんでした。

そのため置換処理も走らず、吐き出されるHTMLは変化せず四苦八苦。
string.upper等の文字列処理を試しても、ブラウザ上ではHTML認識されず、更には文字化けテキストが表示され悩まされ。
UTF8のライブラリを使い、エンコードしてみても駄目だったのです。

色々調べているうちに、偶然、解決方法を見つけました。
HTTPヘッダのAccept-Encoding が原因でした。
レスポンス本文が圧縮されている場合、
中身を出力したところで、エンコード関係なく文字化けしている様に見えるわけですね!


実際に動かしてみる

こうなりました!
各ページで、javascriptのconfirmが動作している様子が伺えますね!

f:id:setsuna82001:20200717144324g:plain:w350


最後に

思った通りに実装するのはなかなか難しいですが、動いた時は感無量ですね!