PhoenixフレームワークにおけるHTTP通信とWebソケット通信①~Plug.connについて~

## はじめに

ElixirでのWebアプリケーション開発において出会う様々な用語や概念を深堀します。今回は、Phoenixフレームワークを使用した際のHTTP通信とWebソケット通信の違いに焦点を当てて解説していきます。

## Phoenixフレームワークにおけるクライアントとサーバー間でのデータのやり取り

Phoenixフレームワークでは、HTTP通信とWebソケット通信の両方がサポートされています。これにより、静的なウェブページの提供からリアルタイム通信を必要とするアプリケーションまで、幅広いニーズに対応できます。

### HTTP通信とWebソケット通信の特徴

#### HTTP通信
---
- 非持続的な接続: HTTPはリクエスト/レスポンスモデルに基づいており、クライアントがリクエストを送信した後にサーバーからレスポンスを受け取ると、その接続は通常終了
- ステートレス: 各HTTPリクエストは独立しており、以前のリクエストからの情報は保存されません（セッションやクッキーを使用して間接的に状態を管理する方法はある）
- リクエスト主導型: データの交換はクライアントからのリクエストによってのみ開始

#### Webソケット通信
---

- 持続的な接続: Webソケットは接続を確立した後、その接続を開いたままにしておくことで、サーバーとクライアント間でリアルタイムのデータ交換が可能
- 双方向通信: サーバーとクライアントは、接続が開いている間はいつでもお互いにデータを送信できます。これにより、リアルタイムアプリケーションの実装が容易に
- フルデュプレックス通信: サーバーとクライアントは同時にデータを送受信できるため、より効率的な通信が可能

---


## HTTP通信の実装方法

Phoenixフレームワークでは、Plug.Connを使用してHTTPリクエストの情報を取得し、レスポンスを構築します。

```elixir
defmodule MyAppWeb.MyController do
  use MyAppWeb, :controller
  import Plug.Conn, only: [get_req_header: 2]

  def my_action(conn, _params) do
    headers = conn.req_headers
    user_agent = get_req_header(conn, "user-agent")
    text(conn, "User-Agent: #{user_agent}")
  end
end


```
[参考hexdocs: Plug.Conn](https://hexdocs.pm/plug/Plug.Conn.html)

### Plugライブラリとは
---
ElixirでHTTPミドルウェアとアプリケーションを構築するための仕組みを提供するライブラリです。Plugを使用することで、開発者はリクエスト処理のパイプラインを簡単に定義し、リクエストの変換や特定の条件下でのレスポンスの返し方を制御できます。

### Plug.Connとは
---
`Plug.Conn`とは、HTTP接続（リクエストとレスポンス）を処理するための構造体とユーティリティ関数を提供するものです。
`Plug.Conn`を使用することで開発者はリクエストの受け取りからレスポンスの生成に至るまでの処理を細かく制御できます。この構造体を使うことで、認証、データのバリデーション、レスポンスのカスタマイズなど、リクエストのライフサイクルにわたってさまざまな処理を効率的かつ柔軟に、実行できるようになります。

### コード内で出てくるconnとは
---
先ほどの例で出てきた`conn`は`Plug.Conn`構造体のインスタンスです。この`conn`オブジェクトを通してHTTPリクエストの処理とHTTPレスポンスの生成が行われます。

#### connの具体的な渡され方
---
Phoenixフレームワークを使用するWebアプリケーションでは、HTTPリクエストがサーバーに届くと、Plugパイプラインを通じて処理が行われ、最終的に特定のコントローラーのアクション(関数)に`conn`が渡されます。

Plugパイプラインは`router.ex`の以下のような設定

``` elixir
 pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {HelloServerWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :fetch_current_user
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", PjCanolaServerWeb do
    pipe_through [:browser, :require_verify_header]

    live "/", PageLive
  end

```

ファイル内で定義されている`pipeline :browser do`ブロックや`pipeline :api do`ブロック内にある`plug`宣言は、Plugパイプラインの一部です。
これらのplugは、Phoenixフレームワークがリクエストを処理する際に、Plug.Conn構造体を通して順番に実行される一連のミドルウェア（または操作）を定義しています。

上記の`plug :fetch_current_user`の定義は以下。

```Elixir
  @spec fetch_current_user(Plug.Conn.t(), keyword()) :: Plug.Conn.t()
  def fetch_current_user(conn, _opts) do
    {user_token, conn} = ensure_user_token(conn)
    user = user_token && Accounts.get_user_by_session_token(user_token)
    conn = assign(conn, :current_user, user)
  end
```
リクエストに含まれるユーザートークンを使用して現在のユーザーを識別し、その情報をconnに割り当てることで、以降の処理でユーザー情報に基づいたロジックを実行できるようにしています。


## 基本の整理

基本が曖昧な初学者向けに(私w)上記以外のことも整理しました。
### パイプラインとは
---
`pipeline :browser do ... end`のように定義されるパイプラインは、その中に含まれる一連のplugによって構成されます。Phoenixにおいてパイプラインはplugパイプライのみなのでパイプライン＝Plugパイプラインです。(PhoenixフレームワークおよびPlugライブラリにおけるパイプラインは、一連のplugを通じてHTTPリクエストを処理するためのメカニズムという意味です。)
つまり、Plug.Conn構造体を通して一連のplug処理を行うことを意味します。ここでの`plug`は、リクエストを受け取り、何らかの操作を施し、次のplugへ渡す関数またはモジュールです。
それをまとめたのがパイプラインとなります。

### パイプラインの役割
---
- コンテントタイプの受け入れ
- セッションの取得と管理
- フラッシュメッセージの取得
- CSRF保護の適用
- セキュリティヘッダーの追加
- 現在のユーザーの取得




### トークンとは
---
トークンは、認証やセッション管理においてユーザーを一意に識別するために使用される文字列です。ウェブアプリケーションにおいて、トークンはユーザーがログインした際に生成され、クライアント（例えば、ブラウザのクッキー）に保存されることが多いです。その後のリクエストでは、このトークンをサーバーに送信してユーザーを認証します。


## まとめ

パイプラインを定義することで、複数のルートやコントローラー間で共通の処理を再利用できるようになります。例えば、ウェブアプリケーションの全てのページに対する共通のセキュリティポリシーを適用する場合、それをパイプライン内で定義し、必要なルートに`pipe_through`を使用して適用します。

パイプラインは、PhoenixやPlugにおいて非常に強力な概念であり、HTTPリクエストの処理を柔軟かつ効率的に構成するためのキーとなります。

## 最後に
PhoenixフレームワークはHTTP通信とWebソケット通信の二つの通信方法が混在しており、`Plug.conn`はHTTP通信なんだという理解ができるとセッション管理のあたりが徐々にわかってきました。

誤りなどありましたらご指摘お願いします！
また、thewaggleではElixirでの開発にジョインしてくださる仲間を募集しています！
ご興味ある方、お気軽にご連絡ください。