UnityでHTTPリクエストを処理してみる

Unityのゲームを動かしている間に外からHTTPでメッセージを送って制御したりするのをやりたかったので、ゲーム内でHTTPリクエストを処理する方法を調べました。

.NETに標準で搭載されている`HttpListener`を使うことでHTTPリクエストを受け付ける処理を書くことができます。最近のUnityではスクリプトランタイムを.NET 4.6環境に設定することができて`async/await`を使うこともできるので、より簡潔な方法で記述することができるようになりました。

本記事ではさらにUnityのイベントシステムと組み合わせることで、クリックイベントなどと同じような感覚でHTTPリクエストを処理できるようなしくみを作ってみました。

## HTTPサーバーのコンポーネントを作る

まず、以下のようなスクリプトを書きます。

```csharp:HttpServer.cs
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[System.Serializable]
public class OnRequestEvent : UnityEvent<HttpListenerContext> { }

public class HttpServer : MonoBehaviour
{
  private HttpListener httpListener = new HttpListener();

  // エディター上から設定する
  public int port = 8080;
  public string path = "/";
  public bool startOnAwake = true;

  // リクエスト処理するイベントハンドラー。エディター上から設定する。
  public OnRequestEvent OnRequest;

  void Start()
  {
    httpListener.Prefixes.Add("http://*:" + port + path); // http://*:8080/ のようになる

    // コンポーネントの開始時に自動起動
    if (startOnAwake)
    {
      StartServer();
    }
  }

  // リクエストの受け付けを開始する
  public async Task StartServer()
  {
    httpListener.Start();

    while (true)
    {
      // リモートからの接続を待機
      var context = await httpListener.GetContextAsync();

      // 着信ログを表示
      Debug.Log("Request path: " + context.Request.RawUrl);

      OnRequest.Invoke(context);
    }
  }

  // サーバーを停止する
  public void StopServer()
  {
    httpListener.Stop();
  }

  // 破棄時にサーバーを止める
  void OnDestroy()
  {
    StopServer();
  }
}
```

そして、シーンに空のゲームオブジェクトを作成して、上記スクリプトをアタッチします。

<img width="330" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/56668/25f4d321-4d75-ac1b-8688-d4a3429f50f2.png">

こんな感じになると思います。

## HTTPリクエストを処理する

もう一つ空のゲームオブジェクトを作り、以下のようなスクリプトを書きます。

```csharp:MyRequestHandler.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net;

public class MyRequestHandler : MonoBehaviour
{
  public string message = "Hello from Unity!";

  public void OnRequest(HttpListenerContext context)
  {
    // message の内容をバイト配列に変換してレスポンスを返す
    var data = System.Text.Encoding.UTF8.GetBytes(message);
    context.Response.StatusCode = 200;
    context.Response.Close(data, false);
  }
}
```

## スクリプト同士をくっつける

最後に、HTTPサーバーのスクリプトとHTTPリクエストを処理するスクリプトをくっつけます。HTTPサーバーのゲームオブジェクトを選択してインスペクターのOn Request (HttpListenerContext)の部分の右下の+アイコンを選択して項目を一つ増やします。
<img width="322" alt="2.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/56668/c5f6d457-bb2a-a993-fb05-ed7e4811f556.png">

None (Object)の右の丸いポチを選択して、先程HTTPリクエストを処理するスクリプトを設定したゲームオブジェクトを選択します。
<img width="146" alt="3.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/56668/6127e9f7-a979-efed-8573-4f70c78886a6.png">

No FunctionのドロップダウンからMyRequestHandler→OnRequestの順に辿ります。
<img width="349" alt="4.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/56668/e4e812a4-b7f8-9a24-290f-8ecfcabe6a90.png">

こんな感じになったらOKです。
<img width="323" alt="5.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/56668/a7c75a8d-a5e3-3b51-dc0a-4c7bd565e773.png">

## 動かしてみる

さてここまでできたら、ゲームを動かしてみましょう。ブラウザを開いて[http://localhost:8080/](http://localhost:8080/)にアクセスしてみてください。その後**Unityエディターにフォーカスを戻さないとスクリプトが動かないのでHTTPレスポンスが返らない**ので注意してください。
ブラウザに`Hello from Unity!`という文字が出ていれば成功です。