Repository: phoenixframework/phoenix (elixir)

Issue:
Phoenix.js channel.join error handling with LongPoll transport is broken
### Environment

* Elixir version (elixir -v): Elixir 1.9.1 / Erlang/OTP 22
* Phoenix version (mix deps): 1.5.0-dev (ba55f7e)
* NodeJS version (node -v): 10.16.3 (not important)
* NPM version (npm -v): 6.11.3 (not important)

### Issue

With WebSocket (OK behaviour):
**Given**: I have a channel that returns `{:error, :channel_disabled}` on join
**When**: User joins the channel with **WebSocket** transport
**Then** : `channel.join(...).receive('error')` is triggered where we can handle the error and decide if we want to reconnect or not.

With LongPoll:
**Given**: I have a channel that returns `{:error, :channel_disabled}` on join
**When**: User joins the channel with **LongPoll** transport
**Then** : `channel.join(...).receive('error')` is never triggered (**NOT OK**). Instead the whole transport reconnect to the PubSub. In our case we return an error again and so it basically starts reconnecting in a loop.

I did some investigation and it seems that if channel join fails then LongPoll Transport receives non 200 status in `send()` function and that invokes closeAndRetry on the transport. https://github.com/phoenixframework/phoenix/blob/master/assets/js/phoenix.js#L1166-L1167 To be exact then the status is 401 whenever an error is returned from a join. I think that's because of this line: https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/transports/long_poll.ex#L90

### Summary

Channel returning an error in `join` (server side) terminates longpoll connection (client side) instead of invoking `error` listener on the channel join. This affects only LongPoll. WebSocket is fine.

Pull request description:
Fix error handling when joining a channel using LongPoll
This fixes #3590

With **WebSocket** (OK behaviour):
**Given**: I have a channel that returns `{:error, :channel_disabled}` on join
**When**: User joins the channel with WebSocket transport
**Then**: `channel.join(...).receive('error')` is triggered where we can handle the error and decide if we want to reconnect or not.

With **LongPoll** (NOT OK behaviour):
**Given**: I have a channel that returns `{:error, :channel_disabled}` on join
**When**: User joins the channel with LongPoll transport
**Then**: `channel.join(...).receive('error')` is never triggered (NOT OK). Instead the whole transport reconnects to the PubSub. In our case we return an error again and so it basically starts reconnection loop.

This happened because `LongPoll` returned 401 whenever channel returned
something that was not `{:ok, socket}`. phoenix.js [ajax request][1] has:
```
      if(!resp || resp.status !== 200){
        this.onerror(resp && resp.status)
        this.closeAndRetry()
      }
```
This basically means that when it got 401 then it called
`closeAndRetry` on the whole transport.

`LongPoll` now returns 200 even if the join failed. This way we don't
trigger closeAndRetry. The actual error message is still received by
polling. `phoenix.js` now successfully triggers the join error callback
like with WebSocket.

[1]: https://github.com/phoenixframework/phoenix/blob/bf395cbeafae0f212e8c96c99c173d0bdb278f58/assets/js/phoenix.js#L1164-L1169

Patch:
diff --git a/assets/js/phoenix.js b/assets/js/phoenix.js
index 76bff1f80..a5a78f876 100644
--- a/assets/js/phoenix.js
+++ b/assets/js/phoenix.js
@@ -474,6 +474,20 @@ export class Channel {
   }
 
   /**
+   * Unsubscribes off of channel events
+   *
+   * Use the ref returned from a channel.on() to unsubscribe one 
+   * handler, or pass nothing for the ref to unsubscribe all 
+   * handlers for the given event.
+   *
+   * @example
+   * // Unsubscribe the do_stuff handler
+   * const ref1 = channel.on("event", do_stuff)
+   * channel.off("event", ref1)
+   * 
+   * // Unsubscribe all handlers from event
+   * channel.off("event")
+   *
    * @param {string} event
    * @param {integer} ref
    */
diff --git a/guides/deployment/heroku.md b/guides/deployment/heroku.md
index c522d56d9..d6236749d 100644
--- a/guides/deployment/heroku.md
+++ b/guides/deployment/heroku.md
@@ -69,15 +69,14 @@ Once we have signed up, we can download the correct version of the Heroku Toolbe
 The Heroku CLI, part of the Toolbelt, is useful to create Heroku applications, list currently running dynos for an existing application, tail logs or run one-off commands (mix tasks for instance).
 
 ## Create and Setup Heroku Application
-There are two different ways to deploy a Phoenix app on Heroku. We could use Heroku buildpacks or their container stack. The difference between these two approaches is in how we tell Heroku to treat our build. In buildpack case, we need to update our apps configuration on Heroku to use Phoenix/Elixir specific buildpacks instead of trying to automatically detect it. On container approach, we have more control on how we want to setup our app and we can define our container image using `Dockerfile` and `heroku.yml`. We describe both approaches below.
 
-### Using Heroku Buildpacks
+There are two different ways to deploy a Phoenix app on Heroku. We could use Heroku buildpacks or their container stack. The difference between these two approaches is in how we tell Heroku to treat our build. In buildpack case, we need to update our apps configuration on Heroku to use Phoenix/Elixir specific buildpacks. On container approach, we have more control on how we want to setup our app and we can define our container image using `Dockerfile` and `heroku.yml`. This section will explore the buildpack approach. In order to use Dockerfile, it is often recommended to convert our app to use releases, which we will describe later on.
 
-A [buildpack](https://devcenter.heroku.com/articles/buildpacks) is a convenient way of packaging framework and/or runtime support. Phoenix requires 2 buildpacks to run on Heroku, the first adds basic Elixir support and the second adds Phoenix specific commands. We will see how to install both in the next sections.
+### Create Application
 
-#### Create Application
+A [buildpack](https://devcenter.heroku.com/articles/buildpacks) is a convenient way of packaging framework and/or runtime support. Phoenix requires 2 buildpacks to run on Heroku, the first adds basic Elixir support and the second adds Phoenix specific commands.
 
-Now that we have the Toolbelt installed, let's create the Heroku application. We will do so using the latest available version of the [Elixir buildpack](https://github.com/HashNuke/heroku-buildpack-elixir):
+With the Toolbelt installed, let's create the Heroku application. We will do so using the latest available version of the [Elixir buildpack](https://github.com/HashNuke/heroku-buildpack-elixir):
 
 ```console
 $ heroku create --buildpack hashnuke/elixir
@@ -105,7 +104,7 @@ elixir_version=1.8.1
 erlang_version=21.2.5
 ```
 
-#### Adding the Phoenix Server and Assets Buildpack
+### Adding the Phoenix Server and Assets Buildpack
 
 To successfully run Phoenix in production, we need to compile assets and start the Phoenix server. The [Phoenix Static buildpack](https://github.com/gjaldon/heroku-buildpack-phoenix-static) can take care of that for us, so let's add it now.
 
@@ -128,34 +127,6 @@ Heroku will recognize this file and use the command to start your application, e
 
 Finally, note that since we are using multiple buildpacks, you might run into an issue where the sequence is out of order (the Elixir buildpack needs to run before the Phoenix Static buildpack). [Heroku's docs](https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app) explain this better, but you will need to make sure the Phoenix Static buildpack comes last.
 
-### Using Container Stack
-#### Create Heroku application
-Set the stack of your app to `container`, this allows us to use `Dockerfile` to define our app setup.
-```console
-$ heroku create
-Creating app... done, ⬢ mysterious-meadow-6277
-$ heroku stack:set container
-```
-Add a new `heroku.yml` file to your root folder. In this file you can define addons used by your app, how to build the image and what configs are passed to the image. You can learn more about Heroku's `heroku.yml` options [here](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml). Here is a sample:
-```yaml
-setup:
-  addons:
-    - plan: heroku-postgresql
-      as: DATABASE
-build:
-  docker:
-    web: Dockerfile
-  config:
-    MIX_ENV: prod
-    SECRET_KEY_BASE: $SECRET_KEY_BASE
-    DATABASE_URL: $DATABASE_URL
-```
-
-#### Setup Your app with Dockerfile
-Add `Dockerfile` to your root folder. You can follow [container release docs](./releases.md#containers).
-
-Once you have the image definition setup, you can push your app to heroku and you can see it starts building the image and deploy it.
-
 ## Making our Project ready for Heroku
 
 Every new Phoenix project ships with a config file `config/prod.secret.exs` which loads configuration and secrets from [environment variables](https://devcenter.heroku.com/articles/config-vars). This aligns well with Heroku best practices, so most the only work left for us to do is to configure URLs and SSL.
@@ -345,19 +316,39 @@ $ heroku run "POOL_SIZE=2 mix ecto.migrate"
 
 And that's it!
 
-## Connecting to your dyno
+## Deploying to Heroku using the container stack
 
-Heroku gives you the ability to connect to your dyno with an IEx shell which allows running Elixir code such as database queries.
+### Create Heroku application
 
-- Modify the `web` process in your Procfile to run a named node:
-  ```
-  web: elixir --sname server -S mix phx.server
-  ```
-- Redeploy to Heroku
-- Connect to the dyno with `heroku ps:exec` (if you have several applications on the same repository you will need to specify the app name or the remote name with `--app APP_NAME` or `--remote REMOTE_NAME`)
-- Launch an iex session with `iex --sname console --remsh server`
+Set the stack of your app to `container`, this allows us to use `Dockerfile` to define our app setup.
 
-You have an iex session into your dyno!
+```console
+$ heroku create
+Creating app... done, ⬢ mysterious-meadow-6277
+$ heroku stack:set container
+```
+
+Add a new `heroku.yml` file to your root folder. In this file you can define addons used by your app, how to build the image and what configs are passed to the image. You can learn more about Heroku's `heroku.yml` options [here](https://devcenter.heroku.com/articles/build-docker-images-heroku-yml). Here is a sample:
+
+```yaml
+setup:
+  addons:
+    - plan: heroku-postgresql
+      as: DATABASE
+build:
+  docker:
+    web: Dockerfile
+  config:
+    MIX_ENV: prod
+    SECRET_KEY_BASE: $SECRET_KEY_BASE
+    DATABASE_URL: $DATABASE_URL
+```
+
+### Setup releases and Dockerfile
+
+Now we need to define a `Dockerfile` at the root folder of your project that contains your application. We recommend to use releases when doing so, as the release will allow us to build a container with only the parts of Erlang and Elixir we actually use. Follow [the releases docs](/releases.html). At the end of the guide, there is a sample Dockerfile file you can use.
+
+Once you have the image definition setup, you can push your app to heroku and you can see it starts building the image and deploy it.
 
 ## Useful Heroku Commands
 
@@ -379,6 +370,20 @@ In fact, we can run anything using the `heroku run` command, like the Ecto migra
 $ heroku run "POOL_SIZE=2 mix ecto.migrate"
 ```
 
+## Connecting to your dyno
+
+Heroku gives you the ability to connect to your dyno with an IEx shell which allows running Elixir code such as database queries.
+
+- Modify the `web` process in your Procfile to run a named node:
+  ```
+  web: elixir --sname server -S mix phx.server
+  ```
+- Redeploy to Heroku
+- Connect to the dyno with `heroku ps:exec` (if you have several applications on the same repository you will need to specify the app name or the remote name with `--app APP_NAME` or `--remote REMOTE_NAME`)
+- Launch an iex session with `iex --sname console --remsh server`
+
+You have an iex session into your dyno!
+
 ## Troubleshooting
 
 ### Compilation Error
diff --git a/guides/ecto.md b/guides/ecto.md
index 3846e1242..80ee3a2ea 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -122,10 +122,12 @@ Our `Hello.Repo` module is the foundation we need to work with databases in a Ph
 defmodule Hello.Repo do
   use Ecto.Repo,
     otp_app: :hello,
-    adapter: Ecto.Adapter.Postgres
+    adapter: Ecto.Adapters.Postgres
 end
 ```
 
+It begins by configuring our `otp_app` name and repo module. Then it sets the adapter – Postgres, in our case. It also sets our login credentials. Of course, you can change these to match your actual credentials if they are different.
+
 Our repo has three main tasks - to bring in all the common query functions from `Ecto.Repo`, to set the `otp_app` name equal to our application name, and to configure our database adapter. We'll talk more about how to use the Repo in a bit.
 
 When `phx.new` generated our application, it included some basic repo configuration as well. Let's look at `config/dev.exs`.
@@ -142,8 +144,6 @@ config :hello, Hello.Repo,
 ...
 ```
 
-It begins by configuring our `otp_app` name and repo module. Then it sets the adapter – Postgres, in our case. It also sets our login credentials. Of course, you can change these to match your actual credentials if they are different.
-
 We also have similar configuration in `config/test.exs` and `config/prod.secret.exs` which can also be changed to match your actual credentials.
 
 ## The Schema
diff --git a/guides/endpoint.md b/guides/endpoint.md
index 01328b727..10f9b3201 100644
--- a/guides/endpoint.md
+++ b/guides/endpoint.md
@@ -166,7 +166,7 @@ In these examples, the `rewrite_on:` key specifies the HTTP header used by a rev
 
 ### HSTS
 
-HSTS or "strict-transport-security" is a mechanism that allows a website to declare itself as only accessible via a secure connection (HTTPS). It was introduced to prevent man-in-the-middle attacks that strip SSL/TLS. It causes web browers to redirect from HTTP to HTTPS and refuse to connect unless the connection uses SSL/TLS.
+HSTS or "strict-transport-security" is a mechanism that allows a website to declare itself as only accessible via a secure connection (HTTPS). It was introduced to prevent man-in-the-middle attacks that strip SSL/TLS. It causes web browsers to redirect from HTTP to HTTPS and refuse to connect unless the connection uses SSL/TLS.
 
 With `force_ssl: :hsts` set the `Strict-Transport-Security` header is set with a max age that defines the length of time the policy is valid for. Modern web browsers will respond to this by redirecting from HTTP to HTTPS for the standard case but it does have other consequenses. [RFC6797](https://tools.ietf.org/html/rfc6797) which defines HSTS also specifies **that the browser should keep track of the policy of a host and apply it until it expires.** It also specifies that **traffic on any port other than 80 is assumed to be encrypted** as per the policy.
 
diff --git a/guides/routing.md b/guides/routing.md
index 9d1f2a6eb..0633013eb 100644
--- a/guides/routing.md
+++ b/guides/routing.md
@@ -32,7 +32,7 @@ defmodule HelloWeb.Router do
   # end
 end
 ```
-The name you gave your application will appear instead of `HelloWeb` for both the router module and controller name.
+Both the router and controller module names will be prefixed with the name you gave your application instead of `HelloWeb`.
 
 The first line of this module, `use HelloWeb, :router`, simply makes Phoenix router functions available in our particular router.
 
@@ -181,7 +181,7 @@ defmodule HelloWeb.Router do
 end
 ```
 
-This means that all routes starting with `/jobs` will be sent to the `BackgroundJob.Plug` module.
+This means that all routes starting with `/jobs` will be sent to the `HelloWeb.BackgroundJob.Plug` module.
 
 We can even use the `forward/4` macro in a pipeline. If we wanted to ensure that the user was authenticated and an administrator in order to see the jobs page, we could use the following in our router.
 
@@ -214,16 +214,16 @@ Writing an actual background job worker is beyond the scope of this guide. Howev
 
 
 ```elixir
-defmodule BackgroundJob.Plug do
+defmodule HelloWeb.BackgroundJob.Plug do
   def init(opts), do: opts
   def call(conn, opts) do
     conn
     |> Plug.Conn.assign(:name, Keyword.get(opts, :name, "Background Job"))
-    |> BackgroundJob.Router.call(opts)
+    |> HelloWeb.BackgroundJob.Router.call(opts)
   end
 end
 
-defmodule BackgroundJob.Router do
+defmodule HelloWeb.BackgroundJob.Router do
   use Plug.Router
 
   plug :match
diff --git a/installer/templates/phx_ecto/data_case.ex b/installer/templates/phx_ecto/data_case.ex
index 4d6c9e6ca..9860f88c7 100644
--- a/installer/templates/phx_ecto/data_case.ex
+++ b/installer/templates/phx_ecto/data_case.ex
@@ -10,8 +10,8 @@ defmodule <%= app_module %>.DataCase do
   we enable the SQL sandbox, so changes done to the database
   are reverted at the end of every test. If you are using
   PostgreSQL, you can even run database tests asynchronously
-  by setting `use <%= web_namespace %>.DataCase, async: true`, although
-  this option is not recommendded for other databases.
+  by setting `use <%= app_module %>.DataCase, async: true`, although
+  this option is not recommended for other databases.
   """
 
   use ExUnit.CaseTemplate
diff --git a/installer/templates/phx_single/README.md b/installer/templates/phx_single/README.md
index 2f263fda0..4c7dcff6c 100644
--- a/installer/templates/phx_single/README.md
+++ b/installer/templates/phx_single/README.md
@@ -16,5 +16,5 @@ Ready to run in production? Please [check our deployment guides](https://hexdocs
   * Official website: https://www.phoenixframework.org/
   * Guides: https://hexdocs.pm/phoenix/overview.html
   * Docs: https://hexdocs.pm/phoenix
-  * Mailing list: http://groups.google.com/group/phoenix-talk
+  * Forum: https://elixirforum.com/c/phoenix-forum
   * Source: https://github.com/phoenixframework/phoenix
diff --git a/installer/templates/phx_static/app.js b/installer/templates/phx_static/app.js
index 69bf58f0b..c0b39de9d 100644
--- a/installer/templates/phx_static/app.js
+++ b/installer/templates/phx_static/app.js
@@ -1,3 +1,3 @@
 // for phoenix_html support, including form and button helpers
 // copy the following scripts into your javascript bundle:
-// * https://raw.githubusercontent.com/phoenixframework/phoenix_html/master/priv/static/phoenix_html.js
+// * deps/phoenix_html/priv/static/phoenix_html.js
diff --git a/installer/templates/phx_umbrella/apps/app_name_web/README.md b/installer/templates/phx_umbrella/apps/app_name_web/README.md
index 87cba417c..3f02bbe88 100644
--- a/installer/templates/phx_umbrella/apps/app_name_web/README.md
+++ b/installer/templates/phx_umbrella/apps/app_name_web/README.md
@@ -16,5 +16,5 @@ Ready to run in production? Please [check our deployment guides](https://hexdocs
   * Official website: https://www.phoenixframework.org/
   * Guides: https://hexdocs.pm/phoenix/overview.html
   * Docs: https://hexdocs.pm/phoenix
-  * Mailing list: http://groups.google.com/group/phoenix-talk
+  * Forum: https://elixirforum.com/c/phoenix-forum
   * Source: https://github.com/phoenixframework/phoenix
diff --git a/lib/phoenix/code_reloader.ex b/lib/phoenix/code_reloader.ex
index 8de52c8aa..51a8e0ebb 100644
--- a/lib/phoenix/code_reloader.ex
+++ b/lib/phoenix/code_reloader.ex
@@ -2,9 +2,17 @@ defmodule Phoenix.CodeReloader do
   @moduledoc """
   A plug and module to handle automatic code reloading.
 
-  For each request, Phoenix checks if any of the modules previously
-  compiled requires recompilation via `__phoenix_recompile__?/0` and then
-  calls `mix compile` for sources exclusive to the `web` directory.
+  For each request, Phoenix goes through all modules and checks if any of
+  them implement a `__phoenix_recompile__?/0` function. If they do and
+  it returns true, the module source file is touched, forcing it to be
+  recompiled. For this functionality to work, Phoenix requires you to add
+  the `:phoenix` compiler to your list of compilers:
+
+      compilers: [:phoenix] ++ Mix.compilers()
+
+  This is useful, for example, to recompile modules that depend on external
+  systems, such as directories, databases, etc. Note if you simply depend on
+  external files, `@external_resource` annotation should be used.
 
   To avoid race conditions, all code reloads are funneled through a
   sequential call operation.
diff --git a/lib/phoenix/endpoint.ex b/lib/phoenix/endpoint.ex
index 81ebff427..d1c544c86 100644
--- a/lib/phoenix/endpoint.ex
+++ b/lib/phoenix/endpoint.ex
@@ -75,7 +75,7 @@ defmodule Phoenix.Endpoint do
   ### Compile-time configuration
 
     * `:code_reloader` - when `true`, enables code reloading functionality.
-      For code the list of code reloader configuration options see
+      For the list of code reloader configuration options see
       `Phoenix.CodeReloader.reload!/1`
 
     * `:debug_errors` - when `true`, uses `Plug.Debugger` functionality for
@@ -100,7 +100,9 @@ defmodule Phoenix.Endpoint do
     * `:cache_static_manifest` - a path to a json manifest file that contains
       static files and their digested version. This is typically set to
       "priv/static/cache_manifest.json" which is the file automatically generated
-      by `mix phx.digest`
+      by `mix phx.digest`.
+      It can be either: a string containing a file system path or a tuple containing 
+      the application name and the path within that application.
 
     * `:check_origin` - configure the default `:check_origin` setting for
       transports. See `socket/3` for options. Defaults to `true`.
@@ -839,7 +841,7 @@ defmodule Phoenix.Endpoint do
     * `:max_frame_size` - the maximum allowed frame size in bytes.
       Supported from Cowboy 2.3 onwards, defaults to "infinity"
 
-    * `:compress` - whether to enable per message compresssion on
+    * `:compress` - whether to enable per message compression on
       all data frames, defaults to false
 
     * `:subprotocols` - a list of supported websocket subprotocols.
diff --git a/lib/phoenix/endpoint/cowboy2_handler.ex b/lib/phoenix/endpoint/cowboy2_handler.ex
index c478c75c4..4cd34eb20 100644
--- a/lib/phoenix/endpoint/cowboy2_handler.ex
+++ b/lib/phoenix/endpoint/cowboy2_handler.ex
@@ -2,7 +2,7 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
   @moduledoc false
 
   if Code.ensure_loaded?(:cowboy_websocket) and
-    function_exported?(:cowboy_websocket, :behaviour_info, 1) do
+       function_exported?(:cowboy_websocket, :behaviour_info, 1) do
     @behaviour :cowboy_websocket
   end
 
@@ -45,26 +45,17 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
         {:ok, req, {handler, opts}}
     end
   catch
-    :error, value ->
+    kind, reason ->
       case System.stacktrace() do
         # Maybe the handler is not available because the code is being recompiled.
         # Sync with the code reloader and retry once.
-        [{^endpoint, :__handler__, _, _} | _] when value == :undef and retry? ->
+        [{^endpoint, :__handler__, _, _} | _] when reason == :undef and retry? ->
           Phoenix.CodeReloader.Server.sync()
           init(conn, endpoint, opts, false)
 
-        _ ->
-          stack = System.stacktrace()
-          exception = Exception.normalize(:error, value, stack)
-          exit({{exception, stack}, {endpoint, :call, [conn, opts]}})
+        stacktrace ->
+          exit_on_error(kind, reason, stacktrace, {endpoint, :call, [conn, opts]})
       end
-
-    :throw, value ->
-      stack = System.stacktrace()
-      exit({{{:nocatch, value}, stack}, {endpoint, :call, [conn, opts]}})
-
-    :exit, value ->
-      exit({value, {endpoint, :call, [conn, opts]}})
   after
     receive do
       @already_sent -> :ok
@@ -82,6 +73,42 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
             inspect(other)
   end
 
+  defp exit_on_error(
+         :error,
+         %Plug.Conn.WrapperError{kind: kind, reason: reason, stack: stack},
+         _stack,
+         call
+       ) do
+    exit_on_error(kind, reason, stack, call)
+  end
+
+  defp exit_on_error(:error, value, stack, call) do
+    exception = Exception.normalize(:error, value, stack)
+    exit({{exception, stack}, call})
+  end
+
+  defp exit_on_error(:throw, value, stack, call) do
+    exit({{{:nocatch, value}, stack}, call})
+  end
+
+  defp exit_on_error(:exit, value, _stack, call) do
+    exit({value, call})
+  end
+
+  defp copy_resp_headers(%Plug.Conn{} = conn, req) do
+    Enum.reduce(conn.resp_headers, req, fn {key, val}, acc ->
+      :cowboy_req.set_resp_header(key, val, acc)
+    end)
+  end
+
+  defp handle_reply(handler, {:ok, state}), do: {:ok, [handler | state]}
+  defp handle_reply(handler, {:push, data, state}), do: {:reply, data, [handler | state]}
+
+  defp handle_reply(handler, {:reply, _status, data, state}),
+    do: {:reply, data, [handler | state]}
+
+  defp handle_reply(handler, {:stop, _reason, state}), do: {:stop, [handler | state]}
+
   ## Websocket callbacks
 
   def websocket_init([handler | state]) do
@@ -125,15 +152,4 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
   def terminate(reason, _req, [handler | state]) do
     handler.terminate(reason, state)
   end
-
-  defp copy_resp_headers(%Plug.Conn{} = conn, req) do
-    Enum.reduce(conn.resp_headers, req, fn {key, val}, acc ->
-      :cowboy_req.set_resp_header(key, val, acc)
-    end)
-  end
-
-  defp handle_reply(handler, {:ok, state}), do: {:ok, [handler | state]}
-  defp handle_reply(handler, {:push, data, state}), do: {:reply, data, [handler | state]}
-  defp handle_reply(handler, {:reply, _status, data, state}), do: {:reply, data, [handler | state]}
-  defp handle_reply(handler, {:stop, _reason, state}), do: {:stop, [handler | state]}
 end
diff --git a/lib/phoenix/endpoint/render_errors.ex b/lib/phoenix/endpoint/render_errors.ex
index 2f97f3c55..9b596c855 100644
--- a/lib/phoenix/endpoint/render_errors.ex
+++ b/lib/phoenix/endpoint/render_errors.ex
@@ -107,7 +107,7 @@ defmodule Phoenix.Endpoint.RenderErrors do
     reason = Exception.normalize(kind, reason, stack)
     format = Controller.get_format(conn)
     template = "#{conn.status}.#{format}"
-    assigns = %{kind: kind, reason: reason, stack: stack}
+    assigns = %{kind: kind, reason: reason, stack: stack, status: conn.status}
 
     conn
     |> Controller.put_view(view)
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index a09e5f165..59b5d8649 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -452,7 +452,14 @@ defmodule Phoenix.Endpoint.Supervisor do
 
   defp cache_static_manifest(endpoint) do
     if inner = endpoint.config(:cache_static_manifest) do
-      outer = Application.app_dir(endpoint.config(:otp_app), inner)
+      {app, inner} = 
+        case inner do
+          {_, _} = inner -> inner
+          inner when is_binary(inner) -> {endpoint.config(:otp_app), inner}
+          _ -> raise ArgumentError, ":cache_static_manifest must be a binary or a tuple"
+        end
+
+      outer = Application.app_dir(app, inner)
 
       if File.exists?(outer) do
         outer |> File.read!() |> Phoenix.json_library().decode!()
diff --git a/lib/phoenix/router.ex b/lib/phoenix/router.ex
index 6c9db4b44..e93964670 100644
--- a/lib/phoenix/router.ex
+++ b/lib/phoenix/router.ex
@@ -15,6 +15,13 @@ defmodule Phoenix.Router do
     end
   end
 
+  defmodule MalformedURIError do
+    @moduledoc """
+    Exception raised when the URI is malformed on matching.
+    """
+    defexception [:message, plug_status: 400]
+  end
+
   @moduledoc """
   Defines a Phoenix router.
 
@@ -375,7 +382,15 @@ defmodule Phoenix.Router do
       def call(conn, _opts) do
         %{method: method, path_info: path_info, host: host} = conn = prepare(conn)
 
-        case __match_route__(method, Enum.map(path_info, &URI.decode/1), host) do
+        decoded =
+          try do
+            Enum.map(path_info, &URI.decode/1)
+          rescue
+            ArgumentError ->
+              raise MalformedURIError, "malformed URI path: #{inspect conn.request_path}"
+          end
+
+        case __match_route__(method, decoded, host) do
           :error -> raise NoRouteError, conn: conn, router: __MODULE__
           match -> Phoenix.Router.__call__(conn, match)
         end
diff --git a/lib/phoenix/router/helpers.ex b/lib/phoenix/router/helpers.ex
index fd305e824..70b43f4b6 100644
--- a/lib/phoenix/router/helpers.ex
+++ b/lib/phoenix/router/helpers.ex
@@ -327,16 +327,22 @@ defmodule Phoenix.Router.Helpers do
   @doc """
   Callback for generate router catch alls.
   """
-  def raise_route_error(mod, fun, arity, action, routes, params)
-      when is_list(params) or is_map(params) do
+  def raise_route_error(mod, fun, arity, action, routes, params) do
+    cond do
+      not Keyword.has_key?(routes, action) ->
+        "no action #{inspect action} for #{inspect mod}.#{fun}/#{arity}"
+        |> invalid_route_error(fun, routes)
 
-    prelude =
-      if Keyword.has_key?(routes, action) do
-        "no action #{inspect action} for helper #{inspect mod}.#{fun}/#{arity}"
-      else
+      is_list(params) or is_map(params) ->
         "no function clause for #{inspect mod}.#{fun}/#{arity} and action #{inspect action}"
-      end
+        |> invalid_route_error(fun, routes)
 
+      true ->
+        invalid_param_error(mod, fun, arity, action, routes)
+    end
+  end
+
+  defp invalid_route_error(prelude, fun, routes) do
     suggestions =
       for {action, bindings} <- routes do
         bindings = Enum.join([inspect(action) | bindings], ", ")
@@ -345,7 +351,8 @@ defmodule Phoenix.Router.Helpers do
 
     raise ArgumentError, "#{prelude}. The following actions/clauses are supported:\n#{suggestions}"
   end
-  def raise_route_error(mod, fun, arity, action, routes, _params) do
+
+  defp invalid_param_error(mod, fun, arity, action, routes) do
     call_vars = Keyword.fetch!(routes, action)
 
     raise ArgumentError, """
@@ -353,7 +360,7 @@ defmodule Phoenix.Router.Helpers do
     The last argument to this function should be a keyword list or a map.
     For example:
 
-    #{fun}(#{Enum.join(["conn", ":#{action}" | call_vars], ", ")}, page: 5, per_page: 10)
+        #{fun}(#{Enum.join(["conn", ":#{action}" | call_vars], ", ")}, page: 5, per_page: 10)
 
     It is possible you have called this function without defining the proper
     number of path segments in your router.
diff --git a/lib/phoenix/socket.ex b/lib/phoenix/socket.ex
index 8c219cbeb..b8bbca716 100644
--- a/lib/phoenix/socket.ex
+++ b/lib/phoenix/socket.ex
@@ -106,7 +106,7 @@ defmodule Phoenix.Socket do
   The server may send messages or replies back. For messages, the
   ref uniquely identifies the message. For replies, the ref matches
   the original message. Both data-types also include a join_ref that
-  uniquely identifes the currently joined channel.
+  uniquely identifies the currently joined channel.
 
   The `Phoenix.Socket` implementation may also send special messages
   and replies:
diff --git a/lib/phoenix/template.ex b/lib/phoenix/template.ex
index ef3c38c8f..5473104c3 100644
--- a/lib/phoenix/template.ex
+++ b/lib/phoenix/template.ex
@@ -106,6 +106,7 @@ defmodule Phoenix.Template do
   ]
 
   @default_pattern "*"
+  @private_assigns [:__phx_template_not_found__]
 
   defmodule UndefinedError do
     @moduledoc """
@@ -334,7 +335,7 @@ defmodule Phoenix.Template do
   def raise_template_not_found(view_module, template, assigns) do
     {root, pattern, names} = view_module.__templates__()
     raise UndefinedError,
-      assigns: assigns,
+      assigns: Map.drop(assigns, @private_assigns),
       available: names,
       template: template,
       root: root,
diff --git a/lib/phoenix/transports/long_poll.ex b/lib/phoenix/transports/long_poll.ex
index 4a8c6f234..a9c6e9b5a 100644
--- a/lib/phoenix/transports/long_poll.ex
+++ b/lib/phoenix/transports/long_poll.ex
@@ -87,7 +87,7 @@ defmodule Phoenix.Transports.LongPoll do
 
     receive do
       {:ok, ^ref} -> :ok
-      {:error, ^ref} -> :unauthorized
+      {:error, ^ref} -> :ok
     after
       opts[:window_ms] -> :request_timeout
     end
diff --git a/priv/templates/phx.gen.context/schema_access.ex b/priv/templates/phx.gen.context/schema_access.ex
index 769889156..38a3db8f6 100644
--- a/priv/templates/phx.gen.context/schema_access.ex
+++ b/priv/templates/phx.gen.context/schema_access.ex
@@ -67,7 +67,7 @@
   end
 
   @doc """
-  Deletes a <%= inspect schema.alias %>.
+  Deletes a <%= schema.singular %>.
 
   ## Examples
 


Test patch:
diff --git a/installer/templates/phx_test/support/channel_case.ex b/installer/templates/phx_test/support/channel_case.ex
index 732675003..85928c26f 100644
--- a/installer/templates/phx_test/support/channel_case.ex
+++ b/installer/templates/phx_test/support/channel_case.ex
@@ -8,9 +8,11 @@ defmodule <%= web_namespace %>.ChannelCase do
   to build common data structures and query the data layer.
 
   Finally, if the test case interacts with the database,
-  it cannot be async. For this reason, every test runs
-  inside a transaction which is reset at the beginning
-  of the test unless the test case is marked as async.
+  we enable the SQL sandbox, so changes done to the database
+  are reverted at the end of every test. If you are using
+  PostgreSQL, you can even run database tests asynchronously
+  by setting `use <%= web_namespace %>.ChannelCase, async: true`, although
+  this option is not recommended for other databases.
   """
 
   use ExUnit.CaseTemplate
diff --git a/installer/templates/phx_test/support/conn_case.ex b/installer/templates/phx_test/support/conn_case.ex
index be9fcb2f2..6ce8e47de 100644
--- a/installer/templates/phx_test/support/conn_case.ex
+++ b/installer/templates/phx_test/support/conn_case.ex
@@ -12,7 +12,7 @@ defmodule <%= web_namespace %>.ConnCase do
   are reverted at the end of every test. If you are using
   PostgreSQL, you can even run database tests asynchronously
   by setting `use <%= web_namespace %>.ConnCase, async: true`, although
-  this option is not recommendded for other databases.
+  this option is not recommended for other databases.
   """
 
   use ExUnit.CaseTemplate
diff --git a/test/mix/tasks/phx.digest_test.exs b/test/mix/tasks/phx.digest_test.exs
index 83fbe33eb..ecceee699 100644
--- a/test/mix/tasks/phx.digest_test.exs
+++ b/test/mix/tasks/phx.digest_test.exs
@@ -28,7 +28,7 @@ defmodule Mix.Tasks.Phx.DigestTest do
   end
 
   @input_path "input_path"
-  test "uses the input path as output path when no outputh path is given" do
+  test "uses the input path as output path when no output path is given" do
     in_tmp @input_path, fn ->
       File.mkdir_p!(@input_path)
       Mix.Tasks.Phx.Digest.run([@input_path, "--no-deps-check"])
diff --git a/test/phoenix/controller/controller_test.exs b/test/phoenix/controller/controller_test.exs
index 14f1fe7b2..fafcf49c5 100644
--- a/test/phoenix/controller/controller_test.exs
+++ b/test/phoenix/controller/controller_test.exs
@@ -585,7 +585,7 @@ defmodule Phoenix.Controller.ControllerTest do
     assert Phoenix.Controller.__view__(MyApp.Admin.UserController) == MyApp.Admin.UserView
   end
 
-  test "__layout__ returns the layout modoule based on controller module" do
+  test "__layout__ returns the layout module based on controller module" do
     assert Phoenix.Controller.__layout__(UserController, []) ==
            LayoutView
     assert Phoenix.Controller.__layout__(MyApp.UserController, []) ==
diff --git a/test/phoenix/endpoint/endpoint_test.exs b/test/phoenix/endpoint/endpoint_test.exs
index ac83e94d1..93767256f 100644
--- a/test/phoenix/endpoint/endpoint_test.exs
+++ b/test/phoenix/endpoint/endpoint_test.exs
@@ -170,6 +170,13 @@ defmodule Phoenix.Endpoint.EndpointTest do
       event: "event3", payload: %{key: :val}, topic: "sometopic"}
   end
 
+  test "loads cache manifest from specified application" do
+    config = put_in(@config[:cache_static_manifest], {:phoenix, "../../../../test/fixtures/cache_manifest.json"})
+    
+    assert Endpoint.config_change([{Endpoint, config}], []) == :ok
+    assert Endpoint.static_path("/foo.css") == "/foo-d978852bea6530fcd197b5445ed008fd.css?vsn=d"
+  end
+
   test "server?/2 returns true for explicitly true server", config do
     endpoint = Module.concat(__MODULE__, config.test)
     Application.put_env(:phoenix, endpoint, server: true)
diff --git a/test/phoenix/endpoint/render_errors_test.exs b/test/phoenix/endpoint/render_errors_test.exs
index 61bf2a91d..b9d3e687c 100644
--- a/test/phoenix/endpoint/render_errors_test.exs
+++ b/test/phoenix/endpoint/render_errors_test.exs
@@ -11,19 +11,19 @@ defmodule Phoenix.Endpoint.RenderErrorsTest do
     "Layout: " <> render(view_template, assigns)
   end
 
-  def render("404.html", %{kind: kind, reason: _reason, stack: _stack, conn: conn}) do
+  def render("404.html", %{kind: kind, reason: _reason, stack: _stack, status: 404, conn: conn}) do
     "Got 404 from #{kind} with #{conn.method}"
   end
 
-  def render("404.json", %{kind: kind, reason: _reason, stack: _stack, conn: conn}) do
+  def render("404.json", %{kind: kind, reason: _reason, stack: _stack, status: 404, conn: conn}) do
     %{error: "Got 404 from #{kind} with #{conn.method}"}
   end
 
-  def render("415.html", %{kind: kind, reason: _reason, stack: _stack, conn: conn}) do
+  def render("415.html", %{kind: kind, reason: _reason, stack: _stack, status: 415, conn: conn}) do
     "Got 415 from #{kind} with #{conn.method}"
   end
 
-  def render("500.html", %{kind: kind, reason: _reason, stack: _stack, conn: conn}) do
+  def render("500.html", %{kind: kind, reason: _reason, stack: _stack, status: 500, conn: conn}) do
     "Got 500 from #{kind} with #{conn.method}"
   end
 
diff --git a/test/phoenix/integration/long_poll_channels_test.exs b/test/phoenix/integration/long_poll_channels_test.exs
index 19dd8d518..385343798 100644
--- a/test/phoenix/integration/long_poll_channels_test.exs
+++ b/test/phoenix/integration/long_poll_channels_test.exs
@@ -335,7 +335,7 @@ defmodule Phoenix.Integration.LongPollChannelsTest do
         topic: "room:lobby"
       }
 
-      # Publish unauthorized event
+      # Publish event to an unjoined room
       capture_log fn ->
         Phoenix.PubSub.subscribe(__MODULE__, "room:private-room")
         resp = poll :post, "/ws", @vsn, session, %{
@@ -344,8 +344,19 @@ defmodule Phoenix.Integration.LongPollChannelsTest do
           "ref" => "12300",
           "payload" => %{"body" => "this method shouldn't send!'"}
         }
-        assert resp.body["status"] == 401
+        assert resp.body["status"] == 200
         refute_receive %Broadcast{event: "new_msg"}
+
+        # Get join error
+        resp = poll(:get, "/ws", @vsn, session)
+        assert resp.body["status"] == 200
+        assert List.last(resp.body["messages"]) == %Message{
+          join_ref: nil,
+          event: "phx_reply",
+          payload: %{"response" => %{"reason" => "unmatched topic"}, "status" => "error"},
+          ref: "12300",
+          topic: "room:private-room"
+        }
       end
     end
 
diff --git a/test/phoenix/router/helpers_test.exs b/test/phoenix/router/helpers_test.exs
index 51964c65d..263633026 100644
--- a/test/phoenix/router/helpers_test.exs
+++ b/test/phoenix/router/helpers_test.exs
@@ -157,7 +157,7 @@ defmodule Phoenix.Router.HelpersTest do
 
     error_message = fn helper, arity ->
       """
-      no function clause for #{inspect Helpers}.#{helper}/#{arity} and action :skip. The following actions/clauses are supported:
+      no action :skip for #{inspect Helpers}.#{helper}/#{arity}. The following actions/clauses are supported:
 
           #{helper}(conn_or_endpoint, :file, file, params \\\\ [])
           #{helper}(conn_or_endpoint, :show, id, params \\\\ [])
@@ -261,43 +261,15 @@ defmodule Phoenix.Router.HelpersTest do
     assert Helpers.user_comment_path(__MODULE__, :new, 88, []) == "/users/88/comments/new"
     assert Helpers.user_comment_path(__MODULE__, :new, 88) == "/users/88/comments/new"
 
-    error_message = fn helper, arity ->
-      """
-      no function clause for #{inspect Helpers}.#{helper}/#{arity} and action :skip. The following actions/clauses are supported:
-
-          user_comment_file_path(conn_or_endpoint, :create, user_id, comment_id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :delete, user_id, comment_id, id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :edit, user_id, comment_id, id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :index, user_id, comment_id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :new, user_id, comment_id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :show, user_id, comment_id, id, params \\\\ [])
-          user_comment_file_path(conn_or_endpoint, :update, user_id, comment_id, id, params \\\\ [])
-      """ |> String.trim
-    end
-
-    assert_raise ArgumentError, error_message.("user_comment_file_path", 4), fn ->
+    assert_raise ArgumentError, ~r/no action :skip/, fn ->
       Helpers.user_comment_file_path(__MODULE__, :skip, 123, 456)
     end
 
-    assert_raise ArgumentError, error_message.("user_comment_file_path", 5), fn ->
+    assert_raise ArgumentError, ~r/no action :skip/, fn ->
       Helpers.user_comment_file_path(__MODULE__, :skip, 123, 456, foo: "bar")
     end
 
-    arity_error_message =
-      """
-      no action :show for helper #{inspect Helpers}.user_comment_path/3. The following actions/clauses are supported:
-
-          user_comment_path(conn_or_endpoint, :create, user_id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :delete, user_id, id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :edit, user_id, id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :index, user_id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :new, user_id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :show, user_id, id, params \\\\ [])
-          user_comment_path(conn_or_endpoint, :update, user_id, id, params \\\\ [])
-
-      """ |> String.trim
-
-    assert_raise ArgumentError, arity_error_message, fn ->
+    assert_raise ArgumentError, ~r/no function clause for Phoenix.Router.HelpersTest.Router.Helpers.user_comment_path\/3 and action :show/, fn ->
       Helpers.user_comment_path(__MODULE__, :show, 123)
     end
   end
diff --git a/test/phoenix/router/routing_test.exs b/test/phoenix/router/routing_test.exs
index 22240eb7a..81a11c849 100644
--- a/test/phoenix/router/routing_test.exs
+++ b/test/phoenix/router/routing_test.exs
@@ -181,6 +181,12 @@ defmodule Phoenix.Router.RoutingTest do
     assert conn.resp_body == "users any"
   end
 
+  test "raises on malformed URIs" do
+    assert_raise Phoenix.Router.MalformedURIError, fn ->
+      call(Router, :get, "/<% foo %>")
+    end
+  end
+
   describe "logging" do
     setup do
       Logger.enable(self())
diff --git a/test/phoenix/socket/transport_test.exs b/test/phoenix/socket/transport_test.exs
index 030c56825..af25c0cdf 100644
--- a/test/phoenix/socket/transport_test.exs
+++ b/test/phoenix/socket/transport_test.exs
@@ -99,6 +99,7 @@ defmodule Phoenix.Socket.TransportTest do
     end
 
     def invalid_allowed?(%URI{host: nil}), do: true
+    def invalid_allowed?(%URI{host: ""}), do: true
 
     test "allows custom MFA check to handle invalid host" do
       mfa = {__MODULE__, :invalid_allowed?, []}