技術メモ

プログラミングとか電子工作とか

Phoenix入門 (第6章 Endpoint)

f:id:ysmn_deus:20190219130922p:plain

どうも、靖宗です。 今回はEndpointということでPlugのところでもちょっとやったところです。
全てのリクエストはまずEndpointに渡されて、その後にルーティング処理に入ります。

そもそもPhoenixは一番最初にEndpointをSupervisorの管理下に起動するようです。
lib/hello_phoenix/application.exを確認します。

defmodule HelloPhoenix.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      # Start the Ecto repository
      HelloPhoenix.Repo,
      # Start the endpoint when the application starts
      HelloPhoenixWeb.Endpoint
      # Starts a worker by calling: HelloPhoenix.Worker.start_link(arg)
      # {HelloPhoenix.Worker, arg},
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: HelloPhoenix.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  def config_change(changed, _new, removed) do
    HelloPhoenixWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

Endpointがchildrenに入っており、Supervisor.start_link(children, opts)が実行されています。

Endpoint Contents

Phoenixのプロジェクトを開始した際に自動で生成されるlib/hello_phoenix_web/endpoint.exの中を見ていきます。

defmodule HelloPhoenixWeb.Endpoint do
  ...
end

まず最初に

use Phoenix.Endpoint, otp_app: :hello

とあります。PhoenixのEndpointとして利用するためにuseで様々な機能を呼び出しています。
otp_app: :helloは設定用に用いられると記載がありますので、おそらくconfig/config.exsに記載がある

# Configures the endpoint
config :hello_phoenix, HelloPhoenixWeb.Endpoint,
...

の箇所の設定などが適応されるのでしょう。

次はSocketの設定。

  socket "/socket", HelloPhoenixWeb.UserSocket,
    websocket: true,
    longpoll: false

RoutingのChannelの箇所でも見ましたが、websocketの設定だと思います。
詳しくはChannelsの章に回します。

次はPlug.Staticが適応されています。名前からしても静的ファイルの配信などでしょう。

  plug Plug.Static,
    at: "/",
    from: :hello_phoenix,
    gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

圧縮を適応したり配信ファイルを設定したりフィルタリング(該当する静的ファイルのリクエストに対してのみ圧縮ファイルを利用するとか?)の設定とかできそうです。

次はコードリロードが適応されてるかどうかの処理。

  if code_reloading? do
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader
  end

コードリロードはwebsocketで実装されてるんですかね。
主にホットリロードの機能は開発時のみに適応されます。ホットリロードのON/OFFはconfig/config.exsのEndpointの箇所で設定するようです。

config :hello_phoenix, HelloPhoenixWeb.Endpoint, code_reloader: true

お次はPlug.RequestIdPlug.Logger。以前にちょっとだけ言及した気はしますがPlug.RequestIdが各リクエストに対してユニークなIDを降るPlugで、Plug.Loggerはロギングでしょう。
より詳しく知るにはPlugのModulesを閲覧するのが良さそうです。今はスルーします。

次は公式ドキュメントでの言及はありませんでしたが、Plug.ParsersPlug.MethodOverridePlug.Head。これもルーティングのところで言及していますが、 Plug.Parsersがリクエストの本体をパースするPlug、 Plug.MethodOverrideがmethodのパラメータからPOSTのリクエストをPUT、PATCH、DELETEに書き換えるPlug、 Plug.HeadがHEADリクエストをGETリクエストに変換するPlugです。

  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Phoenix.json_library()

  plug Plug.MethodOverride
  plug Plug.Head

お次にPlug.Session。名前の通りCookiesなどセッション情報の処理をするPlugでしょう。

  plug Plug.Session,
    store: :cookie,
    key: "_hello_phoenix_key",
    signing_salt: "hogefuga"

最後にルーティング機能のPlugを呼び出しています。同じディレクトリにあるrouter.exが該当します。

  plug HelloPhoenixWeb.Router

もっと色々知りたい人はEndpoint API docsを読むといいよ!と書かれています。今回はスルーしますが、必要に応じて確認したいところです。

hexdocs.pm

Using SSL

SSLを使うときには設定を書き換えるのと2つの環境変数が必要なようです。
設定はconfig/prod.exsに書き、otp_app: :hello_phoenixを指定しているときは秘密鍵と証明書はpriv/ディレクトリに入れとくようです。

use Mix.Config

config :hello_phoenix, HelloPhoenixWeb.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [host: "example.com"],
  cache_static_manifest: "priv/static/cache_manifest.json",
  https: [
    port: 443,
    otp_app: :hello_phoenix,
    keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
    certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
    # OPTIONAL Key for intermediate certificates:
    cacertfile: System.get_env("INTERMEDIATE_CERTFILE_PATH")
  ]

otp_app:の値なしでの設定方法も書いてますが、ちょっとよく分からないのでスルーします。(PlugでPath.expand("../../../some/path/to/ssl/key.pem", __DIR__)が必要って書いてあるけどどのPlug?)

TLSのバージョンなど厳密に指定したい場合はversions: [:'tlsv1.2']などのキーを追加すればいいようですが、セキュリティ上望ましくないと思いますし気にしなくて良い気がします。

SSL in Development

開発時にSSLを使いたい時はmix phx.gen.certオレオレ証明書(self-signed certificate)を発行できるようです。
開発時のみでの使用が推奨されますので、config/dev.exsに記載します。

config :hello_phoenix, HelloPhoenixWeb.Endpoint,
  ...
  https: [
    port: 4001,
    cipher_suite: :strong,
    keyfile: "priv/cert/selfsigned_key.pem",
    certfile: "priv/cert/selfsigned.pem"
  ]

Force SSL

SSLのページにリダイレクトしたいときとかの設定?
Endpointのコンフィグにforce_ssl:を追記します。
:urlの設定に書いてねと言われてますのでconfig/prod.exsとかでしょうか。若しくは開発時に試すためにconfig/dev.exs

config :hello_phoenix, HelloPhoenixWeb.Endpoint,
  force_ssl: [rewrite_on: [:x_forwarded_proto]]

ダイナミックにリダイレクト(たぶんhttp://hoge.com/api/fugahttps://hoge.com/api/fugaとか?)したい場合はhost: nilを設定するようです。

config :my_app, MyApp.Endpoint,
  force_ssl: [rewrite_on: [:x_forwarded_proto], host: nil]

HSTS

HTTP Strict Transport Securityを適応したい場合はforce_ssl: :hstsと書けば適応されるそうです。
ただし、ローカル環境での開発だとなんかしらんけど動かん、とかあるみたい(80への通信を強制的に443にリダイレクトするあたりが悪さする?)なんで、ブラウザごとで色々対策してねとあります。この辺を鑑みると開発用サーバーにデプロイするなど、HSTSを使う際にはあまりローカルで開発しない方がいいかもしれません。

SSLあたりはまだまだ勉強不足なので実際使ってみてまた何かあれば補足情報など記載したいと思います。