88
99% % mc
1010-export ([
11- init /1 ,
11+ init /2 ,
1212 size /1 ,
1313 x_header /2 ,
1414 routing_headers /2 ,
4848 ]).
4949
5050% % mc implementation
51- init (# content {} = Content0 ) ->
51+ init (# content {} = Content0 , Env ) ->
5252 Content1 = rabbit_binary_parser :ensure_content_decoded (Content0 ),
5353 % % project essential properties into annotations
54- Anns = essential_properties (Content1 ),
54+ Anns = essential_properties (Content1 , Env ),
5555 Content = strip_header (Content1 , ? DELETED_HEADER ),
5656 {Content , Anns }.
5757
@@ -483,7 +483,8 @@ message(#resource{name = ExchangeNameBin}, RoutingKey,
483483 {ok , mc :init (? MODULE ,
484484 Content ,
485485 Anns #{? ANN_ROUTING_KEYS => [RoutingKey | HeaderRoutes ],
486- ? ANN_EXCHANGE => ExchangeNameBin })}
486+ ? ANN_EXCHANGE => ExchangeNameBin },
487+ ? MC_ENV )}
487488 end ;
488489message (# resource {} = XName , RoutingKey ,
489490 # content {} = Content , Anns , false ) ->
@@ -514,75 +515,6 @@ from_basic_message(#basic_message{content = Content,
514515
515516% % Internal
516517
517- deaths_to_headers (Deaths , Headers0 ) ->
518- Infos = case Deaths of
519- # deaths {records = Records } ->
520- % % sort records by the last timestamp
521- List = lists :sort (
522- fun ({_ , # death {anns = #{last_time := L1 }}},
523- {_ , # death {anns = #{last_time := L2 }}}) ->
524- L1 =< L2
525- end , maps :to_list (Records )),
526- lists :foldl (fun (Record , Acc ) ->
527- Table = death_table (Record ),
528- [Table | Acc ]
529- end , [], List );
530- _ ->
531- lists :map (fun death_table /1 , Deaths )
532- end ,
533- rabbit_misc :set_table_value (Headers0 , <<" x-death" >>, array , Infos ).
534-
535- convert_from_amqp_deaths ({array , map , Maps }) ->
536- L = lists :map (
537- fun ({map , KvList }) ->
538- {Ttl , KvList1 } = case KvList of
539- [{{symbol , <<" ttl" >>}, {uint , Ttl0 }} | Tail ] ->
540- {Ttl0 , Tail };
541- _ ->
542- {undefined , KvList }
543- end ,
544- [
545- {{symbol , <<" queue" >>}, {utf8 , Queue }},
546- {{symbol , <<" reason" >>}, {symbol , Reason }},
547- {{symbol , <<" count" >>}, {ulong , Count }},
548- {{symbol , <<" first-time" >>}, {timestamp , FirstTime }},
549- {{symbol , <<" last-time" >>}, {timestamp , _LastTime }},
550- {{symbol , <<" exchange" >>}, {utf8 , Exchange }},
551- {{symbol , <<" routing-keys" >>}, {array , utf8 , RKeys0 }}
552- ] = KvList1 ,
553- RKeys = [Key || {utf8 , Key } <- RKeys0 ],
554- death_table (Queue , Reason , Exchange , RKeys , Count , FirstTime , Ttl )
555- end , Maps ),
556- {true , {<<" x-death" >>, array , L }};
557- convert_from_amqp_deaths (_IgnoreUnknownValue ) ->
558- false .
559-
560- death_table ({{QName , Reason },
561- # death {exchange = Exchange ,
562- routing_keys = RoutingKeys ,
563- count = Count ,
564- anns = DeathAnns = #{first_time := FirstTime }}}) ->
565- death_table (QName , Reason , Exchange , RoutingKeys , Count , FirstTime ,
566- maps :get (ttl , DeathAnns , undefined )).
567-
568- death_table (QName , Reason , Exchange , RoutingKeys , Count , FirstTime , Ttl ) ->
569- L0 = [
570- {<<" count" >>, long , Count },
571- {<<" reason" >>, longstr , rabbit_data_coercion :to_binary (Reason )},
572- {<<" queue" >>, longstr , QName },
573- {<<" time" >>, timestamp , FirstTime div 1000 },
574- {<<" exchange" >>, longstr , Exchange },
575- {<<" routing-keys" >>, array , [{longstr , Key } || Key <- RoutingKeys ]}
576- ],
577- L = case Ttl of
578- undefined ->
579- L0 ;
580- _ ->
581- Expiration = integer_to_binary (Ttl ),
582- [{<<" original-expiration" >>, longstr , Expiration } | L0 ]
583- end ,
584- {table , L }.
585-
586518strip_header (# content {properties = # 'P_basic' {headers = undefined }}
587519 = DecodedContent , _Key ) ->
588520 DecodedContent ;
@@ -732,11 +664,11 @@ message_id({utf8, S}, HKey, H0) ->
732664message_id (undefined , _HKey , H ) ->
733665 {H , undefined }.
734666
735- essential_properties (# content {} = C ) ->
667+ essential_properties (# content {properties = Props }, Env ) ->
736668 # 'P_basic' {delivery_mode = Mode ,
737669 priority = Priority ,
738670 timestamp = TimestampRaw ,
739- headers = Headers } = Props = C # content . properties ,
671+ headers = Headers } = Props ,
740672 {ok , MsgTTL } = rabbit_basic :parse_expiration (Props ),
741673 Timestamp = case TimestampRaw of
742674 undefined ->
@@ -752,6 +684,8 @@ essential_properties(#content{} = C) ->
752684 _ ->
753685 undefined
754686 end ,
687+ Deaths = headers_to_deaths (Headers , Env ),
688+
755689 maps_put_truthy (
756690 ? ANN_PRIORITY , Priority ,
757691 maps_put_truthy (
@@ -762,7 +696,125 @@ essential_properties(#content{} = C) ->
762696 ? ANN_DURABLE , Durable ,
763697 maps_put_truthy (
764698 bcc , BccKeys ,
765- #{}))))).
699+ maps_put_truthy (
700+ deaths , Deaths ,
701+ #{})))))).
702+
703+ headers_to_deaths (_ , #{? FF_MC_DEATHS_V2 := false }) ->
704+ undefined ;
705+ headers_to_deaths (undefined , _ ) ->
706+ undefined ;
707+ headers_to_deaths (Headers , _ ) ->
708+ case lists :keymember (<<" x-death" >>, 1 , Headers ) of
709+ true ->
710+ case rabbit_misc :amqp_table (Headers ) of
711+ #{<<" x-death" >> := XDeathList }
712+ when is_list (XDeathList ) ->
713+ recover_deaths (XDeathList , []);
714+ _ ->
715+ undefined
716+ end ;
717+ false ->
718+ undefined
719+ end .
720+
721+ recover_deaths ([], Acc ) ->
722+ lists :reverse (Acc );
723+ recover_deaths ([Map = #{<<" exchange" >> := Exchange ,
724+ <<" queue" >> := Queue ,
725+ <<" routing-keys" >> := RKeys ,
726+ <<" reason" >> := ReasonBin ,
727+ <<" count" >> := Count ,
728+ <<" time" >> := Ts } | Rem ], Acc0 ) ->
729+ Reason = binary_to_existing_atom (ReasonBin ),
730+ DeathAnns0 = #{first_time => Ts ,
731+ % % Given that this timestamp is absent in the AMQP 0.9.1
732+ % % x-death header, the last_time we set here is incorrect
733+ % % if the message was dead lettered more than one time.
734+ last_time => Ts },
735+ DeathAnns = case Map of
736+ #{<<" original-expiration" >> := Exp } ->
737+ DeathAnns0 #{ttl => binary_to_integer (Exp )};
738+ _ ->
739+ DeathAnns0
740+ end ,
741+ Acc = [{{Queue , Reason },
742+ # death {anns = DeathAnns ,
743+ exchange = Exchange ,
744+ count = Count ,
745+ routing_keys = RKeys }} | Acc0 ],
746+ recover_deaths (Rem , Acc );
747+ recover_deaths ([_IgnoreInvalid | Rem ], Acc ) ->
748+ recover_deaths (Rem , Acc ).
749+
750+ deaths_to_headers (Deaths , Headers0 ) ->
751+ Infos = case Deaths of
752+ # deaths {records = Records } ->
753+ % % sort records by the last timestamp
754+ List = lists :sort (
755+ fun ({_ , # death {anns = #{last_time := L1 }}},
756+ {_ , # death {anns = #{last_time := L2 }}}) ->
757+ L1 =< L2
758+ end , maps :to_list (Records )),
759+ lists :foldl (fun (Record , Acc ) ->
760+ Table = death_table (Record ),
761+ [Table | Acc ]
762+ end , [], List );
763+ _ ->
764+ lists :map (fun death_table /1 , Deaths )
765+ end ,
766+ rabbit_misc :set_table_value (Headers0 , <<" x-death" >>, array , Infos ).
767+
768+ convert_from_amqp_deaths ({array , map , Maps }) ->
769+ L = lists :map (
770+ fun ({map , KvList }) ->
771+ {Ttl , KvList1 } = case KvList of
772+ [{{symbol , <<" ttl" >>}, {uint , Ttl0 }} | Tail ] ->
773+ {Ttl0 , Tail };
774+ _ ->
775+ {undefined , KvList }
776+ end ,
777+ [
778+ {{symbol , <<" queue" >>}, {utf8 , Queue }},
779+ {{symbol , <<" reason" >>}, {symbol , Reason }},
780+ {{symbol , <<" count" >>}, {ulong , Count }},
781+ {{symbol , <<" first-time" >>}, {timestamp , FirstTime }},
782+ {{symbol , <<" last-time" >>}, {timestamp , _LastTime }},
783+ {{symbol , <<" exchange" >>}, {utf8 , Exchange }},
784+ {{symbol , <<" routing-keys" >>}, {array , utf8 , RKeys0 }}
785+ ] = KvList1 ,
786+ RKeys = [Key || {utf8 , Key } <- RKeys0 ],
787+ death_table (Queue , Reason , Exchange , RKeys , Count , FirstTime , Ttl )
788+ end , Maps ),
789+ {true , {<<" x-death" >>, array , L }};
790+ convert_from_amqp_deaths (_IgnoreUnknownValue ) ->
791+ false .
792+
793+ death_table ({{QName , Reason },
794+ # death {exchange = Exchange ,
795+ routing_keys = RoutingKeys ,
796+ count = Count ,
797+ anns = DeathAnns = #{first_time := FirstTime }}}) ->
798+ death_table (QName , Reason , Exchange , RoutingKeys , Count , FirstTime ,
799+ maps :get (ttl , DeathAnns , undefined )).
800+
801+ death_table (QName , Reason , Exchange , RoutingKeys , Count , FirstTime , Ttl ) ->
802+ L0 = [
803+ {<<" count" >>, long , Count },
804+ {<<" reason" >>, longstr , rabbit_data_coercion :to_binary (Reason )},
805+ {<<" queue" >>, longstr , QName },
806+ {<<" time" >>, timestamp , FirstTime div 1000 },
807+ {<<" exchange" >>, longstr , Exchange },
808+ {<<" routing-keys" >>, array , [{longstr , Key } || Key <- RoutingKeys ]}
809+ ],
810+ L = case Ttl of
811+ undefined ->
812+ L0 ;
813+ _ ->
814+ Expiration = integer_to_binary (Ttl ),
815+ [{<<" original-expiration" >>, longstr , Expiration } | L0 ]
816+ end ,
817+ {table , L }.
766818
767819% % headers that are added as annotations during conversions
768820is_internal_header (<<" x-basic-" , _ /binary >>) ->
0 commit comments