@@ -168,6 +168,13 @@ pub enum Replication {
168168 FullyReplicated ,
169169 /// The request is not replicated, i.e. only the node with the given `NodeId` will attempt the http request.
170170 NonReplicated ( NodeId ) ,
171+ /// The request is sent to a committee of nodes that all attempt the http request.
172+ /// The canister receives between `min_responses` and `max_responses` (potentially differing) responses.
173+ Flexible {
174+ committee : BTreeSet < NodeId > ,
175+ min_responses : u32 ,
176+ max_responses : u32 ,
177+ } ,
171178}
172179
173180#[ derive( Clone , Eq , PartialEq , Hash , Debug , Deserialize , Serialize , FromRepr ) ]
@@ -179,15 +186,30 @@ pub enum PricingVersion {
179186
180187impl From < & CanisterHttpRequestContext > for pb_metadata:: CanisterHttpRequestContext {
181188 fn from ( context : & CanisterHttpRequestContext ) -> Self {
182- let replication_type = match context. replication {
189+ let replication_type = match & context. replication {
183190 Replication :: FullyReplicated => {
184191 pb_metadata:: replication:: ReplicationType :: FullyReplicated ( ( ) )
185192 }
186193 Replication :: NonReplicated ( node_id) => {
187194 pb_metadata:: replication:: ReplicationType :: NonReplicated ( node_id_into_protobuf (
188- node_id,
195+ * node_id,
189196 ) )
190197 }
198+ Replication :: Flexible {
199+ committee,
200+ min_responses,
201+ max_responses,
202+ } => pb_metadata:: replication:: ReplicationType :: Flexible (
203+ pb_metadata:: FlexibleReplication {
204+ committee : committee
205+ . iter ( )
206+ . copied ( )
207+ . map ( node_id_into_protobuf)
208+ . collect ( ) ,
209+ min_responses : * min_responses,
210+ max_responses : * max_responses,
211+ } ,
212+ ) ,
191213 } ;
192214
193215 let replication_message = pb_metadata:: Replication {
@@ -269,6 +291,17 @@ impl TryFrom<pb_metadata::CanisterHttpRequestContext> for CanisterHttpRequestCon
269291 Some ( pb_metadata:: replication:: ReplicationType :: NonReplicated ( node_id) ) => {
270292 Replication :: NonReplicated ( node_id_try_from_protobuf ( node_id) ?)
271293 }
294+ Some ( pb_metadata:: replication:: ReplicationType :: Flexible ( flexible) ) => {
295+ Replication :: Flexible {
296+ committee : flexible
297+ . committee
298+ . into_iter ( )
299+ . map ( node_id_try_from_protobuf)
300+ . collect :: < Result < BTreeSet < NodeId > , ProxyDecodeError > > ( ) ?,
301+ min_responses : flexible. min_responses ,
302+ max_responses : flexible. max_responses ,
303+ }
304+ }
272305 None => Replication :: FullyReplicated ,
273306 } ,
274307 None => Replication :: FullyReplicated ,
@@ -1016,43 +1049,55 @@ mod tests {
10161049
10171050 #[ test]
10181051 fn canister_http_request_context_proto_round_trip ( ) {
1019- let initial = CanisterHttpRequestContext {
1020- url : "https://example.com" . to_string ( ) ,
1021- headers : vec ! [ CanisterHttpHeader {
1022- name: "Content-Type" . to_string( ) ,
1023- value: "application/json" . to_string( ) ,
1024- } ] ,
1025- body : Some ( b"{\" hello\" :\" world\" }" . to_vec ( ) ) ,
1026- max_response_bytes : Some ( NumBytes :: from ( 1234 ) ) ,
1027- http_method : CanisterHttpMethod :: POST ,
1028- transform : Some ( Transform {
1029- method_name : "transform_response" . to_string ( ) ,
1030- context : vec ! [ 1 , 2 , 3 ] ,
1031- } ) ,
1032- request : Request {
1033- receiver : CanisterId :: ic_00 ( ) ,
1034- sender : CanisterId :: ic_00 ( ) ,
1035- sender_reply_callback : CallbackId :: from ( 3 ) ,
1036- payment : Cycles :: new ( 10 ) ,
1037- method_name : "transform" . to_string ( ) ,
1038- method_payload : Vec :: new ( ) ,
1039- metadata : Default :: default ( ) ,
1040- deadline : NO_DEADLINE ,
1041- } ,
1042- time : UNIX_EPOCH ,
1043- replication : Replication :: NonReplicated ( node_test_id ( 42 ) ) ,
1044- pricing_version : PricingVersion :: PayAsYouGo ,
1045- refund_status : RefundStatus {
1046- refundable_cycles : Cycles :: new ( 13_000_000 ) ,
1047- per_replica_allowance : Cycles :: new ( 1_000_000 ) ,
1048- refunded_cycles : Cycles :: new ( 123 ) ,
1049- refunding_nodes : BTreeSet :: from ( [ node_test_id ( 1 ) , node_test_id ( 2 ) ] ) ,
1052+ let replications = [
1053+ Replication :: FullyReplicated ,
1054+ Replication :: NonReplicated ( node_test_id ( 42 ) ) ,
1055+ Replication :: Flexible {
1056+ committee : BTreeSet :: from ( [ node_test_id ( 1 ) , node_test_id ( 2 ) , node_test_id ( 3 ) ] ) ,
1057+ min_responses : 2 ,
1058+ max_responses : 3 ,
10501059 } ,
1051- } ;
1060+ ] ;
1061+
1062+ for replication in replications {
1063+ let initial = CanisterHttpRequestContext {
1064+ url : "https://example.com" . to_string ( ) ,
1065+ headers : vec ! [ CanisterHttpHeader {
1066+ name: "Content-Type" . to_string( ) ,
1067+ value: "application/json" . to_string( ) ,
1068+ } ] ,
1069+ body : Some ( b"{\" hello\" :\" world\" }" . to_vec ( ) ) ,
1070+ max_response_bytes : Some ( NumBytes :: from ( 1234 ) ) ,
1071+ http_method : CanisterHttpMethod :: POST ,
1072+ transform : Some ( Transform {
1073+ method_name : "transform_response" . to_string ( ) ,
1074+ context : vec ! [ 1 , 2 , 3 ] ,
1075+ } ) ,
1076+ request : Request {
1077+ receiver : CanisterId :: ic_00 ( ) ,
1078+ sender : CanisterId :: ic_00 ( ) ,
1079+ sender_reply_callback : CallbackId :: from ( 3 ) ,
1080+ payment : Cycles :: new ( 10 ) ,
1081+ method_name : "transform" . to_string ( ) ,
1082+ method_payload : Vec :: new ( ) ,
1083+ metadata : Default :: default ( ) ,
1084+ deadline : NO_DEADLINE ,
1085+ } ,
1086+ time : UNIX_EPOCH ,
1087+ replication,
1088+ pricing_version : PricingVersion :: PayAsYouGo ,
1089+ refund_status : RefundStatus {
1090+ refundable_cycles : Cycles :: new ( 13_000_000 ) ,
1091+ per_replica_allowance : Cycles :: new ( 1_000_000 ) ,
1092+ refunded_cycles : Cycles :: new ( 123 ) ,
1093+ refunding_nodes : BTreeSet :: from ( [ node_test_id ( 1 ) , node_test_id ( 2 ) ] ) ,
1094+ } ,
1095+ } ;
10521096
1053- let pb: pb_metadata:: CanisterHttpRequestContext = ( & initial) . into ( ) ;
1054- let round_trip: CanisterHttpRequestContext = pb. try_into ( ) . unwrap ( ) ;
1055- assert_eq ! ( initial, round_trip) ;
1097+ let pb: pb_metadata:: CanisterHttpRequestContext = ( & initial) . into ( ) ;
1098+ let round_trip: CanisterHttpRequestContext = pb. try_into ( ) . unwrap ( ) ;
1099+ assert_eq ! ( initial, round_trip) ;
1100+ }
10561101 }
10571102
10581103 #[ test]
0 commit comments