|
76 | 76 | }).
|
77 | 77 |
|
78 | 78 | -record(incoming_link, {
|
79 |
| - exchange :: rabbit_exchange:name(), |
| 79 | + exchange :: rabbit_types:exchange() | rabbit_exchange:name(), |
80 | 80 | routing_key :: undefined | rabbit_types:routing_key(),
|
81 | 81 | %% queue_name_bin is only set if the link target address refers to a queue.
|
82 | 82 | queue_name_bin :: undefined | rabbit_misc:resource_name(),
|
@@ -713,9 +713,9 @@ handle_control(#'v1_0.attach'{role = ?SEND_ROLE,
|
713 | 713 | user = User}}) ->
|
714 | 714 | ok = validate_attach(Attach),
|
715 | 715 | case ensure_target(Target, Vhost, User) of
|
716 |
| - {ok, XName, RoutingKey, QNameBin} -> |
| 716 | + {ok, Exchange, RoutingKey, QNameBin} -> |
717 | 717 | IncomingLink = #incoming_link{
|
718 |
| - exchange = XName, |
| 718 | + exchange = Exchange, |
719 | 719 | routing_key = RoutingKey,
|
720 | 720 | queue_name_bin = QNameBin,
|
721 | 721 | delivery_count = DeliveryCountInt,
|
@@ -945,17 +945,14 @@ handle_control(#'v1_0.flow'{handle = Handle} = Flow,
|
945 | 945 | end
|
946 | 946 | end;
|
947 | 947 |
|
948 |
| -handle_control(#'v1_0.detach'{handle = Handle = ?UINT(HandleInt), |
949 |
| - closed = Closed}, |
| 948 | +handle_control(Detach = #'v1_0.detach'{handle = ?UINT(HandleInt)}, |
950 | 949 | State0 = #state{queue_states = QStates0,
|
951 | 950 | incoming_links = IncomingLinks,
|
952 | 951 | outgoing_links = OutgoingLinks0,
|
953 | 952 | outgoing_unsettled_map = Unsettled0,
|
954 | 953 | cfg = #cfg{
|
955 |
| - writer_pid = WriterPid, |
956 | 954 | vhost = Vhost,
|
957 |
| - user = #user{username = Username}, |
958 |
| - channel_num = Ch}}) -> |
| 955 | + user = #user{username = Username}}}) -> |
959 | 956 | Ctag = handle_to_ctag(HandleInt),
|
960 | 957 | %% TODO delete queue if closed flag is set to true? see 2.6.6
|
961 | 958 | %% TODO keep the state around depending on the lifetime
|
@@ -1011,8 +1008,7 @@ handle_control(#'v1_0.detach'{handle = Handle = ?UINT(HandleInt),
|
1011 | 1008 | incoming_links = maps:remove(HandleInt, IncomingLinks),
|
1012 | 1009 | outgoing_links = OutgoingLinks,
|
1013 | 1010 | outgoing_unsettled_map = Unsettled},
|
1014 |
| - rabbit_amqp_writer:send_command(WriterPid, Ch, #'v1_0.detach'{handle = Handle, |
1015 |
| - closed = Closed}), |
| 1011 | + maybe_detach_reply(Detach, State, State0), |
1016 | 1012 | publisher_or_consumer_deleted(State, State0),
|
1017 | 1013 | {noreply, State};
|
1018 | 1014 |
|
@@ -1533,7 +1529,7 @@ incoming_link_transfer(
|
1533 | 1529 | rcv_settle_mode = RcvSettleMode,
|
1534 | 1530 | handle = Handle = ?UINT(HandleInt)},
|
1535 | 1531 | MsgPart,
|
1536 |
| - #incoming_link{exchange = XName = #resource{name = XNameBin}, |
| 1532 | + #incoming_link{exchange = Exchange, |
1537 | 1533 | routing_key = LinkRKey,
|
1538 | 1534 | delivery_count = DeliveryCount0,
|
1539 | 1535 | incoming_unconfirmed_map = U0,
|
@@ -1564,20 +1560,16 @@ incoming_link_transfer(
|
1564 | 1560 | Sections = amqp10_framing:decode_bin(MsgBin),
|
1565 | 1561 | ?DEBUG("~s Inbound content:~n ~tp",
|
1566 | 1562 | [?MODULE, [amqp10_framing:pprint(Section) || Section <- Sections]]),
|
1567 |
| - Anns0 = #{?ANN_EXCHANGE => XNameBin}, |
1568 |
| - Anns = case LinkRKey of |
1569 |
| - undefined -> Anns0; |
1570 |
| - _ -> Anns0#{?ANN_ROUTING_KEYS => [LinkRKey]} |
1571 |
| - end, |
1572 |
| - Mc0 = mc:init(mc_amqp, Sections, Anns), |
1573 |
| - Mc1 = rabbit_message_interceptor:intercept(Mc0), |
1574 |
| - {Mc, RoutingKey} = ensure_routing_key(Mc1), |
1575 |
| - check_user_id(Mc, User), |
1576 |
| - messages_received(Settled), |
1577 |
| - case rabbit_exchange:lookup(XName) of |
1578 |
| - {ok, Exchange} -> |
1579 |
| - check_write_permitted_on_topic(Exchange, User, RoutingKey), |
1580 |
| - QNames = rabbit_exchange:route(Exchange, Mc, #{return_binding_keys => true}), |
| 1563 | + case rabbit_exchange_lookup(Exchange) of |
| 1564 | + {ok, X = #exchange{name = #resource{name = XNameBin}}} -> |
| 1565 | + Anns = #{?ANN_EXCHANGE => XNameBin}, |
| 1566 | + Mc0 = mc:init(mc_amqp, Sections, Anns), |
| 1567 | + {RoutingKey, Mc1} = ensure_routing_key(LinkRKey, Mc0), |
| 1568 | + Mc = rabbit_message_interceptor:intercept(Mc1), |
| 1569 | + check_user_id(Mc, User), |
| 1570 | + messages_received(Settled), |
| 1571 | + check_write_permitted_on_topic(X, User, RoutingKey), |
| 1572 | + QNames = rabbit_exchange:route(X, Mc, #{return_binding_keys => true}), |
1581 | 1573 | rabbit_trace:tap_in(Mc, QNames, ConnName, ChannelNum, Username, Trace),
|
1582 | 1574 | case not Settled andalso
|
1583 | 1575 | RcvSettleMode =:= ?V_1_0_RECEIVER_SETTLE_MODE_SECOND of
|
@@ -1619,19 +1611,29 @@ incoming_link_transfer(
|
1619 | 1611 | {error, [Disposition, Detach]}
|
1620 | 1612 | end.
|
1621 | 1613 |
|
1622 |
| -ensure_routing_key(Mc) -> |
1623 |
| - case mc:routing_keys(Mc) of |
1624 |
| - [RoutingKey] -> |
1625 |
| - {Mc, RoutingKey}; |
1626 |
| - [] -> |
1627 |
| - %% Set the default routing key of AMQP 0.9.1 'basic.publish'{}. |
1628 |
| - %% For example, when the client attached to target /exchange/amq.fanout and sends a |
1629 |
| - %% message without setting a 'subject' in the message properties, the routing key is |
1630 |
| - %% ignored during routing, but receiving code paths still expect some routing key to be set. |
1631 |
| - DefaultRoutingKey = <<"">>, |
1632 |
| - Mc1 = mc:set_annotation(?ANN_ROUTING_KEYS, [DefaultRoutingKey], Mc), |
1633 |
| - {Mc1, DefaultRoutingKey} |
1634 |
| - end. |
| 1614 | +rabbit_exchange_lookup(X = #exchange{}) -> |
| 1615 | + {ok, X}; |
| 1616 | +rabbit_exchange_lookup(XName = #resource{}) -> |
| 1617 | + rabbit_exchange:lookup(XName). |
| 1618 | + |
| 1619 | +ensure_routing_key(LinkRKey, Mc0) -> |
| 1620 | + RKey = case LinkRKey of |
| 1621 | + undefined -> |
| 1622 | + case mc:property(subject, Mc0) of |
| 1623 | + undefined -> |
| 1624 | + %% Set the default routing key of AMQP 0.9.1 'basic.publish'{}. |
| 1625 | + %% For example, when the client attached to target /exchange/amq.fanout and sends a |
| 1626 | + %% message without setting a 'subject' in the message properties, the routing key is |
| 1627 | + %% ignored during routing, but receiving code paths still expect some routing key to be set. |
| 1628 | + <<"">>; |
| 1629 | + {utf8, Subject} -> |
| 1630 | + Subject |
| 1631 | + end; |
| 1632 | + _ -> |
| 1633 | + LinkRKey |
| 1634 | + end, |
| 1635 | + Mc = mc:set_annotation(?ANN_ROUTING_KEYS, [RKey], Mc0), |
| 1636 | + {RKey, Mc}. |
1635 | 1637 |
|
1636 | 1638 | process_routing_confirm([], _SenderSettles = true, _, U) ->
|
1637 | 1639 | rabbit_global_counters:messages_unroutable_dropped(?PROTOCOL, 1),
|
@@ -1692,16 +1694,25 @@ ensure_target(#'v1_0.target'{address = Address,
|
1692 | 1694 | {ok, Dest} ->
|
1693 | 1695 | QNameBin = ensure_terminus(target, Dest, Vhost, User, Durable),
|
1694 | 1696 | {XNameList1, RK} = rabbit_routing_parser:parse_routing(Dest),
|
1695 |
| - XName = rabbit_misc:r(Vhost, exchange, list_to_binary(XNameList1)), |
| 1697 | + XNameBin = list_to_binary(XNameList1), |
| 1698 | + XName = rabbit_misc:r(Vhost, exchange, XNameBin), |
1696 | 1699 | {ok, X} = rabbit_exchange:lookup(XName),
|
1697 | 1700 | check_internal_exchange(X),
|
1698 | 1701 | check_write_permitted(XName, User),
|
| 1702 | + %% Pre-declared exchanges are protected against deletion and modification. |
| 1703 | + %% Let's cache the whole #exchange{} record to save a |
| 1704 | + %% rabbit_exchange:lookup(XName) call each time we receive a message. |
| 1705 | + Exchange = case XNameBin of |
| 1706 | + <<>> -> X; |
| 1707 | + <<"amq.", _/binary>> -> X; |
| 1708 | + _ -> XName |
| 1709 | + end, |
1699 | 1710 | RoutingKey = case RK of
|
1700 | 1711 | undefined -> undefined;
|
1701 | 1712 | [] -> undefined;
|
1702 | 1713 | _ -> list_to_binary(RK)
|
1703 | 1714 | end,
|
1704 |
| - {ok, XName, RoutingKey, QNameBin}; |
| 1715 | + {ok, Exchange, RoutingKey, QNameBin}; |
1705 | 1716 | {error, _} = E ->
|
1706 | 1717 | E
|
1707 | 1718 | end;
|
@@ -2192,6 +2203,22 @@ publisher_or_consumer_deleted(
|
2192 | 2203 | ok
|
2193 | 2204 | end.
|
2194 | 2205 |
|
| 2206 | +%% If we previously already sent a detach with an error condition, and the Detach we |
| 2207 | +%% receive here is therefore the client's reply, do not reply again with a 3rd detach. |
| 2208 | +maybe_detach_reply(Detach, |
| 2209 | + #state{incoming_links = NewIncomingLinks, |
| 2210 | + outgoing_links = NewOutgoingLinks, |
| 2211 | + cfg = #cfg{writer_pid = WriterPid, |
| 2212 | + channel_num = Ch}}, |
| 2213 | + #state{incoming_links = OldIncomingLinks, |
| 2214 | + outgoing_links = OldOutgoingLinks}) |
| 2215 | + when map_size(NewIncomingLinks) < map_size(OldIncomingLinks) orelse |
| 2216 | + map_size(NewOutgoingLinks) < map_size(OldOutgoingLinks) -> |
| 2217 | + Reply = Detach#'v1_0.detach'{error = undefined}, |
| 2218 | + rabbit_amqp_writer:send_command(WriterPid, Ch, Reply); |
| 2219 | +maybe_detach_reply(_, _, _) -> |
| 2220 | + ok. |
| 2221 | + |
2195 | 2222 | check_internal_exchange(#exchange{internal = true,
|
2196 | 2223 | name = XName}) ->
|
2197 | 2224 | protocol_error(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS,
|
|
0 commit comments