Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions lib/srh/http/base_router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Srh.Http.BaseRouter do
alias Srh.Http.ResultEncoder

plug(:match)
plug(Srh.Http.ContentTypeCheckPlug)
plug(Plug.Parsers, parsers: [:json], pass: ["application/json"], json_decoder: Jason)
plug(:dispatch)

Expand All @@ -25,7 +26,7 @@ defmodule Srh.Http.BaseRouter do
end

match _ do
send_resp(conn, 404, "Endpoint not found")
handle_response({:not_found, "SRH: Endpoint not found. SRH might not support this feature yet."}, conn)
end

defp do_command_request(conn, success_lambda) do
Expand Down Expand Up @@ -71,41 +72,42 @@ defmodule Srh.Http.BaseRouter do
end

defp handle_response(response, conn) do
%{code: code, message: message, json: json} =
# Errors are strings, and data just means the content is directly encoded with Jason.encode!
# {404, {:error, "Message"}}
# {200, {:data, ""}}

{code, resp_data} =
case response do
{:ok, data} ->
%{code: 200, message: Jason.encode!(data), json: true}
{200, {:data, data}}

{:not_found, message} ->
%{code: 404, message: message, json: false}
{404, {:error, message}}

{:malformed_data, message} ->
%{code: 400, message: message, json: false}
{400, {:error, message}}

{:redis_error, data} ->
%{code: 400, message: Jason.encode!(data), json: true}
{400, {:data, data}}

{:not_authorized, message} ->
%{code: 401, message: message, json: false}
{401, {:error, message}}

{:connection_error, message} ->
%{code: 500, message: Jason.encode!(%{error: message}), json: true}

{:server_error, _} ->
%{code: 500, message: "An error occurred internally", json: false}
{500, {:error, message}}

_ ->
%{code: 500, message: "An error occurred internally", json: false}
{500, {:error, "SRH: An error occurred internally"}}
end

case json do
true ->
conn
|> put_resp_header("content-type", "application/json")

false ->
conn
end
|> send_resp(code, message)
conn
|> put_resp_header("content-type", "application/json")
|> send_resp(code, create_response_body(resp_data))
end

# :data just directly encodes
defp create_response_body({:data, data}), do: Jason.encode!(data)

# :error wraps the message in an error object
defp create_response_body({:error, error}), do: Jason.encode!(%{error: error})
end
42 changes: 42 additions & 0 deletions lib/srh/http/content_type_check_plug.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule Srh.Http.ContentTypeCheckPlug do
import Plug.Conn

def init(opts), do: opts

def call(conn, _opts) do
# Only parse for POST, PUT, PATCH, and DELETE requests, which is what Plug.Parsers does
case conn.method do
"POST" ->
check_content_type(conn)

"PUT" ->
check_content_type(conn)

"PATCH" ->
check_content_type(conn)

"DELETE" ->
check_content_type(conn)

# All other methods can proceed
_ ->
conn
end
end

defp check_content_type(conn) do
case get_req_header(conn, "content-type") do
["application/json"] ->
# Proceed, this is the valid content type for SRH
conn

# Either missing, or a type that we don't support
_ ->
# Return a custom error, ensuring the same format as the other errors
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Jason.encode!(%{error: "Invalid content type. Expected application/json."}))
|> halt()
end
end
end