20190415のNode.jsに関する記事は4件です。

Node.js でつくる WASM トランスパイラー - 04:比較演算子を実装する

はじめに

Node.jsで小さなプログラミング言語を作ってみるシリーズを、「ミニコンパイラー」「ミニインタープリター」とやってきました。そして三部作(?)の最後として、 ミニNode.jsからWASMを生成するトランスパイラーに取り組んでいます。

今回実現したいこと

今回は次の比較演算子のサポートが目標です。それぞれ、WASTでは次のように表記します。

  • === ... eq
  • !== ... ne
  • < ... lt_s (符号あり)
  • > ... gt_s (符号あり)
  • <= ... le_s (符号あり)
  • >= ... ge_s (符号あり)

比較の変換例

このミニNode.jsのソースコードを対象とします。

putn(1 !== 2);

putn()は、前回用意した簡易デバッグ出力用の組み込み関数です。

これを読み込み、単純化したASTはこちらになります。

[ 'func_call', 'putn', 
  [ '!==', [ 'lit', 1 ], [ 'lit', 2 ] ]
]

この部分を次のWASTに変換することが、今回の目的です。

(call $putn
  (i32.ne
    (i32.const 1)
    (i32.const 2)
  )
)

比較演算子の生成

早速ランスパイラーのgenerate()関数を拡張します。

function generate(tree, indent, lctx) {
  // ... 省略 ...

  // --- compare operator ---
  if (tree[0] === '===') {
    return generateCompareOperator(tree, indent, 'eq', lctx);
  }
  if (tree[0] === '==') {
    return generateCompareOperator(tree, indent, 'eq', lctx);
  }
  if (tree[0] === '!==') {
    return generateCompareOperator(tree, indent, 'ne', lctx);
  }
  if (tree[0] === '!=') {
    return generateCompareOperator(tree, indent, 'ne', lctx);
  }
  if (tree[0] === '>') {
    return generateCompareOperator(tree, indent, 'gt_s', lctx);
  }
  if (tree[0] === '>=') {
    return generateCompareOperator(tree, indent, 'ge_s', lctx);
  }
  if (tree[0] === '<') {
    return generateCompareOperator(tree, indent, 'lt_s', lctx);
  }
  if (tree[0] === '<=') {
    return generateCompareOperator(tree, indent, 'le_s', lctx);
  }

  // ... 省略 ...
}

// --- compare operator ---
function generateCompareOperator(tree, indent, operator, lctx) {
  const leftBlock = generate(tree[1], indent+1, lctx);
  const rightBlock = generate(tree[2], indent+1, lctx);

  let block = TABs(indent) + '(i32.' + operator + LF();
  block = block + leftBlock + LF();
  block = block + rightBlock + LF();
  block = block + TABs(indent) + ')';
  return block;
}

実際にコードを生成するのは、generateCompareOperator()関数で行なっています。

比較演算子の実行

対象ソース

こちらのコードをWASM生成対象とします。

sample/neq.js
putn(1 !== 2);

0;

WASMの生成

今回のステップまでのトランスパイラーのソースコードを、mininode_wasm_04.js とします。次のようにgenerated.wast を生成します。

$ node mininode_wasm_04.js sample/neq.js

生成結果はこちら。

generated.wast
(module
  (func $putn (import "imports" "imported_putn") (param i32))
  (export "exported_main" (func $main))
  (func $main (result i32)

    (call $putn
      (i32.ne
        (i32.const 1)
        (i32.const 2)
      )
    )

    (i32.const 0)

  )
)

これを WASM に変換、前回用意した run_wasm_putn.js を使って実行します。

$ wasm-as generated.wast
$ node run_wasm_putn.js generated.wasm
Loading wasm file: generated.wasm
1
ret code=0

「1 !==2」は true なので、1 が標準出力に表示されました。

色々な比較の実行

もう一つ、色々な比較を使ったサンプルを変換、実行してみます。

comp.js
let a = 1;
putn(a); // expect 1

let b = (a === 2);
putn(b); // expect 0
putn(a == 1); // expect 1

let c = (a !== 2); 
putn(c); // expect 1
putn(a != 1); // expect 0

putn(999);

a = a * 10;
putn(a > 10) ; // expect 0
putn(a >= 10) ; // expect 1
putn(a < 10) ; // expect 0
putn(a <= 10) ; // expect 1

22;

実行結果はこちら。色々な比較演算子も想定通りの出力になりました。

Loading wasm file: generated.wasm
1
0
1
1
0
999
0
1
0
1

次回は

目標の1つであるFizzBuzzに必要な、条件分岐を実装する予定です。

ここまでのソース

GitHubにソースを上げておきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

無料で作る集計Webページ!N予備校フォーラム名誉会員のシステムについて

スクリーンショット 2019-04-15 16.49.27.png
こんにちはー!
N予備校プログラミング入門コース、講師の 折原ダビデ竜 です。

N予備校には勉強に躓(つまず)いた時などに質問を投稿することができる「フォーラム」という最高すぎる機能があるのはご存知でしょうか。
ここでは日々たくさんの質問が投稿され、我々講師の方々などが返答しています。

ですが!

これらの質問に答えることこそ、実はすごく良い勉強になるのです。
そして実際に回答してくださっている受講者の方もたくさんいらっしゃっいます。

そこで!

  • このフォーラムの質問に回答してくださっている受講者の方々を讃えたい!
  • さらにより多くの受講者の方にも回答をしてみてもらいたい!

という気持ちが高ぶり

そしてこの度!

N予備校フォーラム名誉会員(めいよかいいん) という称号が回答を多くして下さっている受講者に与えられるシステムを作成いたしました✨
https://progedu.github.io/forum-ranking/
スクリーンショット 2019-04-15 17.19.30.png

N予備校フォーラム名誉会員 について

N予備校のフォーラムに回答してくれている回数が多い方々を「フォーラム名誉会員」とし、
フォーラム名誉会員のページに アカウント名・アイコン・フォーラム貢献数・コメント数 などが掲載され、讃えられます!

毎月更新される「月間フォーラム名誉会員」と全ての期間の「全期間フォーラム名誉会員」があり、日頃からフォーラムの質問に回答してくださっている方々に感謝の気持ちをお伝えしたく、作成いたしました。

こちらでも報告しています。
https://www.nnn.ed.nico/questions/10030

なんと!

この集計ページは全て無料のサービスで作られています!

次に、どのようにしてこの フォーラム名誉会員のページ が作られているのかについても書いてみたいと思います。

N予備校 フォーラム名誉会員のシステムについて

このフォーラム名誉会員のページは、GitHub Pages という無料で Webサイト を公開することができる GitHub の機能を使って無料で実現できています。
(GitHub Pages の使い方は プログラミング入門コース で教えています。)

そして N予備校のフォーラムからデータを取得し、回答数などを集計するシステムは Node.js のプログラムで書かれ、CircleCI にて毎朝4時に実行されることで毎日の集計を実現しています。
CircleCI は、無料でもプログラムのテストを実行してくれるサービスで、集計を行うプログラムを test.js ファイルに記述し、これを定期実行してもらうことでデータの集計を実現しています。
CircleCI についても N予備校プログラミング入門コース で教えています。)

集計結果のデータですが、本来ならばデータベースを使いたかったのですが、まだそれほど大きなデータでも無いということから JSON ファイルに集計結果を書き出すようししました。

以上を図にするとこんな感じです。
forumRankingSystem.png

このようにして GitHub(無料)、GitHub Pages(無料)、CircleCI(無料)のみを使用した無料の集計ページを作ることができました。
みなさまの開発の参考になればと思います。

また、GitHub にソースコードも載せていますので、このシステムをより良くできるアイデアなどありましたら気軽に issue や プルリクエストを投稿してみてください。
GitHub の URL → https://github.com/progedu/forum-ranking

最後に

N予備校には勉強に詰まった時に質問を投稿できる「フォーラム」というシステムがあります。
質問に答えることもすごく良い勉強になりますので、困っている人がいたら助け合い、N予備校をより良い物にしていきましょう。

そして日頃からフォーラムの質問に回答してくださっている方々に感謝をお伝えしたいと思います。

以上、Quiita 初投稿でした?

sugoi1_dqr.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MicrosoftTeamsにGoogleNewsBotを入れてみた

MicrosoftTeamsにGoogleNewsBotを入れてみた

ゴールイメージ

スクリーンショット 2019-04-15 13.11.56.png

動機

  • 会社の公認コミュニケーションツールがSlackじゃなくてTeamsになりそう
  • Teamsのwebhookを使ってみたい
  • お友達の作ったGoogleNewsBot for SlackをTeamsに改造してみよう

実行環境とフロー

  • AWS
    • CloudWatch ルールでLambda(Node.js)を定周期起動
    • GoogleNewsRSSを利用して、指定したキーワドのNewsを取得して、Teamsに投げ込む

Teams側の設定

  • Newsを表示したいチャンネルにIncoming Webhookコネクタを作成する必要がある。
  • 詳しい解説記事が既にQiitaにあるのでこちら参照して作成してください(チャネルの画面に戻ると 3 で付けた名前とアイコンの何者かが参加していますまで実行すれば大丈夫)

ローカルで試してみよう

npmインストール

  • request-promise
  • request
  • rss-parser
  • さくっとやりたいのでserverlessは使わない
$ mkdir newsbot-teams
$ cd newsbot-teams
$ npm install request-promise request rss-parser

ソース

  • 下記構成でそれぞれ作成してください

tree

.
├── index.js
├── google-news-service.js
├── main.js
└── package.json

index.js

index.js
'use strict';

const rp = require('request-promise');
const lodash = require('lodash');
const GoogleNewsService = require('./google-news-service.js');

function dateTimeFilter(articles, fromDate) {
    return articles.filter((article) => {
      const pubDate = new Date(article.pubDate);
      return (pubDate.getTime() - fromDate.getTime()) > 1;  
    });
  }

function post2Slack(teams_webhook,message) {
  return rp.post(teams_webhook, {
    headers: { "Content-type": "application/json" },
    json: { text: message } ,
    uri : teams_webhook
  });
}

async function main(fromDate,teams_webhook,keywords) {
  try {
    const googleNews = await (new GoogleNewsService()).exec(keywords);
    const news = lodash.uniqBy(dateTimeFilter(lodash.concat([], googleNews), fromDate), 'link');
    const titleList = news.map((article) => {
      return `・[${ article.title }](${ article.link })<br>`;
    }).join('');
    const message = 'ニュースとってきた。<br>' + titleList;
    await post2Slack(teams_webhook,message);
  } catch(error) {
    console.error(error);
  }
}

exports.handler = (event, context, callback) => {
    const fromDate = new Date();
    const teams_webhook = event['teams_webhook'];
    const keywords = event['keywords'];
    fromDate.setHours(fromDate.getHours() - event['fromHour']);
    main(fromDate,teams_webhook,keywords);
};

google-news-service.js

google-news-service.js
const Parser = require('rss-parser');
module.exports = class GoogleNewsService {
  constructor(){
  }
  createGoogleNewsUrl(keyword) {
    return 'https://news.google.com/_/rss/search?q='
      + encodeURIComponent(keyword)
      + '&hl=ja&gl=JP&ceid=JP:ja';
  }
  async exec(keywords) {
    const splitkeywords = keywords.split(',');
    const parser = new Parser();
    const feeds = await Promise.all(splitkeywords.map((keyword) => {
      return parser.parseURL(this.createGoogleNewsUrl(keyword));
    }));
    const articles = [];
    feeds.forEach((feed) => {
      feed.items.forEach((item) => {
        articles.push(item);
      });
    });
    return articles;
  }
}

main.js

  • ローカルでlambdaっぽく呼ぶやつ
main.js
var event = {
    version: '0',
    id: 'XXXXXXXX-XXXXX-XXXX-XXXXX-XXXXXXXX',
    'detail-type': 'Scheduled Event',
    source: 'aws.events',
    account: 'XXXXXXXXXX',
    time: '2019-01-30T06:27:04Z',
    region: 'ap-northeast-1',
    resources:
        [ 'arn:aws:events:ap-northeast-1:XXXXXXXXXX:rule/box-auditlog-exec' ],
    detail: {},
    fromHour : '10',
    teams_webhook : 'https://outlook.office.com/webhook/XXXXXXXXXXXXXXXXXXXX/IncomingWebhook/XXXXXXXXXXX/XXXXXXXXXXXXXX',
    keywords : 'AWS,GCP,Azure,CentOS,IDaaS,okta,onelogin,ゼロトラスト,zero trust,Nginx,Microsoft,Windows Server,脆弱性,ゼロデイ'
};

var context = {
    invokeid: 'invokeid',
    done: function(err,message){
        return;
    }
};

var lambda = require("./index.js");
lambda.handler(event,context);
適宜変更してください
  • fromHour : 過去何時間分のニュースを取得するか設定
  • teams_webhook : Teams webhooksのurl.
  • keywords : 取得したいNewsキーワード

ローカルで実行

$ node main.js

指定したTeamsにニュースがポストされたら成功
スクリーンショット 2019-04-15 13.11.56.png

AWSにアップして動かしてみよう

事前準備

  • lambda関数作成

    • ランタイム:Node.js V8.10
    • タイムアウト:3分

    qiita3.png

AWSへデプロイ

  • 今回はS3を経由しているが、S3を経由する必要は特に無い
$ zip -r newsbot-teams.zip index.js node_modules
$ aws s3 cp ./newsbot-teams.zip s3://[mybucket]/newsbot-teams.zip --profile [myprofile]
$ aws lambda update-function-code --function-name newsbot-teams --s3-bucket [mybucket] --s3-key newsbot-teams.zip --publish --profile [myprofile]

実行

  • CloudWatch ルールに、lambdaを指定して引数を設定して起動する。
    • 周期はお好きなように GMTだから気をつけて
    • 引数サンプル:入力の設定-定数(JSONテキスト)
{"fromHour": "15", "teams_webhook": "https://outlook.office.com/webhook/xxxxxxx/IncomingWebhook/xxxxxxxxxxx/xxxxxxxx", "keywords": "AWS,GCP,Azure,CentOS,IDaaS,okta,onelogin,ゼロトラスト,zero trust,Nginx,Microsoft,Windows Server,脆弱性,ゼロデイ"}

スクリーンショット_2019-04-15_13_30_17.png

部単位のチャンネルトとかに、部で共有したいNewsキーワードを設定して毎日1回とか数回ポストすると良いと思う。日常業務に忙殺されて、情報をキャッチアップできてない人多いし。

ソース置き場

こちらにソース置いておきます。(ソース汚かったらPullReqください)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express風の簡単WebSocketフレームワーク、Hexnutのドキュメントを読んで試してみた

注意

WebSocketを趣味で勉強しているものです、間違いなどありましたらご指摘お願いします :bow:

Hexnut とは

google翻訳
?Hexnutはミドルウェアベースの、Express / Koa風のWebソケット用フレームワーク

Installing HexNut

インストール

npm i hexnut

Creating a server

サーバー起動

server.js
const HexNut = require('hexnut');
const app = new HexNut({ port: 8080 });

app.start();
node server.js
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("open", e => console.log("socket open"));
}
</script>
</body>
</html>

これでWebSocketサーバーが簡単に起動でき、接続も確認できました

image.png

Examples

https://github.com/francisrstokes/hexnut#examples

簡単な例

server.js
const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });

app.onerror = async (err, ctx) => {
    ctx.send(`Error! ${err.message}`);
};

app.use(ctx => {
    if (ctx.isConnection) {
        // 接続時に送信
        ctx.state = { count: 0 };
        return ctx.send('Hello, and welcome to the socket!');
    }

    ctx.state.count++;
    ctx.send(`Message No. ${ctx.state.count}: ${ctx.message}`); // ctx.messageで受信したメッセージを取得できる
});

app.start();
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="button" value="send">
<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));
    document.querySelector("[type=button]").addEventListener("click", e => {
        ws.send(new Date().toLocaleTimeString());
    });
}
</script>
</body>
</html>

簡単にメッセージの送受信をすることができました

LoeU6FxC9i.gif

ctx.send(data)だと送信してきたクライアントにしかメッセージを送らないため、
接続しているクライアント全てに送るときはctx.sendToAll(data)とするようです

JSONを自動的に解析する

server.js
const Hexnut = require('hexnut');
const bodyParser = require('hexnut-bodyparser');
const app = new Hexnut({ port: 8080 });

app.use(bodyParser.json());

app.use(ctx => {
    if (ctx.isConnection) {
        ctx.state = { count: 0 };
        return ctx.send('Hello, and welcome to the socket!');
    }

    const { type, date, msg } = ctx.message;
    if (type) {
        ctx.state.count++;
        ctx.send(`Message No. ${ctx.state.count}: ${type} ${date} ${msg}`);
    } else {
        ctx.send(`Invalid message format, expecting JSON with a "type" key`);
    }
});

app.start();
npm i hexnut-bodyparser
node server.js
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="button" value="send">
<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));
    document.querySelector("[type=button]").addEventListener("click", e => {
        ws.send(JSON.stringify({
            type: "send",
            date: new Date().toLocaleTimeString(),
            msg: "\nabc\nあいう\n???",
        }));
    });
}
</script>
</body>
</html>

JSONを自動でオブジェクトに解析してくれたのが確認できました

OcRE3mIUDf.gif

hexnut-bodyparser を使わなくても、 JSON.parse(ctx.message); とすれば同じように扱ってくれます

  const Hexnut = require('hexnut');
  const bodyParser = require('hexnut-bodyparser');
  const app = new Hexnut({ port: 8080 });

- app.use(bodyParser.json());

  app.use(ctx => {
      if (ctx.isConnection) {
          ctx.state = { count: 0 };
          return ctx.send('Hello, and welcome to the socket!');
      }

-     const { type, date, msg } = ctx.message;
+     const { type, date, msg } = JSON.parse(ctx.message);
      if (type) {
          ctx.state.count++;
          ctx.send(`Message No. ${ctx.state.count}: ${type} ${date} ${msg}`);
      } else {
          ctx.send(`Invalid message format, expecting JSON with a "type" key`);
      } 
  });

  app.start();

メッセージの種類判別

server.js
const Hexnut = require('hexnut');
const handle = require('hexnut-handle');
const app = new Hexnut({ port: 8080 });

// 接続時に呼ばれるイベント
app.use(handle.connect(ctx => {
  ctx.count = 0;
}));

// msg === 'incCount' に一致する値が送られ的場合に実行される
app.use(handle.matchMessage(
  msg => msg === 'incCount',
  ctx => ctx.count++
));

// msg === 'decCount' に一致する値が送られ的場合に実行される
app.use(handle.matchMessage(
  msg => msg === 'decCount',
  ctx => ctx.count--
));

// msg === 'getCount' に一致する値が送られ的場合に実行される
app.use(handle.matchMessage(
  msg => msg === 'getCount',
  ctx => ctx.send(ctx.count)
));

// ↑に一致しなかった値が送られてきた場合にはここが実行される
app.use(handle.message(ctx => {
    ctx.send(`Any other kind of message will go here.`);
}));

app.start();
npm i hexnut-handle
node server.js
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="button" value="incCount">
<input type="button" value="decCount">
<input type="button" value="getCount">
<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));

    Array.from(document.querySelectorAll("[type=button]")).forEach(elm => {
        elm.addEventListener("click", e => ws.send(e.target.value));
    });
}
</script>
</body>
</html>

このように、送信するメッセージによって処理を分岐させることができました

masO8QdxPi.gif

Sequencing Interactions

メッセージを受信する順番を保証できる機能のようです

const Hexnut = require('hexnut');
const bodyParser = require('hexnut-bodyparser');
const sequence = require('hexnut-sequence');
const app = new Hexnut({ port: 8080 });

app.use(bodyParser.json());

// ユーザーが接続したときに発生
app.use(sequence.onConnect(function* (ctx) {
  ctx.send(`Welcome, ${ctx.ip}`);
  const name = yield sequence.getMessage(); // 接続時、次のメッセージが来るまで待つ、メッセージを受信したらイテレータを次に進める
  ctx.clientName = name;
  return;
}));

app.use(sequence.interruptible(function* (ctx) {
    // clientNameが存在すればイテレータを次に進める
    yield sequence.assert(() => 'clientName' in ctx);

    // type == greeting を受信すると、イテレータを次に進める
    const greeting = yield sequence.matchMessage(msg => msg.type === 'greeting');

    // type == time を受信すると、イテレータを次に進める
    const time = yield sequence.matchMessage(msg => msg.type === 'time');

    // ↑の条件が全てが受信されると、↓の送信の部分に処理が到達する
    return ctx
        .send(`${greeting.value} ${ctx.clientName}です、 今の時間は${time.value}です`);
}));

app.start();
npm i hexnut-sequence
node server.js
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="button" value="name" data-msg="tarou">
<input type="button" value="greeting" data-msg='{"type":"greeting","value":"おはようございます"}'>
<input type="button" value="time" data-msg='{"type":"time","value":"10:00"}'>
<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));

    Array.from(document.querySelectorAll("[type=button]")).forEach(elm => {
        elm.addEventListener("click", e => ws.send(e.target.dataset.msg));
    });}
</script>
</body>
</html>

sYeyKuFdPr.gif

Middlewareを自作

server.js
const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });

const myMiddleware = async (ctx, next) => {
    ctx.send("send myMiddleware message.");
    return await next();
}
app.use(myMiddleware);

app.use(ctx => {
    ctx.send("send message."); 
});

app.start();
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="button" value="send">
<script>
{
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));
    document.querySelector("[type=button]").addEventListener("click", e => {
        ws.send(new Date().toLocaleTimeString());
    });
}
</script>
</body>
</html>

ミドルウェアで、クライアントに送信前の処理がうまくいきました

ptYMjdxDJg.gif

HexNut Docs - API

https://github.com/francisrstokes/hexnut/blob/master/docs/api.md

引用部分は全て↑のページのgoogle翻訳です:bow:

HexNut Server

new HexNut(wsConfig)

新しいHexNutインスタンスを作成します

const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });

app.use(middleware)

HexNutインスタンスにミドルウェア機能を追加します

app.start()

HexNut Websocketサーバーを起動します。

const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });
app.start(); // サーバー起動

app.stop()

HexNut Websocketサーバーを停止します。

const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });
app.start();
app.stop(); // サーバー起動して即終了する

ctx

HexNut接続を表すコンテキストオブジェクト

ctx.message

受信したメッセージ

app.use(ctx => {
    if (ctx.isConnection) {
        // 接続時はnullになる
        console.log(ctx.message);
    } else {
        // 受信したメッセージ
        console.log(ctx.message);
    }
});

ctx.isConnection

app.use(ctx => {
    // 新しく接続されたのであればtrue、そうでなければfalse
    console.log(ctx.isConnection);
});

ctx.isMessage

app.use(ctx => {
    console.log(ctx.isMessage);
});
html
<script>
const ws = new WebSocket("ws://127.0.0.1:8080"); // 接続時は、ctx.isMessageはfalse

ws.send("hoge"); // メッセージ送信した時は、ctx.isMessageはtrue
</script>

ctx.isClosing

接続終了したらtrueになる

app.use(ctx => {
    console.log(ctx.isClosing);
});
html
<script>
const ws = new WebSocket("ws://127.0.0.1:8080"); // 接続時は、ctx.isClosingはfalse

ws.close(); // 接続終了した時は、ctx.isClosingはtrue
</script>

ctx.requestHeaders

この接続を開始したhttp(s)ヘッダを表すオブジェクト

app.use(ctx => {
    console.log(ctx.requestHeaders);
});
コンソール
{ host: '127.0.0.1:8080',
  connection: 'Upgrade',
  pragma: 'no-cache',
  'cache-control': 'no-cache',
  upgrade: 'websocket',
  origin: 'file://',
  'sec-websocket-version': '13',
  'user-agent':
   'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7',
  'sec-websocket-key': '+oE1f1UWmZ3dJ0k8Et23XQ==',
  'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits' }

ctx.ip

クライアントのIPアドレス

app.use(ctx => {
    console.log(ctx.ip); // ::ffff:127.0.0.1
});

ctx.path

接続を開始した文字列URLパス

app.use(ctx => {
    console.log(ctx.path);
});
html
<script>
    const ws = new WebSocket("ws://127.0.0.1:8080/path"); // ctx.pathは 「/path」となる
</script>

ctx.method

接続を開始するためのHTTPメソッド

app.use(ctx => {
    console.log(ctx.method);
});
html
<script>
    const ws = new WebSocket("ws://127.0.0.1:8080"); // ctx.methodは 「GET」となる
</script>

ctx.send(data)

クライアントにメッセージを送る

app.use(ctx => {
    ctx.send("send message.");
});
html
<script>
    const ws = new WebSocket("ws://127.0.0.1:8080");
    ws.addEventListener("message", e => console.log(e.data));
</script>

image.png

ctx.sendToAll(data)

接続されているすべてのクライアントにメッセージを送信する

app.use(ctx => {
    ctx.sendToAll(new Date().toLocaleTimeString());
});

接続しているクライアントにメッセージを送ることができました

54pZQA4Rx7.gif

ctx.app

HexNutアプリへの参照

const Hexnut = require('hexnut');
const app = new Hexnut({ port: 8080 });

app.use(ctx => {
    console.log(app === ctx.app); // true
});
app.start();

確認バージョン

  • node v10.14.1
  • npm 6.9.0
  • hexnut 0.1.1
  • hexnut-bodyparser 0.1.0
  • hexnut-handle 1.0.0
  • hexnut-sequence 0.1.0

最後まで読んでいただいてありがとうございましたm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む