Packages
livebook
0.15.3
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/endpoint.ex
defmodule LivebookWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :livebook
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
@session_options [
store: :cookie,
key: "lb_session",
signing_salt: "deadbook"
]
# Don't check the origin as we don't know how the web app is gonna be accessed.
# It runs locally, but may be exposed via IP or domain name. The WebSocket
# connection is already protected from CSWSH by using CSRF token.
@websocket_options [
check_origin: false,
connect_info: [:user_agent, :uri, session: @session_options]
]
socket "/live", Phoenix.LiveView.Socket, websocket: @websocket_options
socket "/socket", LivebookWeb.Socket, websocket: @websocket_options
# Serve static files at "/".
#
# In usual Phoenix applications, we serve static files from priv/static,
# however Livebook can also be run as escript, in which case it is
# packaged into a single file and priv/ is not accessible directly.
# In that case, we include priv/ in the escript archive by setting
# the :include_priv_for option. Then, on escript boot, we extract
# the priv files into a temporary directory.
#
# To account for both cases, we configure Plug.Static :from as MFA
# and return the accessible priv/ location in both scenarios.
#
# The priv/ static files are generated by the livebook.gen_priv task
# before building the escript or the release. We gzip the static
# files in priv/, since we want to serve them gzipped, and we don't
# include the non-gzipped ones to minimize app size. Note that we
# still have a separate static/ directory with the CI-precompiled
# assets, which we keep in Git so that people can install escript
# from GitHub or run MIX_ENV=prod phx.server, without Node and NPM.
# Storing minified assets is already not ideal, but we definitely
# want to avoid storing the gzipped variants in Git. That's why we
# store the assets uncompressed and then generate priv/static with
# their compressed variants as part of the build process.
if code_reloading? do
# In development, we use assets from tmp/static_dev, which are
# rebuilt on every change. We build to a different directory than
# priv/static, to make sure it can be built concurrently.
plug Plug.Static,
at: "/",
from: "tmp/static_dev",
gzip: false,
only: ["assets"]
end
plug Plug.Static,
at: "/",
from: {__MODULE__, :static_from, []},
gzip: true,
only: LivebookWeb.static_paths()
@doc false
def static_from(), do: Path.join(Livebook.Config.priv_path(), "static")
plug :force_ssl
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
end
plug Phoenix.LiveDashboard.RequestLogger,
param_key: "request_logger",
cookie_key: "request_logger"
plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
plug LivebookWeb.ProxyPlug
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Phoenix.json_library()
plug Plug.MethodOverride
plug Plug.Head
plug :session
plug :purge_cookies
# Run custom plugs from the app configuration
plug LivebookWeb.ConfiguredPlug
plug LivebookWeb.Router
@plug_session Plug.Session.init(@session_options ++ [same_site: "Lax"])
@plug_session_iframe Plug.Session.init(@session_options ++ [same_site: "None", secure: true])
def session(conn, _opts) do
if Livebook.Config.within_iframe?() do
Plug.Session.call(conn, @plug_session_iframe)
else
Plug.Session.call(conn, @plug_session)
end
end
@plug_ssl Plug.SSL.init(
host: {Livebook.Config, :force_ssl_host, []},
rewrite_on: {Livebook.Config, :rewrite_on, []}
)
def force_ssl(conn, _opts) do
if Livebook.Config.force_ssl_host() do
Plug.SSL.call(conn, @plug_ssl)
else
conn
end
end
def cookie_options() do
if Livebook.Config.within_iframe?() do
[same_site: "None", secure: true]
else
[same_site: "Lax"]
end
end
# Because we run on localhost, we may accumulate
# cookies from several other apps. Our header limit
# is set to 32kB. Once we are 75% of said limit,
# we clear other cookies to make sure we don't go
# over the limit.
def purge_cookies(conn, _opts) do
cookie_size =
conn
|> Plug.Conn.get_req_header("cookie")
|> Enum.map(&byte_size/1)
|> Enum.sum()
if cookie_size > 24576 do
conn.cookies
|> Enum.reject(fn {key, _value} -> String.starts_with?(key, "lb_") end)
|> Enum.take(10)
|> Enum.reduce(conn, fn {key, _value}, conn ->
Plug.Conn.delete_resp_cookie(conn, key)
end)
else
conn
end
end
def access_struct_url() do
base =
case struct_url() do
%URI{scheme: "https", port: 0} = uri ->
%{uri | port: port(:https, 433)}
%URI{scheme: "http", port: 0} = uri ->
%{uri | port: port(:http, 80)}
%URI{} = uri ->
uri
end
base = update_in(base.path, &(&1 || "/"))
case Livebook.Config.authentication() do
%{mode: :token, secret: token} ->
%{base | query: "token=" <> token}
_ ->
base
end
end
def access_url do
URI.to_string(access_struct_url())
end
defp port(scheme, default) do
try do
server_info(scheme)
rescue
_ -> default
else
{:ok, {_, port}} when is_integer(port) -> port
_ -> default
end
end
end