Background
I have an app that is a webserver with cowboy and uses Plugs. Since this app was inherited, using Phoenix is out of the question unless we remake the entire thing, which is not happening.
My objective is to instead of having everything inside one huge file, to have several plugs and connect them via pipelines.
Code
Let’s assume I have a main router Plug, that looks like this:
defmodule MyApp.Web.Router do
use Plug.Router
plug(:match)
forward "/check", to: MyApp.Route.Check
forward "/dispatch", to: MyApp.Plug.Dispatch
end
So here I have 2 things. A Route for the endpoint /check
, which looks like this:
defmodule MyApp.Route.Check do
use Plug.Router
plug(:dispatch)
get "/", do: send_resp(conn, 200, "ok")
end
And a Plug pipeline for /dispatch
that looks like this:
defmodule MyApp.Plug.Dispatch do
use Plug.Builder
plug(Plug.Parsers, parsers: [:urlencoded]) #parses parameters
plug(MyApp.Plug.Metrics) #exposes /metrics path
plug(Cipher.ValidatePlug) #typical URL validation
plug(MyApp.Route.Dispatch) #forwards to dispatch Route
end
This pipeline parses the parameters, notifies a metrics service, validates the request and then sends it to the proper Router, which looks like this:
defmodule MyApp.Route.Dispatch do
use Plug.Router
plug(:dispatch)
get "/", do: send_resp(conn, 200, "Info dispatched")
end
Problem
The problem here is that nothing works. Quite literally if I launch the application and try to access even the dummest endpoint ( /check
) the code blows with errors:
17:44:03.330 [error] #PID<0.402.0> running MyApp.Web.Router (connection #PID<0.401.0>, stream id 1) terminated
Server: localhost:4003 (http)
Request: GET /check
** (exit) an exception was raised:
** (Plug.Conn.NotSentError) a response was neither set nor sent from the connection
(plug_cowboy) lib/plug/cowboy/handler.ex:37: Plug.Cowboy.Handler.maybe_send/2
(plug_cowboy) lib/plug/cowboy/handler.ex:13: Plug.Cowboy.Handler.init/2
(...)
I have now spent the entirety of my day reading documentation and this was as far as I got. The app is super simple, it is pretty much the hello world for plugs:
https://elixirschool.com/en/lessons/specifics/plug/
But with MyApp.Web.Router
instead of the one they use.
A MWE can be seen here:
https://github.com/Fl4m3Ph03n1x/plug-pipeline-problem
What am I doing wrong?
Route was not matching on
Example.Route.Dispatch
andExample.Route.Check
. To fix that you need to make two changes:and
Alternatively, you can do this: https://github.com/Fl4m3Ph03n1x/plug-pipeline-problem/pull/1
To understand more about why it is not matching you can use Plug.Router.match_path/1 on a "catch all" match.