Repository: phoenixframework/phoenix
Pull request #2665 (2017-12-10 09:23:58)

Feat/log on invalid cowboy version
This relies on the Cowboy 2 PR, so that should be merged first. I discovered this could happen quite easily when upgrading an app using the gr-cowboy2 branch.

If a user updates their cowboy dependency without updating the relevant
hander in their config then the warning will be displayed. This should
help minimize issues when users simply update the Cowboy version without
also updating the config.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index efc6287c3..d9a5ab441 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,12 @@
   * [Logger] Add whitelist support to `filter_parameters` logger configuration, via new `:keep` tuple format
   * [Router] Raise on duplicate plugs in pipe_through scopes
 
+* Bug Fixes
+  * [Channel] Fix issue with WebSocket transport sending wrong ContentLength header with 403 response
+
+* JavaScript client enhancements
+  * Add `updateJoinParams` to channel for explicit param updates between rejoins
+
 * Deprecations
   * [Controller] Passing a view in `render/3` and `render/4` is deprecated in favor of `put_view/2`
 
diff --git a/assets/js/phoenix.js b/assets/js/phoenix.js
index d2e06d5c7..07aa3ddd4 100644
--- a/assets/js/phoenix.js
+++ b/assets/js/phoenix.js
@@ -269,6 +269,16 @@ class Push {
     return this
   }
 
+  /**
+   * Updates the Push payload for subsequent resends
+   *
+   * @param {Object} payload
+   * @returns {Push}
+   */
+  updatePayload(payload) {
+    this.payload = payload;
+  }
+
   /**
    * @private
    */
@@ -414,6 +424,17 @@ export class Channel {
     }
   }
 
+  /**
+   * Updates the params passed as the second argument to `new Channel("topic", params, socket)`
+   * Any subsequent reconnects on the channel will send the updated params to the `join` callback on the sever
+   *
+   * @param {Object} params
+   */
+  updateJoinParams(params = {}) {
+    this.params = params;
+    this.joinPush.updatePayload(params);
+  }
+
   /**
    * Hook into channel close
    * @param {Function} callback
diff --git a/guides/docs/contexts.md b/guides/docs/contexts.md
index 6eacecf57..d0b225b0c 100644
--- a/guides/docs/contexts.md
+++ b/guides/docs/contexts.md
@@ -818,7 +818,7 @@ Next, let's add the association in the other direction in `lib/hello/cms/author.
 
 We added the `has_many` association for author pages, and then introduced our data dependency on the `Accounts` context by wiring up the `belongs_to` association to our `Accounts.User` schema.
 
-With our associations in place, let's update our `CMS` context to require an author when creating or updating a page. We'll start off with data fetching changes. Open up your `CMS` context in `lib/hello/cms/cms.ex` and replace the `list_pages/0`, and `get_page!/1` functions with the following definitions:
+With our associations in place, let's update our `CMS` context to require an author when creating or updating a page. We'll start off with data fetching changes. Open up your `CMS` context in `lib/hello/cms/cms.ex` and replace the `list_pages/0`, `get_page!/1`, and `get_author!/1` functions with the following definitions:
 
 ```elixir
   alias Hello.CMS.{Page, Author}
diff --git a/guides/docs/controllers.md b/guides/docs/controllers.md
index c9d052a8a..85b9ef235 100644
--- a/guides/docs/controllers.md
+++ b/guides/docs/controllers.md
@@ -549,6 +549,7 @@ Let's say we have a `show` action which uses `with` to fetch a blog post and the
 defmodule HelloWeb.MyController do
   use Phoenix.Controller
   alias Hello.{Authorizer, Blog}
+  alias HelloWeb.ErrorView
 
   def show(conn, %{"id" => id}, current_user) do
     with {:ok, post} <- Blog.fetch_post(id),
@@ -617,7 +618,7 @@ end
 
 As we mentioned - Controllers are plugs.... specifically plugs which are called toward the end of the plug pipeline.  At any step of the pipeline we might have cause to stop processing - typically because we've redirected or rendered a response. `Plug.Conn.t` has a `:halted` key - setting it to true will cause downstream plugs to be skipped. We can do that easily using `Plug.Conn.halt/1`.
 
-Consider a `HelloWeb.PostFinder` plug. On call, if we find a post related to a given id then we add it to `assigns`; and if we don't find the post we respond with a 404 page.
+Consider a `HelloWeb.PostFinder` plug. On call, if we find a post related to a given id then we add it to `conn.assigns`; and if we don't find the post we respond with a 404 page.
 
 ```elixir
 defmodule HelloWeb.PostFinder do
diff --git a/lib/mix/tasks/phx.gen.html.ex b/lib/mix/tasks/phx.gen.html.ex
index 2c72bd941..692137703 100644
--- a/lib/mix/tasks/phx.gen.html.ex
+++ b/lib/mix/tasks/phx.gen.html.ex
@@ -182,8 +182,6 @@ defmodule Mix.Tasks.Phx.Gen.Html do
 
   defp inputs(%Schema{} = schema) do
     Enum.map(schema.attrs, fn
-      {_, {:array, _}} ->
-        {nil, nil, nil}
       {_, {:references, _}} ->
         {nil, nil, nil}
       {key, :integer} ->
@@ -204,6 +202,10 @@ defmodule Mix.Tasks.Phx.Gen.Html do
         {label(key), ~s(<%= datetime_select f, #{inspect(key)}, class: "form-control" %>), error(key)}
       {key, :naive_datetime} ->
         {label(key), ~s(<%= datetime_select f, #{inspect(key)}, class: "form-control" %>), error(key)}
+      {key, {:array, :integer}} ->
+        {label(key), ~s(<%= multiple_select f, #{inspect(key)}, ["1": 1, "2": 2], class: "form-control" %>), error(key)}
+      {key, {:array, _}} ->
+        {label(key), ~s(<%= multiple_select f, #{inspect(key)}, ["Option 1": "option1", "Option 2": "option2"], class: "form-control" %>), error(key)}
       {key, _}  ->
         {label(key), ~s(<%= text_input f, #{inspect(key)}, class: "form-control" %>), error(key)}
     end)
diff --git a/lib/phoenix/code_reloader/server.ex b/lib/phoenix/code_reloader/server.ex
index 5eb795317..ce24a34cd 100644
--- a/lib/phoenix/code_reloader/server.ex
+++ b/lib/phoenix/code_reloader/server.ex
@@ -25,17 +25,20 @@ defmodule Phoenix.CodeReloader.Server do
 
   def handle_call(:check_symlinks, _from, checked?) do
     if not checked? and Code.ensure_loaded?(Mix.Project) do
-      build_path = Mix.Project.build_path()
-      symlink = Path.join(Path.dirname(build_path), "__phoenix__")
+      priv_path = "#{Mix.Project.app_path}/priv"
+
+      case :file.read_link(priv_path) do
+        {:ok, _} ->
+          :ok
 
-      case File.ln_s(build_path, symlink) do
-        :ok ->
-          File.rm(symlink)
-        {:error, :eexist} ->
-          File.rm(symlink)
         {:error, _} ->
-          Logger.warn "Phoenix is unable to create symlinks. Phoenix' code reloader will run " <>
-                      "considerably faster if symlinks are allowed." <> os_symlink(:os.type)
+          if can_symlink?() do
+            File.rm_rf(priv_path)
+            Mix.Project.build_structure
+          else
+            Logger.warn "Phoenix is unable to create symlinks. Phoenix' code reloader will run " <>
+                        "considerably faster if symlinks are allowed." <> os_symlink(:os.type)
+          end
       end
     end
 
@@ -78,10 +81,30 @@ defmodule Phoenix.CodeReloader.Server do
   end
 
   defp os_symlink({:win32, _}),
-    do: " On Windows, such can be done by starting the shell with \"Run as Administrator\"."
+    do: " On Windows, the lack of symlinks may even cause empty assets to be served. " <>
+        "Luckily, you can address this issue by starting your Windows terminal at least " <>
+        "once with \"Run as Administrator\" and then running your Phoenix application."
   defp os_symlink(_),
     do: ""
 
+  defp can_symlink?() do
+    build_path = Mix.Project.build_path()
+    symlink = Path.join(Path.dirname(build_path), "__phoenix__")
+
+    case File.ln_s(build_path, symlink) do
+      :ok ->
+        File.rm_rf(symlink)
+        true
+
+      {:error, :eexist} ->
+        File.rm_rf(symlink)
+        true
+
+      {:error, _} ->
+        false
+    end
+  end
+
   defp load_backup(mod) do
     mod
     |> :code.which()
diff --git a/lib/phoenix/endpoint/cowboy2_handler.ex b/lib/phoenix/endpoint/cowboy2_handler.ex
index ec65b425c..9116e588d 100644
--- a/lib/phoenix/endpoint/cowboy2_handler.ex
+++ b/lib/phoenix/endpoint/cowboy2_handler.ex
@@ -20,30 +20,53 @@ defmodule Phoenix.Endpoint.Cowboy2Handler do
 
     * Per websocket transport:
 
-          {"/socket/websocket", Phoenix.Endpoint.Cowboy2WebSocket,
-            {Phoenix.Transports.WebSocket,
-              {MyApp.Endpoint, MyApp.UserSocket, :websocket}}}
+      ```
+      {"/socket/websocket", Phoenix.Endpoint.Cowboy2WebSocket,
+        {Phoenix.Transports.WebSocket,
+          {MyAppWeb.Endpoint, MyAppWeb.UserSocket, :websocket}}}
+      ```
 
     * Per longpoll transport:
 
-          {"/socket/long_poll", Plug.Adapters.Cowboy2.Handler,
-            {Phoenix.Transports.LongPoll,
-              {MyApp.Endpoint, MyApp.UserSocket, :longpoll}}}
+      ```
+      {"/socket/long_poll", Plug.Adapters.Cowboy2.Handler,
+        {Phoenix.Transports.LongPoll,
+          {MyAppWeb.Endpoint, MyAppWeb.UserSocket, :longpoll}}}
+      ```
+
+    * For the live-reload websocket:
+
+      ```
+      {"/phoenix/live_reload/socket/websocket", Phoenix.Endpoint.Cowboy2WebSocket,
+        {Phoenix.Transports.WebSocket,
+          {MyAppWeb.Endpoint, Phoenix.LiveReloader.Socket, :websocket}}}
+      ```
+
+      If you decide to include the live-reload websocket, you should
+      disable it when building for production.
 
     * For the endpoint:
 
-          {:_, Plug.Adapters.Cowboy2.Handler, {MyApp.Endpoint, []}}
+      ```
+      {:_, Plug.Adapters.Cowboy2.Handler, {MyAppWeb.Endpoint, []}}
+      ```
 
   For example:
 
-      config :myapp, MyApp.Endpoint,
+      config :myapp, MyAppWeb.Endpoint,
         http: [dispatch: [
                 {:_, [
-                    {"/foo", MyApp.CustomHandler, []},
-                    {"/bar", MyApp.AnotherHandler, []},
-                    {:_, Plug.Adapters.Cowboy2.Handler, {MyApp.Endpoint, []}}
+                    {"/foo", MyAppWeb.CustomHandler, []},
+                    {"/bar", MyAppWeb.AnotherHandler, []},
+                    {"/phoenix/live_reload/socket/websocket", Phoenix.Endpoint.Cowboy2WebSocket,
+                      {Phoenix.Transports.WebSocket,
+                        {MyAppWeb.Endpoint, Phoenix.LiveReloader.Socket, :websocket}}},
+                    {:_, Plug.Adapters.Cowboy2.Handler, {MyAppWeb.Endpoint, []}}
                   ]}]]
 
+  Note: if you reconfigure HTTP options in `MyAppWeb.Endpoint.init/1`,
+  your dispatch options set in mix config will be overwritten.
+
   It is also important to specify your handlers first, otherwise
   Phoenix will intercept the requests before they get to your handler.
   """
diff --git a/lib/phoenix/endpoint/cowboy_handler.ex b/lib/phoenix/endpoint/cowboy_handler.ex
index 9a9c38df3..1ea06a1e0 100644
--- a/lib/phoenix/endpoint/cowboy_handler.ex
+++ b/lib/phoenix/endpoint/cowboy_handler.ex
@@ -24,30 +24,53 @@ defmodule Phoenix.Endpoint.CowboyHandler do
 
     * Per websocket transport:
 
-          {"/socket/websocket", Phoenix.Endpoint.CowboyWebSocket,
-            {Phoenix.Transports.WebSocket,
-              {MyApp.Endpoint, MyApp.UserSocket, :websocket}}}
+      ```
+      {"/socket/websocket", Phoenix.Endpoint.CowboyWebSocket,
+        {Phoenix.Transports.WebSocket,
+          {MyAppWeb.Endpoint, MyAppWeb.UserSocket, :websocket}}}
+      ```
 
     * Per longpoll transport:
 
-          {"/socket/long_poll", Plug.Adapters.Cowboy.Handler,
-            {Phoenix.Transports.LongPoll,
-              {MyApp.Endpoint, MyApp.UserSocket, :longpoll}}}
+      ```
+      {"/socket/long_poll", Plug.Adapters.Cowboy.Handler,
+        {Phoenix.Transports.LongPoll,
+          {MyAppWeb.Endpoint, MyAppWeb.UserSocket, :longpoll}}}
+      ```
+
+    * For the live-reload websocket:
+
+      ```
+      {"/phoenix/live_reload/socket/websocket", Phoenix.Endpoint.CowboyWebSocket,
+        {Phoenix.Transports.WebSocket,
+          {MyAppWeb.Endpoint, Phoenix.LiveReloader.Socket, :websocket}}}
+      ```
+
+      If you decide to include the live-reload websocket, you should
+      disable it when building for production.
 
     * For the endpoint:
 
-          {:_, Plug.Adapters.Cowboy.Handler, {MyApp.Endpoint, []}}
+      ```
+      {:_, Plug.Adapters.Cowboy.Handler, {MyAppWeb.Endpoint, []}}
+      ```
 
   For example:
 
-      config :myapp, MyApp.Endpoint,
+      config :myapp, MyAppWeb.Endpoint,
         http: [dispatch: [
                 {:_, [
-                    {"/foo", MyApp.CustomHandler, []},
-                    {"/bar", MyApp.AnotherHandler, []},
-                    {:_, Plug.Adapters.Cowboy.Handler, {MyApp.Endpoint, []}}
+                    {"/foo", MyAppWeb.CustomHandler, []},
+                    {"/bar", MyAppWeb.AnotherHandler, []},
+                    {"/phoenix/live_reload/socket/websocket", Phoenix.Endpoint.CowboyWebSocket,
+                      {Phoenix.Transports.WebSocket,
+                        {MyAppWeb.Endpoint, Phoenix.LiveReloader.Socket, :websocket}}},
+                    {:_, Plug.Adapters.Cowboy.Handler, {MyAppWeb.Endpoint, []}}
                   ]}]]
 
+  Note: if you reconfigure HTTP options in `MyAppWeb.Endpoint.init/1`,
+  your dispatch options set in mix config will be overwritten.
+
   It is also important to specify your handlers first, otherwise
   Phoenix will intercept the requests before they get to your handler.
   """
diff --git a/lib/phoenix/endpoint/handler.ex b/lib/phoenix/endpoint/handler.ex
index 45ca9125b..915001523 100644
--- a/lib/phoenix/endpoint/handler.ex
+++ b/lib/phoenix/endpoint/handler.ex
@@ -16,6 +16,8 @@ defmodule Phoenix.Endpoint.Handler do
   """
   @callback child_spec(scheme :: atom, endpoint :: module, config :: Keyword.t) :: Supervisor.Spec.spec
 
+  alias Phoenix.Endpoint.{CowboyHandler, Cowboy2Handler}
+
   use Supervisor
   require Logger
 
@@ -26,7 +28,11 @@ defmodule Phoenix.Endpoint.Handler do
 
   @doc false
   def init({otp_app, endpoint}) do
-    handler  = endpoint.config(:handler)
+    user_handler = endpoint.config(:handler)
+    autodetected_handler = cowboy_version_handler()
+    warn_on_different_handler_version(user_handler, autodetected_handler, endpoint)
+    handler = user_handler || autodetected_handler
+
     children =
       for {scheme, port} <- [http: 4000, https: 4040],
           config = endpoint.config(scheme) do
@@ -56,4 +62,33 @@ defmodule Phoenix.Endpoint.Handler do
   defp to_port(binary)  when is_binary(binary), do: String.to_integer(binary)
   defp to_port(integer) when is_integer(integer), do: integer
   defp to_port({:system, env_var}), do: to_port(System.get_env(env_var))
+
+  defp cowboy_version_handler() do
+    case Application.spec(:cowboy, :vsn) do
+      [?1 | _] -> CowboyHandler
+      _ -> Cowboy2Handler
+    end
+  end
+
+  defp warn_on_different_handler_version(CowboyHandler, Cowboy2Handler, endpoint) do
+    Logger.warn("""
+    You have specified #{inspect CowboyHandler} for Cowboy v1.x \
+    in the :handler configuration of your Phoenix endpoint #{inspect endpoint} \
+    but your mix.exs has fetched Cowboy v2.x.
+
+    If you wish to use Cowboy 1, please update mix.exs to point to the \
+    correct Cowboy version:
+
+        {:cowboy, "~> 1.0"}
+
+    If you want to use Cowboy 2, then please remove the :handler option \
+    in your config.exs file or set it to:
+
+        handler: Phoenix.Endpoint.Cowboy2Handler
+
+    """)
+
+    raise "aborting due to handler mismatch"
+  end
+  defp warn_on_different_handler_version(_user, _autodetected, _endpoint), do: nil
 end
diff --git a/lib/phoenix/endpoint/supervisor.ex b/lib/phoenix/endpoint/supervisor.ex
index 90fc9dd5d..c4dff1d18 100644
--- a/lib/phoenix/endpoint/supervisor.ex
+++ b/lib/phoenix/endpoint/supervisor.ex
@@ -114,7 +114,6 @@ defmodule Phoenix.Endpoint.Supervisor do
 
   defp defaults(otp_app, module) do
     [otp_app: otp_app,
-     handler: Phoenix.Endpoint.CowboyHandler,
 
      # Compile-time config
      code_reloader: false,


Test patch:
diff --git a/assets/test/channel_test.js b/assets/test/channel_test.js
index 326c27147..ffcd35efc 100644
--- a/assets/test/channel_test.js
+++ b/assets/test/channel_test.js
@@ -40,6 +40,31 @@ describe("constructor", () => {
   })
 })
 
+describe("updateJoinParams", () => {
+  beforeEach(() => {
+    socket = { timeout: 1234 }
+  })
+
+  it("can update the join params", () => {
+    channel = new Channel("topic", { one: "two" }, socket)
+    const joinPush = channel.joinPush
+
+    assert.deepEqual(joinPush.channel, channel)
+    assert.deepEqual(joinPush.payload, { one: "two" })
+    assert.equal(joinPush.event, "phx_join")
+    assert.equal(joinPush.timeout, 1234)
+
+    channel.updateJoinParams({ three: "four" })
+
+    assert.deepEqual(joinPush.channel, channel)
+    assert.deepEqual(joinPush.payload, { three: "four" })
+    assert.deepEqual(channel.params, { three: "four" })
+    assert.equal(joinPush.event, "phx_join")
+    assert.equal(joinPush.timeout, 1234)
+
+  });
+});
+
 describe("join", () => {
   beforeEach(() => {
     socket = new Socket("/socket", { timeout: defaultTimeout })
diff --git a/guides/docs/testing/testing_controllers.md b/guides/docs/testing/testing_controllers.md
index c7a994b7f..c51ab1867 100644
--- a/guides/docs/testing/testing_controllers.md
+++ b/guides/docs/testing/testing_controllers.md
@@ -17,7 +17,7 @@ However, *don't* actually run this command. Instead, we're going to explore test
 If you haven't already done so, first create a blank project by running:
 
 ```console
-$ mix phx.new hello -y
+$ mix phx.new hello
 ```
 
 Change into the newly-created `hello` directory, configure your database in `config/dev.exs` and then run:
@@ -134,9 +134,10 @@ defmodule HelloWeb.UserControllerTest do
     # create users local to this database connection and test
     [{:ok, user1},{:ok, user2}] = Enum.map(users, &Accounts.create_user(&1))
 
-    response = conn
-    |> get(user_path(conn, :index))
-    |> json_response(200)
+    response =
+      conn
+      |> get(user_path(conn, :index))
+      |> json_response(200)
 
     expected = %{
       "data" => [
@@ -149,9 +150,9 @@ defmodule HelloWeb.UserControllerTest do
   end
 ```
 
-Let's take a look at what's going on here. First we alias `Hello.Accounts`, the context module that provides us with our repository manipulation functions. When we use the `HelloWeb.ConnCase` module, it sets things up such that each connection is wrapped in a transaction, *and* all of the database interactions inside of the test use the same database connection and transaction. This module also sets up a `conn` attribute in our ExUnit context, using `Phoenix.ConnCase/build_conn/0`m we pattern match this to use it in each test case. For details, take a look at the file `test/support/conn_case.ex`, as well as the [Ecto documentation for SQL.Sandbox](https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html). We could put a `build_conn/0` call inside of each test, but it is cleaner to use a setup block to do it.
+Let's take a look at what's going on here. First we alias `Hello.Accounts`, the context module that provides us with our repository manipulation functions. When we use the `HelloWeb.ConnCase` module, it sets things up such that each connection is wrapped in a transaction, *and* all of the database interactions inside of the test use the same database connection and transaction. This module also sets up a `conn` attribute in our ExUnit context, using `Phoenix.ConnCase/build_conn/0`. We then pattern match this to use it in each test case. For details, take a look at the file `test/support/conn_case.ex`, as well as the [Ecto documentation for SQL.Sandbox](https://hexdocs.pm/ecto/Ecto.Adapters.SQL.Sandbox.html). We could put a `build_conn/0` call inside of each test, but it is cleaner to use a setup block to do it.
 
-The index test then hooks into the context to extract the contents of the `conn:` key. We then create two users using the `Hello.Accounts.create_user/1` function. Again, note that this function accesses the test repo, but even though we don't pass the `conn` variable to the call, it still uses the same connection and puts these new users inside the same database transaction. Next the `conn` is piped to a `get` function to make a `GET` request to our `UserController` index action, which is in turn piped into `json_response/2` along with the expected HTTP status code. This will return the JSON from the response body, when everything is wired up properly. We represent the JSON we want the controller action to return with the variable `expected`, and assert that the `response` and `expected` are the same.
+The index test then hooks into the context to extract the contents of the `:conn` key. We then create two users using the `Hello.Accounts.create_user/1` function. Again, note that this function accesses the test repo, but even though we don't pass the `conn` variable to the call, it still uses the same connection and puts these new users inside the same database transaction. Next the `conn` is piped to a `get` function to make a `GET` request to our `UserController` index action, which is in turn piped into `json_response/2` along with the expected HTTP status code. This will return the JSON from the response body, when everything is wired up properly. We represent the JSON we want the controller action to return with the variable `expected`, and assert that the `response` and `expected` are the same.
 
 Our expected data is a JSON response with a top level key of `"data"` containing an array of users that have `"name"` and `"email"` properties that should match the users created before making the request. Also, we do not want the users' "password" properties to show up in our JSON response.
 
@@ -252,10 +253,10 @@ Our show tests currently look like this:
   end
 ```
 
-Run this test only by running the following command: (if your show tests don't start on line 40, change the line number accordingly)
+Run this test only by running the following command: (if your show tests don't start on line 34, change the line number accordingly)
 
 ```console
-$ mix test test/hello_web/controllers/user_controller_test.exs:40
+$ mix test test/hello_web/controllers/user_controller_test.exs:34
 ```
 
 Our first `show/2` test result is, as expected, not implemented. Let's build a test around what we think a successful `show/2` should look like.
@@ -264,11 +265,12 @@ Our first `show/2` test result is, as expected, not implemented. Let's build a t
 test "Responds with user info if the user is found", %{conn: conn} do
   {:ok, user} = Accounts.create_user(%{name: "John", email: "john@example.com", password: "john pass"})
 
-  response = conn
-  |> get(user_path(conn, :show, user.id))
-  |> json_response(200)
+  response =
+    conn
+    |> get(user_path(conn, :show, user.id))
+    |> json_response(200)
 
-  expected = %{ "data" => %{"email" => user.email, "name" => user.name} }
+  expected = %{"data" => %{"email" => user.email, "name" => user.name}}
 
   assert response == expected
 end
@@ -298,11 +300,12 @@ describe "show/2" do
   setup [:create_user]
   test "Responds with user info if the user is found", %{conn: conn, user: user} do
 
-    response = conn
-    |> get(user_path(conn, :show, user.id))
-    |> json_response(200)
+    response =
+      conn
+      |> get(user_path(conn, :show, user.id))
+      |> json_response(200)
 
-    expected = %{ "data" => %{"email" => user.email, "name" => user.name} }
+    expected = %{"data" => %{"email" => user.email, "name" => user.name}}
 
     assert response == expected
   end
@@ -319,11 +322,12 @@ Finally, let's change our `index/2` test to also use the new `create_user` funct
     setup [:create_user]
     test "index/2 responds with all Users", %{conn: conn, user: user} do
 
-      response = conn
-      |> get(user_path(conn, :index))
-      |> json_response(200)
+      response =
+        conn
+        |> get(user_path(conn, :index))
+        |> json_response(200)
 
-      expected = %{ "data" => [%{ "name" => user.name, "email" => user.email }] }
+      expected = %{"data" => [%{"name" => user.name, "email" => user.email}]}
 
       assert response == expected
     end
@@ -389,24 +393,19 @@ Walking through our TDD steps, we add a test that supplies a non-existent user i
 
 ```elixir
 test "Responds with a message indicating user not found", %{conn:  conn} do
-  response =
-    conn
-    |> get(user_path(conn, :show, -1 ))
-    |> json_response(404)
+  conn = get(conn, user_path(conn, :show, -1))
 
-  expected = %{ "errors" => "User not found." }
-
-  assert response == expected
+  assert text_response(conn, 404) =~ "User not found"
 end
 ```
 
-We want a HTTP code of 404 to notify the requester that this resource was not found, as well as an accompanying error message. You can run this test now to see what happens. You should see that an `Ecto.NoResultsError` is thrown, because there is no such user in the database.
+We want a HTTP status code of 404 to notify the requester that this resource was not found, as well as an accompanying error message. Notice that we use [`text_response/2`](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#text_response/2) instead of [`json_response/2`](https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#json_response/2) to assert that the status code is 404 and the response body matches the accompanying error message. You can run this test now to see what happens. You should see that an `Ecto.NoResultsError` is thrown, because there is no such user in the database.
 
 Our controller action needs to handle the error thrown by Ecto. We have two choices here. By default, this will be handled by the [phoenix_ecto](https://github.com/phoenixframework/phoenix_ecto) library, returning a 404. However if we want to show a custom error message, we can create a new `get_user/1` function that does not throw an Ecto error. For this example, we'll take the second path and implement a new `get_user/1` function in the file `lib/hello/accounts/accounts.ex`, just before the `get_user!/1` function:
 
 ```elixir
 @doc """
-Gets a single `%User{}` from the data store  where the primary key matches the
+Gets a single `%User{}` from the data store where the primary key matches the
 given id.
 
 Returns `nil` if no result was found.
@@ -431,16 +430,17 @@ def show(conn, %{"id" => id}) do
     nil ->
       conn
       |> put_status(:not_found)
-      |> json(%{error: "User not found"})
+      |> text("User not found")
+
     user ->
       render(conn, "show.json", user: user)
   end
 end
 ```
 
-The second branch of the case statement handles the "happy path" we've already covered.
+The first branch of the case statement handles the `nil` result case. First, we use the [`put_status/2`](https://hexdocs.pm/plug/Plug.Conn.html#put_status/2) function from `Plug.Conn` to set the desired error status. The complete list of allowed codes can be found in the [Plug.Conn.Status documentation](https://hexdocs.pm/plug/Plug.Conn.Status.html), where we can see that `:not_found` corresponds to our desired "404" status. We then return a text response using [`text/2`](https://hexdocs.pm/phoenix/Phoenix.Controller.html#text/2).
 
-The first branch of the case statement handles the `nil` result case. First, we use the [`put_status/2`](https://hexdocs.pm/plug/Plug.Conn.html#put_status/2) function from `Plug.Conn` to set the desired error status. The complete list of allowed codes can be found in the [Plug.Conn.Status documentation](https://hexdocs.pm/plug/Plug.Conn.Status.html), where we can see that `:not_found` corresponds to our desired "404" status. We then return a JSON error.
+The second branch of the case statement handles the "happy path" we've already covered.
 
 With those implemented, our tests pass.
 
diff --git a/lib/phoenix/test/conn_test.ex b/lib/phoenix/test/conn_test.ex
index 67a290d24..7a3bffd18 100644
--- a/lib/phoenix/test/conn_test.ex
+++ b/lib/phoenix/test/conn_test.ex
@@ -134,7 +134,7 @@ defmodule Phoenix.ConnTest do
   This is useful when a specific connection is required
   for testing a plug or a particular function.
   """
-  @spec build_conn(atom | binary, binary, binary | list | map) :: Conn.t
+  @spec build_conn(atom | binary, binary, binary | list | map | nil) :: Conn.t
   def build_conn(method, path, params_or_body \\ nil) do
     Plug.Adapters.Test.Conn.conn(%Conn{}, method, path, params_or_body)
     |> Conn.put_private(:plug_skip_csrf_protection, true)
@@ -144,7 +144,7 @@ defmodule Phoenix.ConnTest do
   @doc """
   Deprecated version of conn/3. Use build_conn/3 instead
   """
-  @spec conn(atom | binary, binary, binary | list | map) :: Conn.t
+  @spec conn(atom | binary, binary, binary | list | map | nil) :: Conn.t
   def conn(method, path, params_or_body \\ nil) do
     IO.write :stderr, """
     warning: using conn/3 to build a connection is deprecated. Use build_conn/3 instead.
@@ -495,6 +495,13 @@ defmodule Phoenix.ConnTest do
   Useful for unit testing Plugs where Endpoint and/or
   router pipeline plugs are required for proper setup.
 
+  Note the use of `get("/")` following `bypass_through` in the examples below.
+  To execute the plug pipelines, you must issue a request against the router.
+  Most often, you can simpy send a GET request against the root path, but you
+  may also specify a different method or path which your pipelines may operate
+  against. If you ommit the request you may find that your tests return
+  a `flash not fetched, call fetch_flash/2` or simular error.
+
   ## Examples
 
   For example, imagine you are testing an authentication
diff --git a/test/mix/tasks/phx.gen.html_test.exs b/test/mix/tasks/phx.gen.html_test.exs
index e79a1c68a..5c4d3292d 100644
--- a/test/mix/tasks/phx.gen.html_test.exs
+++ b/test/mix/tasks/phx.gen.html_test.exs
@@ -108,6 +108,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
         assert file =~ ~s(<%= text_input f, :title, class: "form-control" %>)
         assert file =~ ~s(<%= number_input f, :votes, class: "form-control" %>)
         assert file =~ ~s(<%= number_input f, :cost, step: "any", class: "form-control" %>)
+        assert file =~ ~s(<%= multiple_select f, :tags, ["Option 1": "option1", "Option 2": "option2"], class: "form-control" %>)
         assert file =~ ~s(<%= checkbox f, :popular, class: "checkbox" %>)
         assert file =~ ~s(<%= datetime_select f, :drafted_at, class: "form-control" %>)
         assert file =~ ~s(<%= datetime_select f, :published_at, class: "form-control" %>)
@@ -119,6 +120,7 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
         assert file =~ ~s(<%= label f, :title, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :votes, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :cost, class: "control-label" %>)
+        assert file =~ ~s(<%= label f, :tags, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :popular, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :drafted_at, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :published_at, class: "control-label" %>)
@@ -127,7 +129,6 @@ defmodule Mix.Tasks.Phx.Gen.HtmlTest do
         assert file =~ ~s(<%= label f, :alarm, class: "control-label" %>)
         assert file =~ ~s(<%= label f, :secret, class: "control-label" %>)
 
-        refute file =~ ":tags"
         refute file =~ ~s(<%= label f, :user_id)
         refute file =~ ~s(<%= number_input f, :user_id)
       end
diff --git a/test/phoenix/integration/endpoint_test.exs b/test/phoenix/integration/endpoint_test.exs
index a3041f238..b892b2f2d 100644
--- a/test/phoenix/integration/endpoint_test.exs
+++ b/test/phoenix/integration/endpoint_test.exs
@@ -8,20 +8,18 @@ defmodule Phoenix.Integration.EndpointTest do
   alias Phoenix.Integration.AdapterTest.ProdEndpoint
   alias Phoenix.Integration.AdapterTest.DevEndpoint
   alias Phoenix.Integration.AdapterTest.ProdInet6Endpoint
-
-  handler =
-    case Application.spec(:cowboy, :vsn) do
-      [?2 | _] -> Phoenix.Endpoint.Cowboy2Handler
-      _ -> Phoenix.Endpoint.CowboyHandler
-    end
+  alias Phoenix.Integration.AdapterTest.InvalidHandlerEndpoint
 
   Application.put_env(:endpoint_int, ProdEndpoint,
     http: [port: "4807"], url: [host: "example.com"], server: true,
-    handler: handler, render_errors: [accepts: ~w(html json)])
+    render_errors: [accepts: ~w(html json)])
   Application.put_env(:endpoint_int, DevEndpoint,
-      http: [port: "4808"], handler: handler, debug_errors: true)
+      http: [port: "4808"], debug_errors: true)
   Application.put_env(:endpoint_int, ProdInet6Endpoint,
-    http: [{:port, "4809"}, :inet6], handler: handler,
+    http: [{:port, "4809"}, :inet6],
+    url: [host: "example.com"], server: true)
+  Application.put_env(:endpoint_int, InvalidHandlerEndpoint,
+    http: [{:port, "4810"}, :inet6], handler: Phoenix.Endpoint.CowboyHandler,
     url: [host: "example.com"], server: true)
 
   defmodule Router do
@@ -80,7 +78,7 @@ defmodule Phoenix.Integration.EndpointTest do
     end
   end
 
-  for mod <- [ProdEndpoint, DevEndpoint, ProdInet6Endpoint] do
+  for mod <- [ProdEndpoint, DevEndpoint, ProdInet6Endpoint, InvalidHandlerEndpoint] do
     defmodule mod do
       use Phoenix.Endpoint, otp_app: :endpoint_int
       @before_compile Wrapper