carloでHTTP通信してみる
carloとは
Carloは、ノードアプリケーションにGoogle Chromeのレンダリング機能を提供し、Puppeteerプロジェクトを使用してローカルにインストールされたブラウザインスタンスと通信し、ノードとブラウザ間の通信のためのリモート呼び出しインフラストラクチャを実装します。
ポイントは「ローカルにインストールされたブラウザインスタンスと通信」の部分。 ローカルのchromeを使うから必然的にバイナリサイズが小さくなる。
疑問 クロスドメインなサイトにajaxでHTTP通信できるの?
結論としては「ajaxではできない」
UI(html)はchrome上で動いてるだけだから。
↑すいません、これ嘘でした。 やり方は追記で。
ただnode.jsとの間で通信できるから
UI => node => HTTP
のような構図でnodeを間に挟めばいけそう。
1. hello world
とりあえず表示するまで
setup
npm init
npm install carlo --save
これでpackage.jsonが作られる。
htmlを作る
www/index.html
<body>Hello</body>
index.jsでhtmlを読み込む
const carlo = require('carlo'); carlo.launch().then(async app => { app.serveFolder(__dirname + '/www'); app.on('exit', () => process.exit()); await app.load('index.html'); });
起動
node .
これでウィンドウにHelloが表示される
2. UIからnodeを呼ぶ
carloにrpcというクラスがあるのでそれを使います。
index.jsに呼ばれる側を作る
const carlo = require('carlo'); const { rpc } = require('carlo/rpc');// 追加 carlo.launch().then(async app => { app.serveFolder(__dirname + '/www'); app.on('exit', () => process.exit()); await app.load('index.html', rpc.handle(new Backend) /* 追加 */); }); // htmlから扱うクラス追加 class Backend { hello(name) { console.log(`Hello ${name}`); return 'Backend is happy'; } }
htmlからnodeを呼ぶ
www/index.html
<script> // 追加 async function run() { console.log("run"); const [backend] = await carlo.loadParams(); console.log(await backend.hello('from frontend')); } </script> <body onload="run()">Hello</body>
carlo.loadParams()
でnode側のインスタンスを取り出してhello()
を実行します。
するとnode側の実装でconsole.log()
が実行され、ターミナル上にfrom frontend
と表示されます。
実行
ターミナル上にHello from frontend
が表示されました。
UI上のconsoleにはBackend is happy
が表示されました。
3. node-fetchで通信する
chromeで使えるfetch関数はnodeにはないので、それをインストールしてhtmlから利用します。
node-fetchのインストール
npm install node-fetch --save
nodeに通信部分を実装する
index.js
const carlo = require('carlo'); const { rpc } = require('carlo/rpc'); const nodeFetch = require('node-fetch');// 追加 carlo.launch().then(async app => { console.log(rpc); app.serveFolder(__dirname + '/www'); app.on('exit', () => process.exit()); await app.load('index.html', rpc.handle(new Backend)); }); class Backend { hello(name) { console.log(`Hello ${name}`); return 'Backend is happy'; } // 追加: 通信して結果を返す async fetch(url) { const res = await nodeFetch(url); return res.text() } }
UIから呼び出す
<script> async function run() { console.log("run"); const [backend] = await carlo.loadParams(); console.log(await backend.hello('from frontend')); // 追加 const text = await backend.fetch('https://www.google.com/') console.log(text); } </script> <body onload="run()">Hello</body>
実行
これでUIのconsole上にgoogleのソースが表示されました
まとめ
carloはchromeのラッパーみたいなものなのでUI上からクロスドメインなHTTP通信はできないが、nodeを介せばできることがわかった。
気軽に使えそうな予感。
追記
「UI側でクロスドメインな通信は無理」と書きましたが、できました。
方法としては起動オプションでセキュリティをoffにすればOK。
こんな感じです。
index.js
const carlo = require('carlo'); carlo.launch({ args:['--disable-web-security']// 追加 }).then(async app => { app.serveFolder(__dirname + '/www'); app.on('exit', () => process.exit()); await app.load('index.html'); });
ポイント launchのオプションに--disable-web-security
を渡すところ。これでクロスドメインの制限が外れる
inde.html
<script> async function run() { console.log("run"); const res = await fetch('https://www.yahoo.co.jp'); const text = await res.text(); console.log(text); } </script> <body onload="run()">Hello</body>
fetchで普通に取得可能になる
セキュリティには気をつけろ
これで目標は達成したけど、webセキュリティを外すと何が起こるのかわからないので気をつけようw
node側でApp.serveHandler(handler)
を使えばHttpRequestにフィルターがかけれるのでそれでアクセス制限かけるのもアリだと思う