PageRenderTime 77ms CodeModel.GetById 25ms RepoModel.GetById 7ms app.codeStats 0ms

/lib/phoenix/endpoint/adapter.ex

https://gitlab.com/kidaa/phoenix
Elixir | 253 lines | 217 code | 29 blank | 7 comment | 6 complexity | f986623c1c33c6467c4bf54088c4254f MD5 | raw file
  1. defmodule Phoenix.Endpoint.Adapter do
  2. # This module contains the logic used by most functions
  3. # in Phoenix.Endpoint as well the supervisor for starting
  4. # the adapters/handlers.
  5. @moduledoc false
  6. require Logger
  7. import Supervisor.Spec
  8. @doc """
  9. Starts the endpoint supervision tree.
  10. """
  11. def start_link(otp_app, mod) do
  12. conf = config(otp_app, mod)
  13. children =
  14. config_children(mod, conf) ++
  15. pubsub_children(mod, conf) ++
  16. server_children(mod, conf) ++
  17. watcher_children(mod, conf) ++
  18. code_reloader_children(mod, conf)
  19. case Supervisor.start_link(children, strategy: :one_for_one, name: mod) do
  20. {:ok, pid} ->
  21. warmup(mod)
  22. {:ok, pid}
  23. {:error, reason} ->
  24. {:error, reason}
  25. end
  26. end
  27. defp config_children(mod, conf) do
  28. app = conf[:otp_app]
  29. args = [app, mod, defaults(app, mod), [name: Module.concat(mod, Config)]]
  30. [worker(Phoenix.Config, args)]
  31. end
  32. defp pubsub_children(mod, conf) do
  33. pub_conf = conf[:pubsub]
  34. if adapter = pub_conf[:adapter] do
  35. [supervisor(adapter, [mod.__pubsub_server__(), pub_conf])]
  36. else
  37. []
  38. end
  39. end
  40. defp server_children(mod, conf) do
  41. args = [conf[:otp_app], mod, [name: Module.concat(mod, Server)]]
  42. [supervisor(Phoenix.Endpoint.Server, args)]
  43. end
  44. defp watcher_children(_mod, conf) do
  45. if conf[:server] do
  46. Enum.map(conf[:watchers], fn {cmd, args} ->
  47. worker(Phoenix.Endpoint.Watcher, [root!(conf), cmd, args],
  48. id: {cmd, args}, restart: :transient)
  49. end)
  50. else
  51. []
  52. end
  53. end
  54. defp code_reloader_children(mod, conf) do
  55. if conf[:code_reloader] do
  56. args = [conf[:otp_app], root!(conf), conf[:reloadable_paths],
  57. [name: Module.concat(mod, CodeReloader)]]
  58. [worker(Phoenix.CodeReloader.Server, args)]
  59. else
  60. []
  61. end
  62. end
  63. defp root!(conf) do
  64. conf[:root] ||
  65. raise "please set root: Path.expand(\"..\", __DIR__) in your endpoint " <>
  66. "inside config/config.exs in order to use code reloading or watchers"
  67. end
  68. @doc """
  69. The endpoint configuration used at compile time.
  70. """
  71. def config(otp_app, endpoint) do
  72. Phoenix.Config.from_env(otp_app, endpoint, defaults(otp_app, endpoint))
  73. end
  74. defp defaults(otp_app, module) do
  75. [otp_app: otp_app,
  76. # Compile-time config
  77. code_reloader: false,
  78. debug_errors: false,
  79. render_errors: [view: render_errors(module), default_format: "html"],
  80. # Transports
  81. transports: [
  82. longpoller_window_ms: 10_000,
  83. longpoller_pubsub_timeout_ms: 1000,
  84. longpoller_crypto: [iterations: 1000,
  85. length: 32,
  86. digest: :sha256,
  87. cache: Plug.Keys],
  88. websocket_serializer: Phoenix.Transports.JSONSerializer,
  89. websocket_timeout: :infinity
  90. ],
  91. # Runtime config
  92. cache_static_lookup: true,
  93. cache_static_manifest: nil,
  94. http: false,
  95. https: false,
  96. reloadable_paths: ["web"],
  97. secret_key_base: nil,
  98. server: Application.get_env(:phoenix, :serve_endpoints, false),
  99. static_url: nil,
  100. url: [host: "localhost", path: "/"],
  101. # Supervisor config
  102. pubsub: [],
  103. watchers: []]
  104. end
  105. defp render_errors(module) do
  106. module
  107. |> Module.split
  108. |> Enum.at(0)
  109. |> Module.concat("ErrorView")
  110. end
  111. @doc """
  112. Callback that changes the configuration from the app callback.
  113. """
  114. def config_change(endpoint, changed, removed) do
  115. res = Phoenix.Config.config_change(endpoint, changed, removed)
  116. warmup(endpoint)
  117. res
  118. end
  119. @doc """
  120. Builds the endpoint url from its configuration.
  121. The result is wrapped in a `{:cache, value}` tuple so
  122. the Phoenix.Config layer knows how to cache it.
  123. """
  124. def url(endpoint) do
  125. {:cache, calculate_url(endpoint, endpoint.config(:url))}
  126. end
  127. @doc """
  128. Builds the static url from its configuration.
  129. The result is wrapped in a `{:cache, value}` tuple so
  130. the Phoenix.Config layer knows how to cache it.
  131. """
  132. def static_url(endpoint) do
  133. url = endpoint.config(:static_url) || endpoint.config(:url)
  134. {:cache, calculate_url(endpoint, url)}
  135. end
  136. defp calculate_url(endpoint, url) do
  137. {scheme, port} =
  138. cond do
  139. config = endpoint.config(:https) ->
  140. {"https", config[:port]}
  141. config = endpoint.config(:http) ->
  142. {"http", config[:port]}
  143. true ->
  144. {"http", "80"}
  145. end
  146. scheme = url[:scheme] || scheme
  147. host = url[:host]
  148. port = port_to_string(url[:port] || port)
  149. case {scheme, port} do
  150. {"https", "443"} -> "https://" <> host
  151. {"http", "80"} -> "http://" <> host
  152. {_, _} -> scheme <> "://" <> host <> ":" <> port
  153. end
  154. end
  155. @doc """
  156. Returns the static path of a file in the static root directory.
  157. When the file exists, it includes a timestamp. When it doesn't exist,
  158. just the static path is returned.
  159. The result is wrapped in a `{:cache | :stale, value}` tuple so
  160. the Phoenix.Config layer knows how to cache it.
  161. """
  162. def static_path(endpoint, "/" <> _ = path) do
  163. file = Application.app_dir(endpoint.config(:otp_app), Path.join("priv/static", path))
  164. case File.stat(file) do
  165. {:ok, %File.Stat{type: :regular, mtime: mtime, size: size}} ->
  166. key = if endpoint.config(:cache_static_lookup), do: :cache, else: :stale
  167. vsn = {size, mtime} |> :erlang.phash2() |> Integer.to_string(16)
  168. {key, path <> "?vsn=" <> vsn}
  169. _ ->
  170. {:stale, path}
  171. end
  172. end
  173. def static_path(_endpoint, path) when is_binary(path) do
  174. raise ArgumentError, "static_path/2 expects a path starting with / as argument"
  175. end
  176. defp port_to_string({:system, env_var}), do: System.get_env(env_var)
  177. defp port_to_string(port) when is_binary(port), do: port
  178. defp port_to_string(port) when is_integer(port), do: Integer.to_string(port)
  179. @doc """
  180. Invoked to warm up caches on start and config change.
  181. """
  182. def warmup(endpoint) do
  183. warmup_url(endpoint)
  184. warmup_static(endpoint)
  185. :ok
  186. rescue
  187. _ -> :ok
  188. end
  189. defp warmup_url(endpoint) do
  190. endpoint.url
  191. end
  192. defp warmup_static(endpoint) do
  193. for {key, value} <- cache_static_manifest(endpoint) do
  194. # This should be in sync with the endpoint lookup.
  195. Phoenix.Config.cache(endpoint, {:__phoenix_static__, "/" <> key}, fn _ ->
  196. {:cache, "/" <> value <> "?vsn=d"}
  197. end)
  198. end
  199. end
  200. defp cache_static_manifest(endpoint) do
  201. if endpoint.config(:cache_static_lookup) &&
  202. (inner = endpoint.config(:cache_static_manifest)) do
  203. outer = Application.app_dir(endpoint.config(:otp_app), inner)
  204. if File.exists?(outer) do
  205. Poison.decode!(File.read!(outer))
  206. else
  207. Logger.error "Could not find static manifest at #{inspect outer}. " <>
  208. "Run mix phoenix.digest after building your static files " <>
  209. "or remove the configuration from config/prod.exs."
  210. end
  211. else
  212. %{}
  213. end
  214. end
  215. end