diff --git a/deps/rabbitmq_management/LICENSE b/deps/rabbitmq_management/LICENSE index 32fbd2a1e5..824e458d2a 100644 --- a/deps/rabbitmq_management/LICENSE +++ b/deps/rabbitmq_management/LICENSE @@ -1,5 +1,12 @@ This package, the RabbitMQ Management Plugin is licensed under the MPL. For the MPL, please see LICENSE-MPL-RabbitMQ. +This package makes use of the following third party libraries: +jQuery - http://jquery.com/ - MIT license +EJS - http://embeddedjs.com/ - MIT license +Sammy - http://code.quirkey.com/sammy/ - MIT license +webmachine - http://webmachine.basho.com/ - Apache license, 2.0 +mochiweb - http://github.com/mochi/mochiweb/ - MIT license + If you have any questions regarding licensing, please contact us at info@rabbitmq.com. diff --git a/deps/rabbitmq_management/priv/www-api/help.html b/deps/rabbitmq_management/priv/www-api/help.html index 9b42ed4a86..33a7d50995 100644 --- a/deps/rabbitmq_management/priv/www-api/help.html +++ b/deps/rabbitmq_management/priv/www-api/help.html @@ -277,7 +277,7 @@ Content-Length: 0 /api/queues/vhost/name An individual queue. To PUT a queue, you will need a body looking something like this: -
{"auto_delete":false,"durable":true,"arguments":[]}
+
{"auto_delete":false,"durable":true,"arguments":[],"node":"rabbit@smacmullen"}
All keys are optional. @@ -382,7 +382,13 @@ Content-Length: 0 /api/users/name An individual user. To PUT a user, you will need a body looking something like this:
{"password":"secret", "administrator":true}
- All keys are mandatory. +or: +
{"password_hash":"2lmoth8l4H0DViLaK9Fxi6l9ds8=", "administrator":true}
+ The administrator key is mandatory. Either + password or password_hash + must be set. Setting password_hash to "" will ensure the + user cannot use a password to log in. + X diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css index 50d702dcf0..cf0f76a6b8 100644 --- a/deps/rabbitmq_management/priv/www/css/main.css +++ b/deps/rabbitmq_management/priv/www/css/main.css @@ -48,8 +48,11 @@ table.facts th, table.facts td { vertical-align: top; padding: 0 10px 10px 10px; table.facts-long th { text-align: right; font-weight: bold; } table.facts-long th, table.facts-long td { vertical-align: top; } -tr.alt1 td { background: #eee; } -tr.alt2 td { background: #fff; } +table.mini th { border: none; padding: 0 2px 2px 2px; text-align: right; } +table.mini td { border: none; padding: 0 2px 2px 2px; } + +tr.alt1>td { background: #eee; } +tr.alt2>td { background: #fff; } td.status div { padding: 5px; text-align: center; border-radius: 5px; -moz-border-radius: 5px; } td.status div.red { background: #F62817; color:white; } @@ -122,12 +125,12 @@ td.binding-endpoint span.arrow { font-size: 200%; } #scratch { display: none; } -tr.alt1 td { +tr.alt1>td { background: -moz-linear-gradient(center top, #f0f0f0 0%,#e0e0e0 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f0f0f0),color-stop(1, #e0e0e0)); } -tr.alt2 td { +tr.alt2>td { background: -moz-linear-gradient(center top, #f8f8f8 0%,#ffffff 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #f8f8f8),color-stop(1, #ffffff)); } diff --git a/deps/rabbitmq_management/priv/www/js/formatters.js b/deps/rabbitmq_management/priv/www/js/formatters.js index a31341db03..02e109a917 100644 --- a/deps/rabbitmq_management/priv/www/js/formatters.js +++ b/deps/rabbitmq_management/priv/www/js/formatters.js @@ -1,6 +1,10 @@ UNKNOWN_REPR = '?'; -DESCRIPTOR_THRESHOLDS=[[0.75, 'red'], - [0.5, 'yellow']]; +FD_THRESHOLDS=[[0.95, 'red'], + [0.8, 'yellow']]; +SOCKETS_THRESHOLDS=[[1.0, 'red'], + [0.8, 'yellow']]; +PROCESS_THRESHOLDS=[[0.75, 'red'], + [0.5, 'yellow']]; MEMORY_THRESHOLDS=[[1.0, 'red']]; function fmt_string(str) { @@ -57,7 +61,7 @@ function fmt_parameters(obj) { } var args = fmt_table_short(obj.arguments); if (args != '') { - res += '

' + args + '

'; + res += args; } return res; } @@ -76,15 +80,14 @@ function fmt_channel_mode(ch) { function fmt_color(r, thresholds) { if (r == undefined) return ''; - if (thresholds == undefined) thresholds = DESCRIPTOR_THRESHOLDS; for (var i in thresholds) { - var threshold = thresholds[i][0]; - var color = thresholds[i][1]; + var threshold = thresholds[i][0]; + var color = thresholds[i][1]; - if (r > threshold) { - return color; - } + if (r >= threshold) { + return color; + } } return 'green'; } @@ -142,19 +145,31 @@ function fmt_download_filename(host) { } function fmt_table_short(table) { + return '' + fmt_table_body(table, ':') + '
'; +} + +function fmt_table_long(table) { + return '' + fmt_table_body(table, '') + + '
'; +} + +function fmt_table_body(table, x) { var res = ''; for (k in table) { - res += k + '=' + table[k] + '
'; + res += '' + k + x + '' + fmt_amqp_value(table[k]) + + ''; } return res; } -function fmt_table_long(table) { - var res = ''; - for (k in table) { - res += ''; +function fmt_amqp_value(val) { + if (val instanceof Array) { + return val.join("
"); + } else if (val instanceof Object) { + return fmt_table_short(val); + } else { + return val; } - return res + '
' + k + '' + table[k] + '
'; } function fmt_uptime(u) { @@ -290,4 +305,4 @@ function fmt_sort(display, sort) { ''; } return '' + prefix + display + ''; -} \ No newline at end of file +} diff --git a/deps/rabbitmq_management/priv/www/js/help.js b/deps/rabbitmq_management/priv/www/js/help.js index bfc0067213..422705be44 100644 --- a/deps/rabbitmq_management/priv/www/js/help.js +++ b/deps/rabbitmq_management/priv/www/js/help.js @@ -46,6 +46,19 @@ HELP = {
Channel is transactional.
\
', + 'file-descriptors': + 'File descriptor count and limit, as reported by the operating system. \ + The count includes network sockets and file handlers.
\ + To optimize disk access RabbitMQ uses as many free descriptors as are \ + available, so the count may safely approach the limit. \ + However, if most of the file descriptors are used by sockets then \ + persister performance will be negatively impacted.', + + 'socket-descriptors': + 'The network sockets count and limit managed by RabbitMQ.
\ + When the limit is exhausted RabbitMQ will stop accepting new \ + network connections.', + 'foo': 'foo' // No comma. }; diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js index d56b41f98e..bdc7cd8365 100644 --- a/deps/rabbitmq_management/priv/www/js/main.js +++ b/deps/rabbitmq_management/priv/www/js/main.js @@ -143,7 +143,7 @@ function dispatcher() { return false; }); - path('#/queues', {'queues': '/queues', 'vhosts': '/vhosts'}, 'queues'); + path('#/queues', {'queues': '/queues', 'vhosts': '/vhosts', 'nodes': '/nodes'}, 'queues'); this.get('#/queues/:vhost/:name', function() { var path = '/queues/' + esc(this.params['vhost']) + '/' + esc(this.params['name']); render({'queue': path, @@ -411,7 +411,7 @@ function postprocess() { } }); $('#download-configuration').click(function() { - var path = '/api/all-configuration?download=' + + var path = '../api/all-configuration?download=' + esc($('#download-filename').val()); window.location = path; setTimeout('app.run()'); @@ -536,7 +536,7 @@ function toggle_visibility(item) { function with_reqs(reqs, acc, fun) { if (keys(reqs).length > 0) { var key = keys(reqs)[0]; - with_req('/api' + reqs[key], function(resp) { + with_req('../api' + reqs[key], function(resp) { acc[key] = jQuery.parseJSON(resp.responseText); var remainder = {}; for (var k in reqs) { @@ -641,7 +641,7 @@ function sync_req(type, params0, path_template) { return false; } var req = xmlHttpRequest(); - req.open(type, '/api' + path, false); + req.open(type, '../api' + path, false); req.setRequestHeader('content-type', 'application/json'); try { if (type == 'GET') @@ -689,7 +689,8 @@ function fill_path_template(template, params) { } // Better suggestions appreciated -var INTEGER_ARGUMENTS = map(['x-expires']); +var INTEGER_ARGUMENTS = map(['x-expires', 'x-message-ttl']); +var ARRAY_ARGUMENTS = map(['upstreams']); // Used by the federation plugin function params_magic(params) { return maybe_remove_password( @@ -718,6 +719,8 @@ function collapse_multifields(params0) { var v = params0[name + '_' + id + '_mfvalue']; if (k in INTEGER_ARGUMENTS) { v = parseInt(v); + } else if (k in ARRAY_ARGUMENTS) { + v = v.split(" "); } params[name][k] = v; } diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/add-binding.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/add-binding.ejs index f8b3f451c6..52449bcebe 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/add-binding.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/add-binding.ejs @@ -1,6 +1,4 @@ -
-

Add Binding

-
+

Add Binding

@@ -68,5 +66,3 @@
-
-
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs index ea282a4c08..9e4eddfb49 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs @@ -123,7 +123,7 @@

Client Library

-<%= format('table', {'table': connection.client_properties}) %> +<%= fmt_table_long(connection.client_properties) %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs index a82633358c..daf6515274 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/exchange.ejs @@ -53,7 +53,7 @@

Bindings

-
+
<% if (exchange.name == "") { %>

Default exchange

@@ -65,7 +65,7 @@ <% } else { %> <% if (bindings_destination.length > 0) { %>

Incoming to <%= fmt_exchange(exchange.name) %>

- +
<%= format('bindings', {'mode': 'exchange_destination', 'bindings': bindings_destination}) %> @@ -76,9 +76,9 @@
- <% } %> +<% } %>

Outgoing from <%= fmt_exchange(exchange.name) %>

- +
<%= fmt_exchange(exchange.name) %> @@ -89,14 +89,12 @@
+<%= format('add-binding', {'mode': 'exchange_source', 'parent': exchange}) %> <% } %>
<% if (exchange.name != "") { %> - -<%= format('add-binding', {'mode': 'exchange_source', 'parent': exchange}) %> -

Delete This Exchange

diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/node.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/node.ejs index 6b2f9c63ba..4fd83e43c1 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/node.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/node.ejs @@ -6,22 +6,36 @@ + + + + diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs index a3033a3fb9..addd4810b7 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/overview.ejs @@ -34,7 +34,11 @@ + + @@ -148,7 +160,7 @@ for (var i = 0; i < overview.listeners.length; i++) {

Import / Export Configuration

-
+
- File Descriptors + File Descriptors (used / available) -
+
<%= node.fd_used %> / <%= node.fd_total %>
+ Socket Descriptors + (used / available) + +
+ <%= node.sockets_used %> / <%= node.sockets_total %> +
+
Erlang Processes (used / available) -
+
<%= node.proc_used %> / <%= node.proc_total %>
Name - File Descriptors + File Descriptors + (used / available) + + Socket Descriptors (used / available) @@ -74,12 +78,20 @@ <% } else { %> -
+
<%= node.fd_used %> / <%= node.fd_total %>
-
+
+ <%= node.sockets_used %> / <%= node.sockets_total %> +
+
+
<%= node.proc_used %> / <%= node.proc_total %>
+<% if (nodes_interesting) { %> + + + + +<% } %>
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs index b3a10f7b33..87ad536107 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/queue.ejs @@ -69,13 +69,6 @@ -
-

Consumers

-
-<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %> -
-
- <% if (statistics_level == 'fine') { %>

Message Rates

@@ -104,10 +97,17 @@ <% } %>
-

Bindings

+

Consumers

+<%= format('consumers', {'mode': 'queue', 'consumers': queue.consumer_details}) %> +
+
+ +
+

Bindings

+

Incoming to <%= queue.name %>

- +
<%= format('bindings', {'mode': 'queue', 'bindings': bindings}) %> @@ -118,11 +118,12 @@
-
-
<%= format('add-binding', {'mode': 'queue', 'parent': queue}) %> +
+ +

Delete / Purge

@@ -143,10 +144,3 @@
- -
-

Backing Queue Status

-
- <%= fmt_table_long(queue.backing_queue_status) %> -
-
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs index 369ee8888b..b7f8fce2d6 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/queues.ejs @@ -102,6 +102,18 @@
+ +
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs deleted file mode 100644 index cd1a7c9343..0000000000 --- a/deps/rabbitmq_management/priv/www/js/tmpl/table.ejs +++ /dev/null @@ -1,5 +0,0 @@ - -<% for (var key in table) { %> - -<% } %> -
<%= key %><%= table[key] %>
diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_app.erl b/deps/rabbitmq_management/src/rabbit_mgmt_app.erl index 7e697ecdbc..2ef2fdee9c 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_app.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_app.erl @@ -36,7 +36,11 @@ -define(PREFIX, "api"). -define(UI_PREFIX, "mgmt"). -define(CLI_PREFIX, "cli"). +-ifdef(trace). +-define(SETUP_WM_TRACE, true). +-else. -define(SETUP_WM_TRACE, false). +-endif. %% Make sure our database is hooked in *before* listening on the network or %% recovering queues (i.e. so there can't be any events fired before it starts). @@ -61,11 +65,9 @@ stop(_State) -> ok. register_contexts() -> - application:set_env( - webmachine, dispatch_list, - [{[?PREFIX | Path], F, A} || - {Path, F, A} <- rabbit_mgmt_dispatcher:dispatcher()]), - application:set_env(webmachine, error_handler, webmachine_error_handler), + Dispatch = + [{[?PREFIX | Path], F, A} || + {Path, F, A} <- rabbit_mgmt_dispatcher:dispatcher()], rabbit_mochiweb:register_authenticated_static_context( ?UI_PREFIX, ?MODULE, "priv/www", "Management: Web UI", fun (U, P) -> @@ -75,7 +77,8 @@ register_contexts() -> end end), rabbit_mochiweb:register_context_handler(?PREFIX, - fun webmachine_mochiweb:loop/1, + rabbit_webmachine:makeloop( + Dispatch), "Management: HTTP API"), rabbit_mochiweb:register_static_context(?CLI_PREFIX, ?MODULE, "priv/www-cli", @@ -84,22 +87,22 @@ setup_wm_logging() -> {ok, LogDir} = application:get_env(rabbit_management, http_log_dir), case LogDir of none -> - ok; + rabbit_webmachine:setup(none); _ -> - application:set_env(webmachine, webmachine_logger_module, - webmachine_logger), + rabbit_webmachine:setup(webmachine_logger), webmachine_sup:start_logger(LogDir) end. %% This doesn't *entirely* seem to work. It fails to load a non-existent %% image which seems to partly break it, but some stuff is usable. setup_wm_trace_app() -> - webmachine_router:start_link(), - wmtrace_resource:add_dispatch_rule("wmtrace", "/tmp"), + Loop = rabbit_webmachine:makeloop([{["wmtrace", '*'], + wmtrace_resource, + [{trace_dir, "/tmp"}]}]), rabbit_mochiweb:register_static_context( "wmtrace/static", ?MODULE, "deps/webmachine/webmachine/priv/trace", none), rabbit_mochiweb:register_context_handler("wmtrace", - fun webmachine_mochiweb:loop/1, + Loop, "Webmachine tracer"). log_startup() -> {ok, Hostname} = inet:gethostname(), diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl index 8172a90f20..15fff34063 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl @@ -20,7 +20,7 @@ -export([node_and_pid/1, protocol/1, resource/1, permissions/1, queue/1]). -export([exchange/1, user/1, internal_user/1, binding/1, url/2]). -export([pack_binding_props/2, unpack_binding_props/1, tokenise/1]). --export([args_type/1, listener/1, properties/1]). +-export([to_amqp_table/1, listener/1, properties/1]). -include_lib("rabbit_common/include/rabbit.hrl"). @@ -71,8 +71,12 @@ properties(Table) -> {struct, [{Name, tuple(Value)} || {Name, Value} <- Table]}. amqp_table(unknown) -> unknown; -amqp_table(Table) -> {struct, [{Name, tuple(Value)} || - {Name, _Type, Value} <- Table]}. +amqp_table(Table) -> {struct, [{Name, amqp_value(Type, Value)} || + {Name, Type, Value} <- Table]}. + +amqp_value(array, Val) -> [amqp_value(T, V) || {T, V} <- Val]; +amqp_value(table, Val) -> amqp_table(Val); +amqp_value(_Type, Val) -> Val. tuple(unknown) -> unknown; tuple(Tuple) when is_tuple(Tuple) -> [tuple(E) || E <- tuple_to_list(Tuple)]; @@ -156,7 +160,7 @@ unpack_binding_props(Str) -> unpack_binding_props0([Key | Args]) -> try - {unquote_binding(Key), unpack_binding_args(Args)} + {unquote_binding(Key), to_amqp_table(unpack_binding_args(Args))} catch E -> E end; unpack_binding_props0([]) -> @@ -167,8 +171,7 @@ unpack_binding_args([]) -> unpack_binding_args([K]) -> throw({bad_request, {no_value, K}}); unpack_binding_args([K, V | Rest]) -> - Value = unquote_binding(V), - [{unquote_binding(K), args_type(Value), Value} | unpack_binding_args(Rest)]. + [{unquote_binding(K), unquote_binding(V)} | unpack_binding_args(Rest)]. unquote_binding(Name) -> list_to_binary(mochiweb_util:unquote(Name)). @@ -185,6 +188,14 @@ tokenise(Str) -> tokenise(string:sub_string(Str, Count + 2))] end. +to_amqp_table(T) -> + [to_amqp_table_row(K, V) || {K, V} <- T]. + +to_amqp_table_row(K, Vs) when is_list(Vs) -> + {K, array, [{args_type(V), V} || V <- Vs]}; +to_amqp_table_row(K, V) -> + {K, args_type(V), V}. + args_type(X) when is_binary(X) -> longstr; args_type(X) when is_number(X) -> diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl index c108a6d0ca..353d161249 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl @@ -24,6 +24,7 @@ -export([all_or_one_vhost/2, http_to_amqp/5, reply/3, filter_vhost/3]). -export([filter_user/3, with_decode/5, redirect/2, args/1]). -export([reply_list/3, reply_list/4, sort_list/4, destination_type/1]). +-export([relativise/2]). -include("rabbit_mgmt.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). @@ -256,10 +257,15 @@ http_to_amqp(MethodName, ReqData, Context, Transformers, Extra) -> case decode(wrq:req_body(ReqData)) of {ok, Props} -> try - rabbit_mgmt_util:amqp_request( - VHost, ReqData, Context, - props_to_method( - MethodName, Props, Transformers, Extra)) + Node = + case proplists:get_value(<<"node">>, Props) of + undefined -> node(); + N -> rabbit_misc:makenode( + binary_to_list(N)) + end, + amqp_request(VHost, ReqData, Context, Node, + props_to_method( + MethodName, Props, Transformers, Extra)) catch {error, Error} -> bad_request(Error, ReqData, Context) end; @@ -295,12 +301,16 @@ parse_bool(true) -> true; parse_bool(false) -> false; parse_bool(V) -> throw({error, {not_boolean, V}}). +amqp_request(VHost, ReqData, Context, Method) -> + amqp_request(VHost, ReqData, Context, node(), Method). + amqp_request(VHost, ReqData, Context = #context{ user = #user { username = Username }, - password = Password }, Method) -> + password = Password }, Node, Method) -> try - Params = #amqp_params{username = Username, - password = Password, + Params = #amqp_params{username = Username, + password = Password, + node = Node, virtual_host = VHost}, case amqp_connection:start(direct, Params) of {ok, Conn} -> @@ -310,19 +320,23 @@ amqp_request(VHost, ReqData, amqp_connection:close(Conn), {true, ReqData, Context}; {error, auth_failure} -> - not_authorised(<<"">>, ReqData, Context) + not_authorised(<<"">>, ReqData, Context); + {error, {nodedown, N}} -> + bad_request( + list_to_binary( + io_lib:format("Node ~s could not be contacted", [N])), + ReqData, Context) end catch exit:{{server_initiated_close, ?NOT_FOUND, Reason}, _} -> - not_found(list_to_binary(Reason), ReqData, Context); + not_found(Reason, ReqData, Context); exit:{{server_initiated_close, ?ACCESS_REFUSED, Reason}, _} -> - not_authorised(list_to_binary(Reason), ReqData, Context); + not_authorised(Reason, ReqData, Context); exit:{{ServerClose, Code, Reason}, _} when ServerClose =:= server_initiated_close; ServerClose =:= server_initiated_hard_close -> bad_request(list_to_binary(io_lib:format("~p ~s", [Code, Reason])), - ReqData, Context); - E:R -> io:format("~p~n", [{E,R}]) + ReqData, Context) end. all_or_one_vhost(ReqData, Fun) -> @@ -349,4 +363,23 @@ redirect(Location, ReqData) -> args({struct, L}) -> args(L); args(L) -> - [{K, rabbit_mgmt_format:args_type(V), V} || {K, V} <- L]. + rabbit_mgmt_format:to_amqp_table(L). + +relativise("/" ++ F, "/" ++ T) -> + From = string:tokens(F, "/"), + To = string:tokens(T, "/"), + relativise0(From, To). + +relativise0([H], [H|_] = To) -> + string:join(To, "/"); +relativise0([H|From], [H|To]) -> + relativise0(From, To); +relativise0(From, []) -> + relativise(From, [], 0); +relativise0(From, To) -> + relativise(From, To, 1). + +relativise(From, To, Diff) -> + string:join(lists:duplicate(length(From) - Diff, "..") ++ To, "/"). + + diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl index a29c38b072..459f39bb4b 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl @@ -82,12 +82,14 @@ accept_content(ReqData, {_Mode, Context}) -> rabbit_mgmt_util:with_decode( [routing_key, arguments], ReqData, Context, fun([Key, Args]) -> - Loc = binary_to_list( - rabbit_mgmt_format:url( - "/api/bindings/~s/e/~s/~s/~s/~s", - [VHost, Source, DestType, Dest, - rabbit_mgmt_format:pack_binding_props( - Key, rabbit_mgmt_util:args(Args))])), + Loc = rabbit_mgmt_util:relativise( + wrq:path(ReqData), + binary_to_list( + rabbit_mgmt_format:url( + "/api/bindings/~s/e/~s/~s/~s/~s", + [VHost, Source, DestType, Dest, + rabbit_mgmt_format:pack_binding_props( + Key, rabbit_mgmt_util:args(Args))]))), ReqData2 = wrq:set_resp_header("Location", Loc, ReqData), {true, ReqData2, Context2} end) diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl index dbbec6e38a..45006b0ebb 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_test_http.erl @@ -312,7 +312,7 @@ bindings_post_test() -> http_post("/bindings/%2f/e/badexchange/q/myqueue", BArgs, ?NOT_FOUND), http_post("/bindings/%2f/e/myexchange/q/myqueue", [{a, "b"}], ?BAD_REQUEST), Headers = http_post("/bindings/%2f/e/myexchange/q/myqueue", BArgs, ?CREATED), - "/api/bindings/%2F/e/myexchange/q/myqueue/routing_foo_bar" = + "../../../../%2F/e/myexchange/q/myqueue/routing_foo_bar" = pget("location", Headers), [{source,<<"myexchange">>}, {vhost,<<"/">>}, @@ -332,7 +332,7 @@ bindings_e2e_test() -> http_post("/bindings/%2f/e/amq.direct/e/badexchange", BArgs, ?NOT_FOUND), http_post("/bindings/%2f/e/badexchange/e/amq.fanout", BArgs, ?NOT_FOUND), Headers = http_post("/bindings/%2f/e/amq.direct/e/amq.fanout", BArgs, ?CREATED), - "/api/bindings/%2F/e/amq.direct/e/amq.fanout/routing" = + "../../../../%2F/e/amq.direct/e/amq.fanout/routing" = pget("location", Headers), [{source,<<"amq.direct">>}, {vhost,<<"/">>}, @@ -618,6 +618,19 @@ arguments_test() -> http_delete("/queues/%2f/myqueue", ?NO_CONTENT), ok. +arguments_table_test() -> + Args = [{'upstreams', [<<"amqp://localhost/%2f/upstream1">>, + <<"amqp://localhost/%2f/upstream2">>]}], + XArgs = [{type, <<"headers">>}, + {arguments, Args}], + http_put("/exchanges/%2f/myexchange", XArgs, ?NO_CONTENT), + AllConfig = http_get("/all-configuration", ?OK), + http_delete("/exchanges/%2f/myexchange", ?NO_CONTENT), + http_post("/all-configuration", AllConfig, ?NO_CONTENT), + Args = pget(arguments, http_get("/exchanges/%2f/myexchange", ?OK)), + http_delete("/exchanges/%2f/myexchange", ?NO_CONTENT), + ok. + queue_purge_test() -> QArgs = [], http_put("/queues/%2f/myqueue", QArgs, ?NO_CONTENT), diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl b/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl index 82e38ea63d..f9b6d9302c 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_test_unit.erl @@ -48,6 +48,15 @@ pack_binding_test() -> rabbit_mgmt_format:unpack_binding_props(<<"bad_routing">>), ok. +relativise_test() -> + "baz" = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bar/baz"), + "../bax/baz" = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bax/baz"), + "../bax/baz" = rabbit_mgmt_util:relativise("/bar/bash", "/bax/baz"), + ".." = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo/bar"), + "../.." = rabbit_mgmt_util:relativise("/foo/bar/bash", "/foo"), + "bar/baz" = rabbit_mgmt_util:relativise("/foo/bar", "/foo/bar/baz"), + ok. + %%-------------------------------------------------------------------- assert_binding(Packed, Routing, Args) ->