Blob Blame History Raw
From 7138dd778571334817af74db899f7e1b557e21da Mon Sep 17 00:00:00 2001
From: Simon MacMullen <simon@rabbitmq.com>
Date: Wed, 29 Oct 2014 11:30:59 +0000
Subject: [PATCH] Ensure responses are always application/json, even in error
 cases handled by webamchine.


diff --git a/src/rabbit_webmachine.erl b/src/rabbit_webmachine.erl
index b62e7ad..a199a31 100644
--- a/src/rabbit_webmachine.erl
+++ b/src/rabbit_webmachine.erl
@@ -22,7 +22,8 @@
 -export([makeloop/1, setup/0]).
 
 setup() ->
-    application:set_env(webmachine, error_handler, webmachine_error_handler).
+    application:set_env(
+      webmachine, error_handler, rabbit_webmachine_error_handler).
 
 makeloop(Dispatch) ->
     fun (MochiReq) ->
@@ -33,11 +34,11 @@ makeloop(Dispatch) ->
             %% however, we don't need to dispatch by the host name.
             case webmachine_dispatcher:dispatch(Path, Dispatch, ReqData) of
                 {no_dispatch_match, _Host, _PathElements} ->
-                    {ErrorHTML, ReqState1} =
-                        webmachine_error_handler:render_error(
+                    {ErrorBody, ReqState1} =
+                        rabbit_webmachine_error_handler:render_error(
                           404, Req, {none, none, []}),
                     Req1 = {webmachine_request, ReqState1},
-                    {ok, ReqState2} = Req1:append_to_response_body(ErrorHTML),
+                    {ok, ReqState2} = Req1:append_to_response_body(ErrorBody),
                     Req2 = {webmachine_request, ReqState2},
                     {ok, ReqState3} = Req2:send_response(404),
                     maybe_log_access(ReqState3);
diff --git a/src/rabbit_webmachine_error_handler.erl b/src/rabbit_webmachine_error_handler.erl
new file mode 100644
index 0000000..849e5b9
--- /dev/null
+++ b/src/rabbit_webmachine_error_handler.erl
@@ -0,0 +1,57 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License
+%% at http://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and
+%% limitations under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2010-2014 GoPivotal, Inc.  All rights reserved.
+%%
+
+%% We need to ensure all responses are application/json; anything
+%% coming back as text/html could constitute an XSS vector. Also I'm
+%% sure it's easier on our clients if they can always expect JSON
+%% responses.
+%%
+%% Based on webmachine_error_handler, but I'm not sure enough remains
+%% to be copyrightable.
+
+-module(rabbit_webmachine_error_handler).
+
+-export([render_error/3]).
+
+render_error(Code, Req, Reason) ->
+    case Req:has_response_body() of
+        {true, _}  -> maybe_log(Req, Reason),
+                      Req:response_body();
+        {false, _} -> render_error_body(Code, Req:trim_state(), Reason)
+    end.
+
+render_error_body(404,  Req, Reason) -> error_body(404,  Req, "Not Found");
+render_error_body(Code, Req, Reason) -> error_body(Code, Req, Reason).
+
+error_body(Code, Req, Reason) ->
+    {ok, ReqState} = Req:add_response_header("Content-Type","application/json"),
+    case Code of
+        500 -> maybe_log(Req, Reason);
+        _   -> ok
+    end,
+    Json = {struct,
+            [{error,  list_to_binary(httpd_util:reason_phrase(Code))},
+             {reason, list_to_binary(rabbit_misc:format("~p~n", [Reason]))}]},
+    {mochijson2:encode(Json), ReqState}.
+
+maybe_log(_Req, {error, {exit, normal, _Stack}}) ->
+    %% webmachine_request did an exit(normal), so suppress this
+    %% message. This usually happens when a chunked upload is
+    %% interrupted by network failure.
+    ok;
+maybe_log(Req, Reason) ->
+    {Path, _} = Req:path(),
+    error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]).