2020-04-17

検出不可能なゲームのチートが発表、今後のオンラインゲームのデザインはこのチートを前提に設計しなければならない

PCILeechというソフトウェアスタックがある。

ufrisk/pcileech: Direct Memory Access (DMA) Attack Software

これはPICeデバイスを使ってターゲットコンピューターのメモリをDMAで読み取るためのPCIeデバイスのFPGAの実装とその操作のための一連のソフトウェア群だ。

原理は簡単だ。ターゲットコンピューターのPCIeスロットにつないだPCIeデバイスからDMAをするだけ。これによりターゲットコンピューターのメモリ領域を読み取ることができる上、ターゲットコンピューターからは検出不可能だ。なぜならばDMAはCPUを介さずに行われる上、ターゲットコンピューターにカーネルスペース/ユーザースペースともに追加のコード実行を必要としないからだ。

PCILeechを作って作られたゲームのチートに、CSL:GOのチートがある。これはマップ上のどこにプレイヤーがいるかを表示するチートだ。

pcileech-webradar/readme.md at master · EngineOwningSoftware/pcileech-webradar

原理上、このチートはターゲットコンピューターからは検出不可能だ。したがっていかなるDRMであろうとも役に立たない。難読化をして多少チートの開発を嫌がらせすることはできるかもしれないが、所詮その程度だ。もはやゲームにおいてメモリは常に読まれるものと考えなければならない。

このチートに抗うのは無意味だ。したがってこれからのオンラインゲームはローカル環境でのメモリが読まれることを前提に設計しなければならない。CS:GOのようなオンラインFPSゲームでは、全プレイヤーの位置は公開すべきだろう。すなわちこのようなチートの意味をなくすのだ。オンラインポーカーのようなゲームでは、ローカルに秘密情報を保持しない実装にしなければならない。秘密情報については中央の権威サーバーのみが処理するか、メンタルポーカーのアルゴリズムを使う。オンラインFPSも中央の権威サーバーによる実装もできるが、それはGoogle Stadiaのような入力をサーバーに送信して映像をストリーミング再生するだけのような実装になるだろう。

この仕組みを使ってメモリに書き込む場合は検出可能だろう。というのも連続していない任意箇所のメモリに対してアトミックに書き込むことはできないので、ゲーム状態を重複して複数のメモリ箇所に保持したり常時チェックサム計算などの対策で、メモリ改変を検出できるはずだ。

また、自分のプログラムが全メモリアクセスを専有している場合は、メモリ帯域を常時使い切ることにより、メモリ帯域に変化が生じればそれはDMAアクセスが行われたと考えていいだろう。問題はPCILeech以外の各種デバイスかもしれず、またIntel MEやAMD PSPのような邪悪で不自由なバイナリブロブのファームウェアによるものかもしれないので、切り分けができない。そして全メモリアクセスを専有する状態ではもはやPCゲームは実装できない。

世の中から無意味で邪悪で人道上の罪であるDRMは滅ぼさなければならない。

2020-04-16

Error handling in Erlang

Last time, I wrote about how to write a hello world HTTP server by using Erlang, Cowboy and Rebar3.

Walkthrough of hello world HTTP server with Erlang, Cowboy, and Rebar3

Today, I'm going to write about the error handling in Erlang.

The error handling in Erlang is easy if we can ignore the error. That is, we don't expect the error. In the event of unlikely failure, we immediately abandon any computation we were doing and start over. The OTP and other famous frameworks were written by following Erlang culture of error handling so it will handle it well. If, on the ohter hand, the errors are expected to happen, so we have to explicitly deal with it. Erlang is suddenly reared its ugly head.

How a function notify it's caller the failure? In other languages, like Haskell, there is a Maybe type which may or may not contains the value. Erlang is not staticaly strong typed language so the Erlang version of the Maybe is tagged tuple. We return ok or {ok, State} upon success and we return any other value otherwise. It may be {error, Reason}, empty list [], or simply an atom like error, false, undefined whatever.

The user of such functions must match the returned value with ok tagged tuple,

do_something( State ) ->
    {ok, State_1} = f( State ),
    do_normal_things.

If the function f return anything other than {ok, any()}, match failed and throw an exception of error:{badmatch, V}. So hopefully, the higher framework catch these exceptions and restart the process.

But what if the caller want to deal with the error? We have to use the patter match for the conditional execution. It can be done by case expression or function.

case expression:

do_something( State ) ->
    case f( State ) of
        { ok, State_1 } -> do_normal_things ;
        { error, Reason } -> do_error_handling
    end.

function :

do_something( State ) ->
    do_something_1( f( State ) ) .

do_something_1( { ok, State } ) -> do_normal_things ;
do_something_1( {error, Reason} ) -> do_error_handling .

Whichever you use, it became quite verbose and boilar-plate.

Erlang has exception like many other langauges. But I feel some oddities on Erlang's exception and that is the concept of class.

The Erlang has three class of exception: error, exit, and throw. These are thrown by calling function error/1,2, exit/1, and throw/1 respectively.

If you don't care about exception, you need to do nothing. But if you care, that is, you want to run some code on the condition of exception, things get verbose.

Let's suppose that previous function f/1 return { ok, State } on success, but throw some exceptions otherwise and you want to deal with it because you expect it to happen. You can use try expression or catch expression

try expression is strightforward, if you ignore the awkward Erlang grammer that is.

try Exprs
    catch Class1:Pattern1 -> Body1 ;
    catch Class2:Pattern2 -> Body2
end

The class is either error, exit, or throw, pattern may vary. If you were to catch the exception thrown by error class's badmatch(1 = 2), it looks like this.

try 1 = 2
    catch error:{ badmatch, V } -> its_bad_man
end

Now, how to do_normal_thing and do_error_handling depending on the presense of exception? try expression can have of section and it will be evaluated only on no exception in Exprs.

try f( State ) of
    { ok, State_1 } -> do_normal_thing ;
catch
    throw:something_went_bad -> do_error_handling
end

Now how to deal with the situation where the error will be reported in either by value or exception? Use try expression's of section to pattern match the error value.

try f( State ) of
    { ok, State_1 } -> do_normal_thing ;
    { error, Reason } -> do_error_handling
catch
    throw:something_went_bad -> do_error_handling
end

There is another way to deal with the exception. The catch expression.

catch Exprs

catch expression evaluate Exprs and return its value on no exception. In case of exception, the value will be either

For exceptions of class error, that is, run-time errors, {'EXIT',{Reason,Stack}} is returned.

For exceptions of class exit, that is, the code called exit(Term), {'EXIT',Term} is returned.

For exceptions of class throw, that is the code called throw(Term), Term is returned.

Erlang -- Expressions

If it's by throw({error, Reason}), the code will be clean.

case catch Exprs of
    { ok, Value } -> do_normal_thing ;
    { error, Reason } -> do_error_handling
end

But if it's error class, the code is too ulgy to read.

case catch 1 = 2 of
    { 'EXIT', { {badmatch, _} }, _ } -> do_error_handling
end

Perhaps, error and exit class were not meant to be handled by catch expression, but some library use these even for the predictable situations. like list_to_integer, binary_to_integer. My guess is to keep the backward compatibility.

Putting it all togather, it's very verbose to handle errors in Erlang.

Let's quickly borrow a code from the hello world HTTP server I explained in the previous article. Walkthrough of hello world HTTP server with Erlang, Cowboy, and Rebar3

Instead of returning the hello world, we're going to return the sum of two query parameter a, b.

$ curl "http://localhost:8080/?a=1&b=2"
3
$ curl "http://localhost:8080/?a=1&b=-100"
-99

All we need to do is modify the cowboy_handler. The simplest code that assume no error will be like this.

init( Req, State ) ->
    P = cowboy_req:parse_qs(Req),
    { _, A } = lists:keyfind(<<"a">>, 1, P ),
    { _, B } = lists:keyfind(<<"b">>, 1, P ),
    Sum = integer_to_binary( binary_to_integer(A) + binary_to_integer(B) ),
    Req_1 = cowboy_req:reply( 200,
        #{<<"content-type">> => <<"text/plain">>},
        Sum, Req ),
    {ok, Req_1, State ).

Well, it's not bad. But I want to deal the the error.

Suppose, the users forget the query parameters.


$ curl "http://localhost:8080/"
$ curl "http://localhost:8080/?a=1"
$ curl "http://localhost:8080/?b=1"

If this happend, our code failed the pattern match because lists:keyfind returns false.


{ _, A } = false,

In such cases, I want to reply with the helpful error messages like this.


$ curl "http://localhost:8080/"
Error: missing query parameter a, b.
$ curl "http://localhost:8080/?a=1"
Error: missing query parameter b.
$ curl "http://localhost:8080/?b=1"
Error: missing query parameter a.

We can do condional branching with either case expression or function pattern match.

Another type of error is although the query paramters are present, it has a string that cannot be parsed as an integer.


$ curl "http://localhost:8080/?a=abc&b=123"

I would like to reply with helpful error messages in this case too.

After consdering the various code, I come up with this code. It's too verbose and ugly but I think alternatives are worse.

init( Req, State ) ->
    P = cowboy_req:parse_qs(Req),
    A = lists:keyfind( <<"a">>, 1, P ),
    B = lists:keyfind( <<"b">, 1, P ),
    { Http_status_code, Answer } = process( A, B ),

    Req_1 = cowboy_req:reply( Http_status_code,
        #{&lt;&lt;"content-type"&gt;&gt; =&gt; &lt;&lt;"text/plain"&gt;&gt;},
        Answer, Req ),
    { ok, Req_1, State }.

process/2 is set of function that ultimately returns { integer(), iodata() }. Here is the verbose code.

%% for missing query parameters.
process( false, false )     -> { 400, <<"Error: missing query parameter a, b.\n">> } ;
process( false, _ )         -> { 400, <<"Error: missing query parameter a.\n">> } ;
process( _, false )         -> { 400, <<"Error: missing query parameter b.\n">> } ;
%% for invalid query parameters
process( badarg, bardarg)   -> { 400, <<"Error: invalid query parameter a, b.\n">> } ;
process( badarg, _ )        -> { 400, <<"Error: invalid query parameter a.\n">> } ;
process( _, bardarg)        -> { 400, <<"Error: invalid query parameter b.\n">> } ;
% lists:keyfind succeeded.
process( { _, A }, { _, B } ) ->
    process(
        try binary_to_integer( A ) catch error:badarg -> badarg end,
        try binary_to_integer( B ) catch error:badarg -> badarg end
    ) ;
% no invalid query parameter. return the result.
process( A, B ) ->
    { 200, { integer_to_binary( A + B ), <<"\n">> } } .

The -spec attribute for this process/2 is abomination.

-spec process(
    { bitstring(), bitstring() } | false | badarg | integer(),
    { bitstring(), bitstring() } | false | badarg | integer()
)  -> { integer(), iodata() }.

Well, at least, I understand the error handling of Erlang.

2020-04-13

Erlang, Cowboy, Rebar3によるHello World HTTPサーバーのチュートリアル

本記事では、Erlang, Cowboy, Rebar3によるHello worldを出力するHTTPサーバーの実装方法を解説する。

目的は、このプログラムを実行中に、以下のような挙動になることだ。

$ curl "htttp://localhost:8080/"
hello,world

ErlangもCowboyもRebar3も、情報が極めて少ない。しかも公式ドキュメントすら間違っている。公式ドキュメントすら間違っているのには理由があり、実際の実装とドキュメントに差がでているのだ。Erlangは変化の少ない言語ではあるが、それでもOTP17からmapが追加されたりと言語的に少し変わっているし、mapの追加により、cowboyも以前ならproplistsを使っていた部分でmapを使うようになり、しかも2.0でAPIかなり破壊的な変更が入り、2.5からはモジュールの構成も少し変わってしまった。Rebar3は本当にひどい。名前が示すように、すでに破壊的な変更を2回経ている。そして技術的負債の塊だ。

この記事で解説する程度の知識を得るのに私は公式ドキュメントと何ヶ月も格闘するはめになった。公式ドキュメントですらこうなのだから、非公式なドキュメントは本当に参考にならない。ここに書いてあることは2020年の時点では正しいが、来年はわからない。

準備

Erlang実装とRebar3が必要だ。CowboyについてはRebar3がダウンロードしてくれるので考えなくてよい。

Debian系のディストロでErlangをインストールする。

$ apt install erlang

GNU/Linuxでrebar3をインストールする最も信頼できる方法は、自前でビルドすることだろう。

$ git clone https://github.com/erlang/rebar3.git
$ cd rebar3
$ ./bootstrap

これで"rebar3"というファイルができるので、このファイルをPATHの通ったディレクトリにコピーするかシンボリックリンクをはればよい。

ln -s rebar3 /usr/local/bin

これで準備が完了した。

プロジェクト作成

まずrebar3を使ってプロジェクトを作成する。名前を"hello_server"としよう。

$ rebar3 new release hello_server
$ cd hello_server

このコマンドで"hello_server"というディレクトリが作成される。その中にはテンプレート生成されたファイルがいくつかある。重要なものだけ説明する。

"hello_server/rebar.config"は設定ファイルで、cowboyの依存を追加するために編集する。

"hello_server/apps"ディレクトリにはアプリケーションが配置される。rebar3のreleaseプロジェクトはumbrella projectと呼ばれていて、複数のアプリケーションを持つことができる。

"hello_server/apps/hello_server"はrebar3がテンプレートから生成したアプリケーションだ。このディレクトリ内には"src"ディレクトリがあり、3つのファイルが作成されている。"hello_server_app.erl", "hello_server_sup.erl", "hello_server.app.src"だ。

"hello_server_app.erl"はapplication behaviourを実装するソースファイルだ。

"hello_server_sup.erl"はsupervisor behaviourを実装する。今回は編集しない。

"hello_server.app.src"はapplication resource fileを生成するためのソースファイルだ。Erlang VMをどのように実行するかということを設定するためのファイルだ。rebar3はこのファイルから実際のapplication resource fileを生成する。このファイルも編集する。

Cowboyを依存に追加

Cowboyを依存に追加してrebar3にダウンロードしてもらう。そのために"hello_server/rebar.config"を編集する。

$ vim rebar.config

最初の数行は以下のようになっている。

[erl_opts, [debug_info]}.
{deps, []}.

[relx, [{release, {hello_server, "0.1.0"},
...

今回編集するのは2行目、つまり"{deps,[]}."という部分だ。このlistのなかに依存を記述していく。記述のフォーマットは様々だが、すべて"{ package_name, ...}"という形のtupleになっている。このチュートリアルではパッケージをhex.pmからダウンロードしてくるので、フォーマットは"{ package_name, "version number"}"になる。本チュートリアルを執筆時点で、最新の安定版のcowboyのバージョンは2.7.0だ。


{deps, [
    {cowboy, "2.7.0"}
]}.

rebar3は依存ライブラリが必要になった時に自動的にダウンロードするが、今回は正しく記述できていることを確認するために明示的にダウンロードしてみよう。

$ rebar3 upgrade

次に、アプリケーションリソースファイルを編集して、cowboyを先にスタートさせるようにする。今実装しているアプリケーションはcowboyを使っているので、cowboyを先にスタートさせておかなければならない。その設定方法として、"hello_server.app.src"を編集する。

$ vim apps/hello_server/src/hello_server.app.src

このファイルの中身を抜粋すると以下のようになっている。


{application, hello_server,
  [...
  {applications
    [kernel,
     stdlib
    ]},
  ...
  ]}.

applicationsのtagged tupleの中のlistに"cowboy"を追加する。

{application, hello_server,
  [...
  {applications
    [kernel,
     stdlib,    % カンマを忘れないこと
     cowboy     % この行を追加
    ]},
  ...
  ]}.

これはErlangのlistなのでカンマを忘れないようにすること。

HTTPサーバーの始動

容易が全て整ったので、HTTPサーバーを開始する。まず"apps/hello_server/src/hello_server_app.erl"を編集する。

vim apps/hello_server/src/hello_server_app.erl

このソースコードはrebar3によって生成されたapplication behaviourを実装するためのモジュールだ。start/2を変更して、HTTPサーバーを開始する。

start(_StartType, _StartArgs) ->
    hello_server_sup:start_link().

HTTPサーバーを開始してコネクションをlistenするには、まずcowboy用語でルートと呼ばれているものを設定する。これは特定のリモートホストやパスをcowboy_handlerに関連付けるための設定だ。ルートを設定するにはcowboy_router:compile/1を使う。この関数は引数としてcowoy_router:routes()型を取る。型は"[{Host, Pathlist}]"となっている。PathList型を展開すると、"[{Host, [{Path, Handler, InitialState}]}"となる。

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { Host, [{Path, Handler, InitialState}]}
    ]),
    hello_server_sup:start_link().

ホストとして'_'を指定すると、任意のホストからのコネクションを受けつける。ホストを制限したい場合、例えばlocalhostからの接続しか受け付けたくない場合は、<<"localhost">>を指定する。

今回の場合、Pathは<<"/">>だ。今回は"http://localhost/aa/bb/cc"のようなPathは受け付けないのでこれでいい。

Handlerにはhello_handlerというatomを指定する。これは後でcowboy_handler behaviourを実装するモジュールとして実装する。

特に状態は持たないので、InitialStateは空のlistを使う。

すべてまとめると、以下のようなコードになる。

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { <<"localhost">>, [{<<"/">>, hello_handler, [] }]
    ]),
    hello_server_sup:start_link().

ルートが準備できたので、HTTPリスナーを開始する。ここでは素のHTTPを使うので、cowboy:start_cear/3を使う。引数はstart_claer( Name, TransportOpts, ProtocolOpts )だ。

NameはこのHTTPリスナーを識別するための名前で、ErlangのTermであればなんでもよい。通常はatomが使われる。ここでは"hello_listener"を使う。

TransportOptsには様々なオプションがあるが、このチュートリアルではlistenするポートを指定するだけだ。今回はHTTPサーバーのポートはは通常80だが、今回は8080を使うので、"{{port, 8080}}"となる。

ProtocolOptsでは先程設定したrouteを指定する。ProtocolOptsの型はmapで、envというキーがあり、値はdispatch型だ。ここに先程束縛したDispatch変数を指定する。

成功した場合、start_clear/2は{ok, pid}を返す。okのtagged tupleに束縛することで失敗時のことはapplication behaviourにまかせよう。

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { <<"localhost">>, [{<<"/">>, hello_handler, []}] }
    ]),
    {ok, _} = cowboy:start_clear(
        ello_listener,
        [{port, 8080}],
        #{env => #{dispatch => Dispatch}}
    ),
    hello_server_sup:start_link().

接続の処理

HTTP listenerの用意が出来たので、やってきた接続要求を処理していく。そのためにはさきほどのhello_handlerを実装しなければならない。これはcowboy_handler behaviourとして実装する。まず新しいソースファイルを作成する。

$ vim apps/hello_server/src/hello_handler.erl

まず基本的なところを埋めていこう。

-module(hello_handler).
-behaviour(cowboy_handler).
-export([init/2]).

init( Req, State ) ->
    {ok, Req, State}.

Reqはリクエストとレスポンスを保持している。Stateは好きに使える状態だが今回は使わないので空のlistとする。

やるべきことは、HTTPステータスコードとして200を返し、ヘッダーのcontent-typeとしてはtext/plainを指定し、コンテンツは"hello,world"とするだけだ。これにはcowboy_req:reply/4を使う。引数の型は"reply(Status, Headers, Body, Req)"だ。

StatusはHTTPステータスコードで型はnon_reg_integer()もしくはbinary()だ。今回は200を指定する。

HeaderはHTTP headerをmap()で指定する。今回は"content-type"を"text/plain"にする。

Bodyには<<"hello,world">>を指定する。

Reqは現在のReqオブジェクトだ。

replyは新しいReqオブジェクトを返す。これ以降Reqオブジェクトを使う際には、この新しいReqオブジェクトを使わなければならない。

init/2のコードは以下のようになる。

init( Req, State ) ->
    Req_1 = cowboy_req:reply(
        200,
        #{<<"content-type">> => <<"text/plain">>},
        <<"hello,world">>,
        Req
    ),
    {ok, Req, State}.

プログラムの実行

$ rebar shell

確認しよう。

$ curl "http://localhost:8080/"
hello,world

次はErlangのエラー処理について書こうと思う。Erlangのエラー処理はその場で処理を中断して無視していい場合は簡単だが、エラーに明示的な対処が必要だととたんに面倒になる。

2020-04-07

Walkthrough of hello world HTTP server with Erlang, Cowboy, and Rebar3

This is the quick walkthrough of implementing and executing a HTTP server that just return hello world by using Erlang, Cowboy and rebar3.

The goal is, while executing this problem, the following happens.

$ curl "htttp://localhost:8080/"
hello,world

Erlang and Cowboy is easier compared to the god-forsaken rebar3. Rebar3, as its name indicates, had major backward incompatible breaking changes twice, and it still has many technical debt piled up on it. Even the official documentation is unhelpful at this time. I wasated months just figuring out the basics and I write it down here so you don't have to.

Prerequisites

You need Erlang implementation, rebar3. The cowboy can be fetched by rebar3.

To install erlang on debian-based distro:

$ apt install erlang

To install rebar3 in most GNU/Linux distros, you should built it yourself.

$ git clone https://github.com/erlang/rebar3.git
$ cd rebar3
$ ./bootstrap

This create a file "rebar3". You can copy this file to somewhere in the PATH or symlink it.

ln -s rebar3 /usr/local/bin

Now you're ready to start using the rebar3.

Creating a new project.

First, you must create a new rebar3 project. Let's call it "hello_server".

$ rebar3 new release hello_server
$ cd hello_server

This command create a directory "hello_server" and bunch of template files in that directory. I don't explain everything in details, but just for the important bits.

"hello_server/rebar.config" is a config file we have to modify to add cowboy as an dependency.

"hello_server/apps" directory contains apps. rebar3's release project is called "umbrella project", it can contains multiple "apps".

"hello_server/apps/hello_server" is the default apps the rebar3 generated from the template. Inside that directory, there is a "src" directory which contains three template files. "hello_server_app.erl", "hello_server_sup.erl" and "hello_server.app.src".

"hello_server_app.erl" is a source file we modify.

"hello_server_sup.erl" is for implementing supervisor behaviour. We don't modify this file in this walkthrough.

"hello_server.app.src" is a source file for application resource file. It tell the Erlang VM how to start the application. Rebar3 just copy it to the appropriate place so you don't have to. We modify this file too.

Adding cowboy as an dependency

Next, we need to add cowboy as an dependency so the rebar3 can fetch it. To do so, open the "hello_server/rebar.config".

$ vim rebar.config

The first few lines are like these.

[erl_opts, [debug_info]}.
{deps, []}.

[relx, [{release, {hello_server, "0.1.0"},
...

We need to modify the second line, that is "{deps, []}.". You can add dependencies in the list. There are many formats for that but every thing is tuple of "{ package_name, ... }". In this walkthrough, we fetch the package from hex.pm. So the format shall be "{ package_name, "version number"}". As of this writing, the latest stable version of cowboy is 2.7.0.


{deps, [
    {cowboy, "2.7.0"}
]}.

rebar3 fetch the necessary dependency automatically when it is needed, but let's just fetch it explicitly to make sure we wrote it correctly.

$ rebar3 upgrade

Also, we need to modify the application resource file to start cowboy before our application. Since our application requires cowboy, the cowboy application must be started before our application. To do so, modify the "hello_server.app.src"

$ vim apps/hello_server/src/hello_server.app.src

The part of the content of this file should looks like this.


{application, hello_server,
  [...
  {applications
    [kernel,
     stdlib
    ]},
  ...
  ]}.

We add "cowboy" to the list.

{application, hello_server,
  [...
  {applications
    [kernel,
     stdlib,    % don't forget comma
     cowboy     % add this line
    ]},
  ...
  ]}.

As you see, this is Erlang's list so don't forget the comma before cowboy.

Fire up the HTTP server

Now we're going to start the HTTP server. First, we modify the "apps/hello_server/src/hello_server_app.erl".

vim apps/hello_server/src/hello_server_app.erl

This source code is generated by rebar3 to implement the application behaviour. We are going to modify the start/2 to fire up the HTTP server.

start(_StartType, _StartArgs) ->
    hello_server_sup:start_link().

In order to start the HTTP server listening the incoming connection, we first need to set what cowboy call it "route". It's a mapping of the connection from some remote hosts, path to cowboy_handler. To do that, we use cowboy_router:compile/1 which take a parameter of type cowboy_router:routes(). The type is "[{Host, PathList}]", if you expand the PathList type, it'll be "[Host [{Path, Handler, InitialState}]]".

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { Host, [{Path, Handler, InitialState}]
    ]),
    hello_server_sup:start_link().

The Host can be '_' which means we allow the connections from any hosts. If you are to allow connection from anywhere, use '_', if on the other hand, you want to restrict the access only from, say, localhost, it would be <<"localhost">>.

The Path in our case is <<"/">>. Since we don't support path like "http://localhost/aa/bb/cc".

For Handler, we specify "hello_hander" atom which we have to implement it as a cowboy_handler behaviour later.

We don't use state so the IinitialState be emply list.

Putting all togather so far, the code looks like this.

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { <<"localhost">>, [{<<"/">>, hello_handler, [] }]
    ]),
    hello_server_sup:start_link().

Now we prepared the route, we're going to fire up the HTTP listener. We are going to use good old plaintext HTTP by cowboy:start_clear/3. The three parameters are "start_clear(Name, TransportOpts, ProtocolOpts)".

Name can be any Erlang term to refer this listener, but atom used used most of the time. Let's use "hello_listener".

TransportOpts has many options, but for this walkthrough, we only need to set the port to listen to. We going to listen port 8080 so it would be "[{port, 8080}]".

In ProtocolOpts, we use the route we made earler. The type of ProtocolOpts is a map which as a key env whose value is also a map which has a key dispatch. We pass Dispatch for the value of this key.

If succeeded, start_claer/2 return "{ok, pid}". Let's make sure it returns ok and fail otherwise.

start(_StartType, _StartArgs) ->
    Dispatch = cowboy_router:compile([
        { <<"localhost">>, [{<<"/">>, hello_handler, []}] }
    ]),
    {ok, _} = cowboy:start_clear(
        ello_listener,
        [{port, 8080}],
        #{env => #{dispatch => Dispatch}}
    ),
    hello_server_sup:start_link().

Handling the incoming connections.

Now the HTTP listners are up and running, we need to implement the hander for the incoming connections. For that, we need to implement the hello_handler we specified earlier by following the cowboy_hander behaviour. Create a new source file .

$ vim apps/hello_server/src/hello_handler.erl

Let's write the basics.

-module(hello_handler).
-behaviour(cowboy_handler).
-export([init/2]).

init( Req, State ) ->
    {ok, Req, State}.

Req represents requests and response. We don't use State really. It's just an empty list.

All we need to do is return a HTTP status code 200, the content-type is text/plain, it's content is "hello,world". We can do that by cowboy_req:reply/4. The parameters are "reply(Status, Headers, Body, Req)".

Status is a HTTP status code in non_neg_integer() or binary(). In this case, it's 200.

Header is a map() to specify HTTP header. we set it's "content-type" to be "text/plain".

For the Body, we return "hello,world".

Req is a current Req object.

reply return a new Req object which we must use it instead of old Req objects after the call.

And now, the init/2 code.

init( Req, State ) ->
    Req_1 = cowboy_req:reply(
        200,
        #{<<"content-type">> => <<"text/plain">>},
        <<"hello,world">>,
        Req
    ),
    {ok, Req, State}.

To run the program

$ rebar shell

Now to confirm it.

$ curl "http://localhost:8080/"
hello,world

If I have in the mood, I'll write the error handling next. The reality is more verbose than textbook.

2020-04-04

2019-2020シーズンに行ったスキー場の感想

2019-2020シーズンは腕も上達したし雪が少なかったこともあって普段は行かない様々なスキー場に行った。ここにその感想を簡潔にまとめておこうと思う。

まずは白馬にあるスキー場からだ。2月上旬というハイシーズンに行ったはずなのに、あいにくとほとんどの日は雪質がわるかった。

栂池高原スキー場は白馬にあるスキー場だ。白馬駅からバスで行ける。特徴はゴンドラでふもとから山頂まで一気に上がって長距離を滑ることができる点だ。とはいってもあいにくの暖冬でふもとは雪質が悪かったので、ゴンドラの中間駅まで滑ってゴンドラを往復していた。

白馬コルチナスキー場は白馬にあるスキー場だ。朝早くの行きは白馬駅から電車とバスで、帰りはバスになる。白馬はパウダーキチガイのオーストラリア人で占拠されているので、開き直ってコース外のほとんどを自己責任で滑走可能なエリアとして解放してしまったスキー場だ。コースのどこからコース外に出てもふもとの同じ場所に帰ってくることができるので、コース外を滑るには適しているスキー場と言えるだろう。白馬コルチナスキー上の横には白馬乗鞍温泉スキー場があり、共通リフト券が買えるのだが白馬コルチナスキー場からは接続が悪かったので買う価値はわからなかった。

白馬五竜とHAKUBA 47は2つのスキー場が併設されているがリフト券も共通なので実質一つのスキー場だ。白馬五竜のほうはなだらかで広いゲレンデがあり雪質の悪さと相まって面白さがわからなかったが、HAKUBA 47はなかなか面白かった。

次に赤倉温泉のスキー場だ。周辺にはもっとスキー場があるのだが、今回は2つしかいかなかった。この暖冬の3月上旬にもかかわらず雪が豊富にあった。もっとも、例年はもっと雪が多いらしいのだが。

赤倉温泉スキー場は赤倉温泉の宿が密集する場所の近くにある。スキー場としては初心者向けでほとんどなだらかなコースしかない。一箇所だけ急なコースがあるのだが、そこに上がるリフトが今にも壊れそうでとても怖い。あまりにも簡単すぎるスキー場だったので、初日の足慣らしや最終日の疲れた足でのんびり滑る分にはいいが、連日でいく価値は見いだせなかった。

赤倉観光リゾートスキー場は赤倉温泉スキー場のとなりにあり、相互に連絡できるが、リフト券は別だ。こちらはとても面白いスキー場で難しいコースもあり、連日行く価値があった。ゴンドラで上がって4km滑ることができるのもよい。

これで今シーズンは滑り納めにしようと思っていたのだが、3月24日に越後湯沢にそれなりの雪が降ったので、25日に急遽日帰りで向かった。今年のかぐらは周りのスキー場が雪不足に悩む中、標高が高くて雪があるということで大勢の客がいる上、ゲレンデまでロープウェイで上がらなければならない。しかも2019年の大型台風19号の影響で下山コースが破壊されており、帰りもロープウェイに乗らなければならない。待ち時間が数時間にもなるおそれがあり、行きたくない。ちょうどコロナウイルスが騒がれていた頃でもあったので、人が密集するロープウェイも避けたい。

そこで、神立スノーリゾートに行った。このスキー場、去年までは神立高原スキー場という名前だったはずなのだが、なぜか名前を変えている。別にリゾートと言うほどの規模のスキー場ではない気がするし、周辺にホテルが密集しているわけでもない。

スキー場自体の作りは小さいながらもよい。リフトに一本乗って2km弱ぐらいのなだらかなコースを滑ることができるし、非圧雪もあるし、きれいに形成されたコブ斜面もあり、万人の需要を満たす作りになっている。