Skip to content

Latest commit

 

History

History
69 lines (55 loc) · 2.02 KB

halting_the_pipe.md

File metadata and controls

69 lines (55 loc) · 2.02 KB

Halting the pipe

Each operation in a pipe takes a single WebPipe::Conn instance as an argument and returns another (or the same) instance. A series of operations on the connection struct is propagated until a final response is sent to the client.

More often than not, you may need to conditionally stop the propagation of a pipe at a given operation. For example, you could fetch the user requesting a resource. In the case they were granted to perform the required action, you would go on. However, if they weren't, you would like to halt the connection and respond with a 4xx HTTP status code.

To stop the pipe, you have to call #halt on the connection struct.

At the implementation level, we must admit that we've not been 100% accurate until now. We said that the first operation in the pipe received a WebPipe::Conn instance. That's true. However, it is more precise to say that it gets a WebPipe::Conn::Ongoing instance WebPipe::Conn::Ongoing being a subclass of WebPipe::Conn).

As long as an operation responds with a WebPipe::Conn::Ongoing instance, the propagation will go on. However, when an operation returns a WebPipe::Conn::Halted instance (another subclass of WebPipe::Conn) then any operation downstream will be ignored. Calling #halt copies all attributes to a WebPipe::Conn::Halted instance and returns it.

This made-up example checks if the user in the request has an admin role. In that case, it returns the solicited resource. Otherwise, they're unauthorized, and they never get it.

WebPipe.load_extensions(:params)

class ShowTaskApp
  include WebPipe
  
  plug :fetch_user
  plug :authorize
  plug :render_task
  
  private
  
  def fetch_user(conn)
    conn.add(
      :user, UserRepo.find(conn.params[:user_id])
    )
  end
  
  def authorize(conn)
    if conn.fetch(:user).admin?
      conn
    else
      conn.
        set_status(401).
        halt
    end
  end
  
  def render_task(conn)
    conn.set_response_body(
      TaskRepo.find(conn.params[:id]).to_json
    )
  end
end

run ShowTaskApp.new