前回の雪かき事故を知らせるアプリの通知先はiPhoneアプリでしたが、AndroidやパソコンのChrome, Firefox, Edgeでは、W3Cでオープンに規格化が進む WebPushこと「Push API」が使用可能とのことで、実験してみました。 コマンドラインから、webから、結構手軽に通知が送れるのでなかなか楽しく、便利です!うまく使えば、気になる情報のチェックや、リマインドに大きく力を発揮してくれそうです。

本ブログにもそのうち追加しようと思いますが、ひとまず、実験できるプログラム一式ができたので、公開します。

WebPushは、各ブラウザベンダーが運用している通知サーバーにデータを送って、そこから各ブラウザへと通信される仕様のようです。 Node.js 用のライブラリを使って、通知用の設定と、コマンドラインで送信するツール、Deno で作ったサンプル通知管理サービスと、クライアント動作するプログラム、すべてJavaScriptです。 便利ですね、JavaScript。

<script type="module"> import { WebPush } from "./WebPush.js"; window.onload = () => { btnSubscribe.onclick = () => { WebPush.subscribe(); }; btnUnsubscribe.onclick = () => { WebPush.unsubscribe(); }; btnTest.onclick = async evt => { const data = { title: "webpush test", body: "本文本文...........", url: "https://fukuno.jig.jp/3093", // 拡張 開くアドレス timeout: 5000, // 拡張 通知を消すまでの長さ msec (デフォルト0:消さない) delay: 1000, // 拡張 表示するまでの時間 msec(デフォルト0) }; console.log(await WebPush.push(data)); }; }; </script>

クライアント側クラス WebPush の使い方は簡単。
1. 登録ボタンを押してもらって WebPush.subscribe()
2. 後は、ユーザーが許可してもらった後、pushすることでブラウザからも通知が可能です
3. 通知を止めるときは WebPush.unsubscribe()

サーバー側では、まず準備。data/mailaddress.txt にメールアドレスを記入したテキストファイルを置き、下記コマンドで、data/vapidKeys.json が生成されます。

$ node push.mjs

webサーバーをDenoで起動します

$ deno run -A webpushserver.js

https://localhost:3004/ にアクセスすると、上記画面が開きます。
登録すると、サーバーの data/subscription 内に通知用のデータがたまります(uuid + .json がファイル名)
この uuid 宛に通知がコマンドで送れます。(uuidを記載します)

$ node push.mjs xxxx-uuid-xxx 'push test'

これでOK!(文字列の代わりにJSONでアイコンなど細かな指定も可能です)
コマンドをラップして、Denoで使えるコンポーネントにもしました。

import { push } from "./push_cmd.js" await push("xxxx-uuid-xxx", "push test in Deno");

サーバーから各人へデータを送って見てもらいたいというシンプルなユースケースですが、現状webではなかなか大変なシステムになってしまいますね。 とはいえ、WebPush、今の所、各ブラウザベンダーによる運用で無料かつ無制限で使えるようなので、いろいろ遊んでみましょう!

Facebookグループ「IchigoJam-FAN」で開催中の「IchigoJamで欲しい機能アンケート」、USBキーボード対応、HDMI出力に次いで3位が小数対応。

一般的に小数の計算は遅くなります。特にIchigoJamで使っているような小さなCPUでは小数計算用の回路もないので、遅くなってしまいます。MSX BASICでは基本変数は小数として扱われ、DEFINTA-Zで整数として宣言することで高速化するテクニックがありました。また小数計算を導入する場合、どのような文法にするかは悩ましい問題。

小数を使った計算をしたくなることは確かにあります。16bitのIchigoJam変数の範囲に収まるようにどう計算式を組み立てるか考えたいときなどに便利です。そこで頼るは、ネットの力。IchigoJamの無線LAN拡張ボード、MixJuiceに対応した小数計算サービスを作りました。(src on GitHub)

?"MJ GETS app.sabae.cc/svrcalc/?2.1+2.2 4.300000000000001

このように結果が返ってきます!四則演算のみに対応しています。

IchigoJam web の I/O → MixJuice オプションでも動きます。

作り方と、微妙な誤差についての解説。

借りているサーバー app.sabae.cc に、式が届いたら結果をテキストで返すプログラムをDeno/JavaScriptで書いて動かしっぱなしにしています。

import { createApp } from "https://servestjs.org/@v1.1.7/mod.ts"; import { calc } from "./calc1.js"; const main = (q) => { return calc(q) + "\r\n"; }; const app = createApp(); app.get(/\/*/, async (req) => { const q = req.url.lastIndexOf("?"); if (q < 0) { return; } const s = req.url.substring(q + 1); const body = main(s); const headers = new Headers({ "Content-Type" : "text/plain", "Access-Control-Allow-Origin": "*", }); await req.respond({ status: 200, headers, body }); }); app.listen({ port: 3001 });

コアになる計算する部分 calc1.js はこういう感じ

const calc = (s) => { const n = s.match(/(\d+(\.\d+)?)([\*|\+|\-|\/])(\d+(\.\d+)?)/); if (!n) { return "err"; } const a = parseFloat(n[1]); const b = parseFloat(n[4]); const op = n[3]; switch (op) { case "*": return a * b; case "+": return a + b; case "-": return a - b; case "/": return a/ b; } return "err"; }; // console.log(calc("0.1+0.2")); export { calc };

正規表現を使って受け取った文字列から数2つと演算子を取り出し、計算結果を返しています。 JavaScriptでの数は、IEEE 754 の倍精度 64ビットバイナリ形式、2進法による浮動小数なので0.1を正確に記録できません。使いたい精度に合わせて適宜丸めましょう。

なぜ、denocalcのように、evalを使わないのでしょう?
ヒントはサイバーセキュリティ。アクセス制限をしていないサービス、悪意を持った人に好き勝手されないよう気をつけましょう。
→ 中高生向けサイバーセキュリティ教育プログラム「CyberSakura

SSL化や、いろんなサービスへと接続するフロントとして、nginxというwebサーバーを使っています。

$ vi /etc/nginx/nginx.conf server { ... location /svrcalc/ { proxy_pass http://localhost:3001/; } $ nginx -s reload $ git clone https://github.com/taisukef/servercalc.git $ cd servecalc $ nohup deno run -A server.js &

試しに「https://app.sabae.cc/svrcalc/?2+2」などとアクセスし、4が返ってきたら成功!

サーバーが自在に使えると楽しいですよ!
時給1円のサーバーくんをプログラミングで自在に操ろう! クラウド入門 ConoHa API編

部活の代わりにHana道場でプログラミングしに通う中学1年生。ステキな制度!

IchigoJamを卒業し、今のメイン言語はJavaScript。高専生にも人気のゲームYacht(ヨット)の自作バージョンを見せてもらいました! 自分でつくりたいものがつくれるっていいですね!(IchigoJamバージョン

13才になったということでオススメ、GitHub!
じぶんのウェブサイトをつくろう! 13才以上なら誰でも無料で持てるホームページ&HTMLとCSSはじめのいっぽ
検索していろんなコードを使ったり改造したり遊び放題!

JavaScriptは、ブラウザだけでなく、パソコンやサーバーでも動きます。Denoをインストールして、コマンドプロンプト(cmd)やターミナルで、

$ deno Deno 1.5.2 exit using ctrl+d or close() > alert("hi") hi [Enter]

と、タイプするだけ、すぐに使えます!
(Deno 1.5でブラウザでおなじみ alert, confirm, prompt にも対応!)


トップ | 高志中学校サイエンス部
こちら福井市の高志中学校サイエンス部のGitHubで公開されているウェブサイト。ネットがあれば遠くの学校との交流ができちゃいますね! 英語で書けばグローバル!


koshiscience/koshiscience.github.io: 高志中学校サイエンス部の公式ホームページ
どのようにできているか、プログラムもGitHubで公開されています。

新型コロナウイルス対策ダッシュボードのデータに「Eihime」という変の間違った県名があるとご連絡あり調べます。(thanks! @ReQ13190149さん)

$ grep -ril Eihime * bedforinfection.json bedforinfection_summary.json covid19japan/000610713.pdf.json covid19japan/000610659.pdf.json covid19japan/000610352.pdf.json covid19japan/2020-03-19.csv covid19japan/2020-03-25.csv covid19japan/2020-03-24.csv covid19japan/2020-03-18.csv covid19japan/2020-03-23.csv covid19japan/000613659.pdf.json covid19japan/2020-03-22.csv covid19japan/2020-03-20.csv covid19japan/2020-03-21.csv covid19japan/000612030.pdf.json covid19japan/000612818.pdf.json covid19japan/000611435.pdf.json covid19japan/000610761.pdf.json covid19japan-all.json

昔のデータに残ってしまっていたようです。文字列を Eihime から Ehime に書き換えるプログラムを作って、動かし、確認、アップして修正完了!

const src = "Eihime"; const dst = "Ehime"; const path = "data/"; const ext = ".csv"; const list = await Deno.readDir(path); for await (const f of list) { const fn = path + f.name; if (!fn.endsWith(ext)) { continue; } const s = await Deno.readTextFile(fn); const s2 = s.replace(new RegExp(src, "g"), dst); if (s != s2) { await Deno.writeTextFile(fn, s2); console.log(fn); } }

動かすときは、

$ deno run -A repall.js

ついでに検索するだけのプログラム findall.js と共に、GitHubにて公開しました。

taisukef/repall: replace all in a directory

いろいろつくりましょう!

ぼくの考える最強の英語の学習法は、言いたいことを翻訳、練習して、まずしゃべること。 関連する質問は聞きとりやすく、関心持ってくれている人なので優しく話してくれます。

練習にぴったりなツール、Macのコンソールで標準で使える楽しいコマンド、say のご紹介。

say hello

「ハロー」と聞こえましたか?
sayのみで起動すると、インタラクティブにいろいろしゃべってもらえて便利です。

原稿をまとめたテキストファイルをしゃべってもらうには

cat input.txt | say

音声aacファイルとして保存する場合は

cat input.txt | say -o output.aac

と使います。


声質の変更が、設定、アクセシビリティからできます。 スピーチ(音声合成)を選び、システムボイスの選択肢から「カスタム」を選ぶと、ずらっといろんな音声がダウンロードできます。 自動でチェックされたものを全部ダウンロードすると10GBとか容量を取ってしまうので、一旦全部チェック外して、再生しながら使いたいものだけ選びます。

オススメは、英語の Samantha(女性)、Tom(男性)。非ネイティブだからだと思いますが、かなり自然な発音をしてくれます。

声を変更するのは -v オプション

say -v Samantha "This is a pen"

流暢です!

Agnesはローレゾなコンピュータっぽい感じでよい感じなので、お試しください!
sayhelper」 on GitHub
いろいろな音声化したテキスト、お試しください!

Alexや、Agnesなど、行間における間がちょっと短いので、DenoのJavaScriptを使って、開業時に間を取るプログラムを混ぜてみました。(標準入出力の使い方 in Deno)

import { readLines } from "https://deno.land/std/io/mod.ts"; for await (const line of readLines(Deno.stdin)) { console.log(line); console.log(".."); }

英語以外の言語の練習にも使えそうです。中国語のTing-Tingや、中国語(台湾)のMei-Jiaもかなりスムーズな印章。 日本語は、女性のKyokoは少し不自然を感じますが、男性のOtoyaはなかなか自然!ちょっと関西風な味付けがおもしろい!

say -v Kyoko "これはとてもかっこいいペンです" say -v Otoya "これはとてもかっこいいペンです" say -v Ting-Ting "这是很酷的笔"

練習して話せるようになるのも楽しいですが、sayコマンドでリアルタイムにしゃべってもらうのもいいかも!

ツールで越えよう、言語の壁!

スマホ、PC、サーバー、VRまで、応用範囲が広くて楽しいJavaScriptプログラミング! JavaScriptでサーバー側を開発できるランタイム Deno 1.4、ウェブ標準のWebSocket APIが使えるようになりました! ブラウザを使わずにテストコードが書けるので、リアルタイムウェブアプリ開発が捗りそうです! (WebSocket on W3C / MDN)

Denoでのテストコードの書き方は、こんな感じです。(参照、Testing - Manual | Deno

import { assertEquals } from "https://deno.land/std@0.69.0/testing/asserts.ts"; Deno.test("test 1+1", () => { assertEquals(1 + 1, 2); }); Deno.test("test 1<<10", () => { assertEquals(1 << 10, 1024); });

これを calc-test.js と、名前が -test で終わるファイル名を付けて、テストを実施します

$ deno test calc-test.js Check file:///Users/fukuno/data/js/code4sabae-js-sample/.deno.test.ts running 2 tests test test 1+1 ... ok (3ms) test test 1<<10 ... ok (1ms) test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (5ms)

2つのテストが通りました!テストコードを複数作って、deno test でまとめてテストできます。

では、早速、先日の30行シンプルサーバー&クライアントのクライアント側のテストコードを書いてみます。

import { ClientSync } from "https://code4sabae.github.io/js/ClientSync.js"; import { assertEquals } from "https://deno.land/std@0.69.0/testing/asserts.ts"; const fn = async () => { const ws = await new ClientSync().connect("/ws/", "localhost", 8881, false); const req1 = { ab: 1 }; ws.send(req1); const res1 = await ws.get(); assertEquals(res1, { n: 2, testsync: req1 }); const req2 = { ab: 2 }; ws.send(req2); const res2 = await ws.get(); assertEquals(res2, { n: 4, testsync: req2 }); const req3 = { ab: 128 }; ws.send(req3); const res3 = await ws.get(); assertEquals(res3, { n: 256, testsync: req3 }); await ws.close(); }; Deno.test({name: "websock-test", fn, sanitizeOps: false });

次にサーバーのコードです。今回は5回返したら、終了させることにします。

import { ServerSync } from "https://code4sabae.github.io/js/ServerSync.js"; import { sleep } from "https://code4sabae.github.io/js/sleep.js"; new ServerSync(8881, async (ws) => { for (let i = 0; i < 5; i++) { const data = await ws.get(); await sleep(500); ws.send({ testsync: data, n: data.ab * 2 }); } });

サーバーコードを test-sync.js として、起動

$ deno run -A test-sync.js

次に、さきほど作ったテストコードを動かします。

$ deno test -A cli-test.js

コンソール上でいろいろとチェックできるのは便利!
* WebSocketを使ったコード、何かがリークしてうまく終了してくれなかったので、sanitizeOpt: false を追加しています。 テストは通りますが、終了してくれないので、まだ何か変なのかも。

Let's create realtime web apps!

links
- 30行でOK! サーバーとクライアントをシンプルに実装するJavaScript同期処理ライブラリ

プログラミングボードゲーム「囲みます」の今日の試合、戦況を見ながらリアルタイムに手動でAIを切り替えるプログラムを作ったすずともが優勝。 重要なのは人とコンピューターのコラボ!GUIをHTML/CSS/JavaScriptで、手軽に作るためのライブラリを作りました。

まずはサーバー側。クライアント届くデータを await getで受け取り適当に処理してsendで返すテストをプログラムを作って、test-sync.js として保存します。(この例では500msec無駄に待って、倍返し)

import { ServerSync } from "https://code4sabae.github.io/js/ServerSync.js"; import { sleep } from "https://code4sabae.github.io/js/sleep.js"; new ServerSync(8881, async (ws) => { for (;;) { const data = await ws.get(); await sleep(500); ws.send({ testsync: data, n: data.ab * 2 }); } });

続いて、クライアント側。HTMLで作ったボタンが押されるたびに、カウントしてサーバーへ送信し、返答を待って表示するプログラム。staticというフォルダを作り、index.html として保存します。

<!DOCTYPE html><html><script type="module"> import { ClientSync } from "https://code4sabae.github.io/js/ClientSync.js"; import { waitClick } from "https://code4sabae.github.io/js/waitClick.js"; window.onload = async () => { const ws = await new ClientSync().connect("/ws/"); let cnt = 0; for (;;) { await waitClick(btn); ws.send({ ab: ++cnt }); const res = await ws.get(); console.log(res); divn.textContent = res.n; } }; </script><body> <button id="btn">click me!</button><br> <div id="divn"></div> <body></html>

JavaScriptのランタイム、Denoを使って動かしましょう。コンソールで test-sync.js があるフォルダに移動し、下記コマンドで実行。

$ deno run -A test-sync.js

ブラウザで http://localhost:8881/ を開くと、動かせます!
サーバーとクライアントをつないだプログラムができました!簡単!


反応が遅い?サーバーで時間がかかる処理をやっている風を演出しているだけです。test-sync.jsの await sleep をコメントアウトしたり、数値を変えてみましょう。

簡単さの肝は、WebSocketClientWebSocketServerクラス。await getとsendを使ってサーバーもクライアントも同じように使えるように、WebSocketを同期処理でカプセル化しています。(src on GitHub)

同期型通信の利点は、APIを一つ一つ設計しなくても、サーバーとクライアントのやり取りをそれぞれ順序立てて作るだけなので分かりやすいことです。福井高専の卒業研究や、卒業後エージェント指向が流行った頃の仕事は、同じようなコードをJavaで実装してたことを思い出しました。

HTML/CSS/JavaScriptでいろんなUIを作って、サーバー通信を経由すれば、手軽にいろんなことができますね。 強力な処理を作った時は、接続元をローカルIPに限定する、鍵を付けるなど、セキュリティ対策も忘れずに!

人に変わってサービスし続けてくれるサーバー。その元祖、タイムシェアリングシステムは、60年近く前、1961年開発。

商業的初成功を収めたダートマス・タイムシェアリングシステム(DTTS)は、誰もがコンピューターを使えるようにとダートマスBASICと共に1964年開発。 見慣れたコマンド、LIST / RUN / SAVE / NEWが並ぶ。 そう、これらはBASICのコマンドではなく、TSS(今で言うシェル)のコマンドだったのです!

圧倒的に高速なコンピューターくん、マイクロ秒単位で相手する人を切り替え、人間から見てあたかも同時に対応してくれているように振る舞います。 シェルは、コンピューターと人とをつなぐ窓口のひとつ。IchigoJam BASICのように、コマンドを送ると、その結果を返事として返してくれます。

開発大詰め迎えるjigインターン、デモのためそれぞれチームごとに提供しているサーバーを設定し、Deno(JaavScript)で開発しているサービスをデプロイ(公開)しよう!

サーバー(AWS)上でDenoをサービスとして動かす方法
まずサーバーにログインする

動かしたいサービスをGitHubからとってくる
リポジトリの緑ボタン[Code]からで、Use HTTPS に切り替えて、Clone with HTTPSのアドレスをコピー

git clone https://github.com/.....git

Webサーバー Nginx(エンジンエックス)設定のため、一時的にrootユーザーになる

sudo su -

Webサーバーnginxの設定を追加する

cat > /etc/nginx/default.d/team.conf

下記を貼り付けて、Ctrl-D(ポート番号 8881 はそれぞれの環境に合わせる)

location / { proxy_pass http://127.0.0.1:8881; } location /ws/ { proxy_pass http://127.0.0.1:8881/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }

nginxを再起動して設定有効化

nginx -s reload

で動かすとエラーがでる

Location の設定がかぶっているので、下記を修正

vi /etc/nginx/conf.d/ssl.conf vi /etc/nginx/nginx.conf

"Location / { }" を # を頭に付けてコメントアウト

再び再起動

nginx -s reload

で動かし、エラーがでなければ設定完了!

exit

で、ユーザーを root からもとに戻す

サービスの動かし方

cd [service path] deno run -A main.js

サーバーのホスト名をブラウザに入れて、確認してみよう!
自分たちで作ったサービスが動くと感動ですね!

画面共有と音声帯域を使ったリアルタイムサポート、チームで感動も共有!

サービスとして、ターミナルから抜けてもずっと動くようにする方法

nohup deno run -A main.js &

サービスの止め方

ps -u [account name]

denoのPIDを探して

kill [PID]

サービスの止め方2
サーバープログラムに、ファイルを見たり、特定アクセスで Deno.exit(0) するようにプログラムしておく

サービスの更新方法

git pull

サーバーで更新してしまっていて、pullできない場合

git stash

で、一時保存した上で、 git pull
サーバープログラムを更新した場合は、サービスを一旦止めて、起動
(staticフォルダ内など、クライアント用ファイル更新のみの場合は再起動不要)

サーバー操作にも通じる、BASIC。
黒い画面に強いこども達が生む、サービス誕生が楽しみです!


ザイログZ80伝説(カラー版) | 鈴木 哲哉 | 工学 | Kindleストア | Amazon
今も生き続けるZ80、開発秘話から現況、タイムシェアリングのはじまりから回路設計まで幅広く多様な内容が楽しい!


IchigoJam BASICの元とした、タイニーBASICのプログラムも掲載!
このBASICでは、@マークでメモリアクセスになってますね。ご先祖を想って楽しい、コンピューター考古学!

せっかくなので、世界最初のBASICプログラムと言われる、エラトステネスの篩による素数計算をIchigoJam BASICで実装。アルゴリズム、見比べてみましょう。

1 'Sieve of Eratosthenes 10 CLV 20 FOR I=2 TO 10:?I 30 IF [I]=1 NEXT 40 N=2 50 M=I*N:IF M<102 [M]=1:N=N+1:CONT 60 NEXT 70 ?"PRIME NUMBERS" 80 FOR I=2 TO 101:IF [I]=0 ?I;" "; 90 NEXT:?

今日のjigインターンブログは、もしゃ!
jig.jpオンラインインターン2020 8日目! - jigintern2020’s diary
情報交換タイムでのおもしろ写真付き!

バーチャル背景生成アプリというジャンルもおもしろい!

どんな大規模プログラムも小さなプログラムの集まりです。 きちんと動くプログラムを作るコツは、構成する小さなプログラムをひとつひとつをシンプルかつ、丁寧に仕上げておくこと。 小さく切り分けておくことで、何か問題があった時に、すばやく解決することができるでしょう。


チーム開発で進む、高専プロコンの競技部門予定だった「Code for KOSEN、囲みマス」。 練習試合は盛り上がるも、忍者が消えてしまうコアモジュール「Kakomimasu.js」のバグは直さねば。

Denoの標準、testフォルダにテストコードを書いて、問題を特定、解決した。

1. 早速、チーム開発の基本に従い、debug-code branch作成。

2. 元々のシンプルなテストコード conflict1_test.js は問題なく通っていた。

cd test deno test conflict1_test.js

3. 忍者が寝返ってしまう状況を再現するランダムテスト random_test.js を書いて実験、時々おかしなことが起きる。

4. 修正がうまくいった時に再現できるよう、乱数の種を設定できる高性能なランダム MersenneTwister.js を用意。

5. おかしなことが起きるタイミングが掴めたので、そのタイミングに合わせて詳しい表示を console.log で入れて、チェック。REMOVEとMOVEの相互競合が原因と分かったので、まずは再現するテストコード conflict3_test.js を書いて、問題が起きることを確認。

6. コア修正し、テストコードが動くようになることを確認!

deno test

(全テストコードをまとめてテスト!)

7. コミットし、プルリク、レビューしてもらって、マージしましょう(GitHubでのチーム開発

テストコードがあれば、何か機能を足した時にデグレ(デグレーショション、退化)していないかを確認できて安心です。 とはいえ、最初から、完全なテストコードを書くのは大変なので、必要となる品質など、開発全体からのバランスを見て決めましょう。

プログラムをどこから手を付けていいか悩む時、テストコードから最初に書く、TDD(テスト駆動開発)という手法も有効です。 問題の切り分け、テストコードと実装、チームで開発する祭の役割分担にも良いですね。楽しい、開発を!

jigインターン、ぷりんによる、1週間のふりかえり!
jig.jpオンラインインターン2020 一週間の振り返り!byぷりん - jigintern2020’s diary

流れが早いweb開発界隈ですが、スマホの台頭とブラウザの仕様が整って来たので、まずはシンプルにはじめましょう!

サーバーとブラウザ上で動くクライアント、それぞれいろいろな選択肢がありますが、ブラウザのために産まれたプログラミング言語「JavaScript」を、サーバーでも使うのが、学習しやすくオススメです。

Windows、Mac、Linux、どの環境の人でも大丈夫。まずは「Deno」をインストール!
(WindowsのPowerShellは「Windowsキー+R」を押して「powershell」と書いてエンター、インストールスクリプト「iwr https://deno.land/x/install/install.ps1 -useb | iex」を貼り付け、エンターで実行!)

公式サイト記載の方法に従ってインストールしたら、コンソールヤコマンドプロンプトを開いて、次のコマンドでバージョン番号が出ることを確認しましょう

$ deno --version deno 1.3.0 v8 8.6.334 typescript 3.9.7

続いて、何かディレクトリを作成し、移動

$ mkdir test $ cd test

そのディレクトリ内に、次の2行だけのプログラムを main.js として保存します

import { Server } from "https://code4sabae.github.io/js/Server.js"; new Server(8881);

実はこれだけでwebサーバープログラムできあがり。
動かす時は deno の run コマンド(-Aでファイルアクセス、ネットアクセスを許可)

$ deno run -A main.js

表示されるアドレス http://localhost:8881/ を開いてみましょう。
「Not Found」と表示されますね。(code4sabae-js は、Serverst で動いてます)

表示する HTML は、static ディレクトリを作成し、その中に index.html を作成します。(test/static/index.html)

<h1>Hello web!</h1>

ブラウザをリロードすると表示されるはず!

web開発の基本は、ブラウザとサーバーとの通信ですが、送受信ともにJSONを使って、すっきりシンプルに作りましょう。

下記のように、Serverクラスを派生した、MyServerクラスに api 関数を用意します。ひとまず受け取ったパスやパラメーターに時間を付けて返すテストします。

import { Server } from "https://code4sabae.github.io/js/Server.js"; class MyServer extends Server { api(path, req) { const time = new Date(); return { path, req, time }; } } new MyServer(8881);

先程動かした Deno のサーバープログラムを Ctrl-C で止めて、上を押してエンター。もう一度実行します。2回目以降は即起動!

http://localhost:8881/api/ を開くと、JSONで「時間」が返ってきます。

次に、index.html を編集して、ブラウザからサーバーへデータを送り、サーバーからのレスポンスを表示してみます。 下記のように、JavaScriptのコードを埋め込みます。今どきJavaScriptは、type="module"!

<!DOCTYPE html> <html><body> <button id="btn">fetchJSON</button><br> <textarea id="ta"></textarea><br> <script type="module"> import { fetchJSON } from "https://code4sabae.github.io/js/fetchJSON.js"; btn.onclick = async () => { const req = { a: 123, b: "abc" }; const res = await fetchJSON("http://localhost:8881/api/test/v1", req); ta.value = JSON.stringify(res); }; </script> </body> </html>

ボタンを押すと、テキストボックスにサーバーからのレスポンスが表示されます!
ブラウザから送られてきた JSON をあれこれ加工すれば、みんなで使うwebサービスが開発できそうですね!

HTML/CSS/JavaScriptに関する復習はこちら → 「JSはじめのいっぽ

プログラムを止めてもデータは残しておきたい?
データベースを学習するのもいいですが、まずは手っ取り早くファイルに保存しておきましょう。 例えば、JSONデータを保存するには、次のように書きます(参考、builtin@stable - deno doc

Deno.writeTextFileSync("test.json", JSON.stringify(json));

メモリは多量にあるので、プロトタイピング中はデータをまるっと1ファイルに保存しておき、起動時に読み込むくらいで十分です。

みんなが写真をアップロードできるようにすると、webサービスは楽しくなります!
便利機能を用意しました。選ばれたファイルを自動的にいい感じのサイズにして、サーバーの data ディレクトリにアップロード。 アップロードされた URL を取得して、あれこれ使うことができます。

<!DOCTYPE html> <html><body> <h1>image uploader test</h1> <input id="file1" type="file" name="file1" multiple> <div id="imgc"></div> <br> <button id="btn">get file url</button><br> <textarea id="ta" style="width: 80vw; height: 10em"></textarea><br> <script type="module"> import { ImageUploader } from "https://code4sabae.github.io/js/ImageUploader.js"; const maxwidth = 1200; const maxsize = 1 * 1024 * 1024; const uploadurl = "http://127.0.0.1:8881/data/" //const uploadurl = "/data/" file1.onchange = async e => { for (const file of e.target.files) { const type = file.type; if (!type.startsWith("image/")) { continue; } const imgup = new ImageUploader(uploadurl); imgup.setFile(file, maxwidth, maxsize); imgc.appendChild(imgup); } }; btn.onclick = () => { const urls = []; for (let i = 0; i < imgc.children.length; i++) { urls.push(imgc.children[i].value); } console.log(urls); ta.value = urls.join("\n"); }; </script> <a href="./index.html">top</a><br> </body> </html>

大きすぎる写真は使いづらいので、縦横最大サイズを1024以下または、1MB以下にしています。maxwidth や maxsize で設定可能。

このように、ファイルをまとめて選んで、アップロードできます。手元のサーバー data ディレクトリを確認しましょう。

ファイルアップロードで使う画像を扱うあれこれ(Image, ArrayBuffer, Blob, Canvasなど)や、img-upload タグなどのプログラムも短めなオープンソース。 自分なりに改造するのも歓迎です!(src on GitHub)

IchigoJamで動作する数十byte単位のプログラムも実装可能なモダンプログラミング言語 Rust。 サーバー実装の実験にリアルタイムwebの要、WebSocketを使って単純に送った文字列が返ってくる、echoサーバーづくり!

JavaScriptのランタイム Denoと、HTTPサーバーモジュール Servest を使った WebSocket の echo server のコードは Servest を使ってこんな感じ。 (servest 1.1.0 では、Deno 1.2.0 でエラーが発生、stdモジュールを新版への差し替えプルリク、下記は servest v1.1.1で動作します!)

import { createApp } from "https://servestjs.org/@v1.1.1/mod.ts"; function handleHandshake(sock) { async function handleMessage(sock) { for await (const msg of sock) { if (typeof msg === "string") { sock.send(msg); } } } handleMessage(sock); } const app = createApp(); app.ws("/ws/", handleHandshake); app.listen({ port: 8080 });

続いて、WebSocketのclient

import { WebSocket } from "https://deno.land/x/websocket/mod.ts"; const endpoint = "ws://127.0.0.1:8080/ws/"; const ws = new WebSocket(endpoint); ws.on("open", () =< { ws.send("hello!"); }); ws.on("message", message =< { console.log("recv", message); ws.close(); });

シンプルに書けて、いい感じ!(deno run -A server.js と den run -A client.js で実行)

続いて、Rustで書く WebSocket の echo server には、速そうな(TechEmpower Web Framework Benchmarksで現在2位)の Actix を使ってみました。

use actix::*; use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer}; use actix_web_actors::ws; struct EchoSession { } impl Actor for EchoSession { type Context = ws::WebsocketContext<Self>; fn started(&mut self, _ctx: &mut Self::Context) { } fn stopping(&mut self, _ctx: &mut Self::Context) -> Running { Running::Stop } } impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for EchoSession { fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) { let msg = match msg { Err(_) => { ctx.stop(); return; } Ok(msg) => msg, }; match msg { ws::Message::Ping(_) => { }, ws::Message::Pong(_) => { }, ws::Message::Text(text) => { ctx.text(text); } ws::Message::Binary(_) => println!("Unexpected binary"), ws::Message::Close(reason) => { ctx.close(reason); ctx.stop(); } ws::Message::Continuation(_) => { ctx.stop(); } ws::Message::Nop => (), } } } async fn echo_route(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, Error> { ws::start(EchoSession {}, &req, stream) } #[actix_rt::main] async fn main() -> std::io::Result<()> { HttpServer::new(move || { App::new() .service(web::resource("/ws/").to(echo_route)) }) .bind("127.0.0.1:8080")? .run() .await }

必要なものはきっちり書くスタイルなので、少し長いですが、やっていることは一緒です。Cargo.tomlにパッケージ名を記入して、cargo runでコンパイル&実行しておきましょう。

[dependencies] actix = "0.9.0" actix-web = "2.0.0" actix-web-actors = "2.0.0"

続いて、client は、こう書けます。

use websocket::{ClientBuilder, Message}; fn main() { let mut client = ClientBuilder::new("ws://127.0.0.1:8080/ws/") .unwrap() .connect_insecure() .unwrap(); let message = Message::text("hello!"); client.send_message(&message).unwrap() let mes = client.recv_message().unwrap(); println!("{:?}", mes); }

Cargo.toml の dependencies に websocket="*" を追加し、Cargo run で動きます。

コネクション確立後、「hello!」を送って返ってくるものを100回繰り返して、時間を計測。 最速タイムで、Deno:35msec、Rust:25msecと、Rustが速いですが、Deno/JavaScriptでもベースは十分速そうです。

ちょっと応用、RustのActix、ブラウザから接続するチャットのサンプルを動かしてみました。 JavaScriptのコードが、jQueryコードだったので、モダンスタイルに更新!(src on GitHub)

速さが要のリアルタイムweb用サーバーづくり、Rustで実装、楽しいかも?

Tweet
クリエイティブ・コモンズ・ライセンス
この作品は「Creative Commons — CC BY 4.0」の下に提供されています。
CC BY / @taisukef / アイコン画像 / プロフィール画像 / RSS