1
1
use crate :: core:: types:: { SettlementAccount , SE_ILP_ADDRESS } ;
2
- use futures:: {
3
- future:: { err, Either } ,
4
- Future , Stream ,
5
- } ;
2
+ use async_trait:: async_trait;
3
+ use futures:: { compat:: Future01CompatExt , TryFutureExt } ;
6
4
use interledger_packet:: { ErrorCode , FulfillBuilder , RejectBuilder } ;
7
- use interledger_service:: { Account , BoxedIlpFuture , IncomingRequest , IncomingService } ;
5
+ use interledger_service:: { Account , IlpResult , IncomingRequest , IncomingService } ;
8
6
use log:: error;
9
- use reqwest:: r#async :: Client ;
7
+ use reqwest:: Client ;
10
8
use std:: marker:: PhantomData ;
11
9
use tokio_retry:: { strategy:: ExponentialBackoff , Retry } ;
12
10
@@ -33,14 +31,13 @@ where
33
31
}
34
32
}
35
33
34
+ #[ async_trait]
36
35
impl < I , A > IncomingService < A > for SettlementMessageService < I , A >
37
36
where
38
37
I : IncomingService < A > + Send ,
39
- A : SettlementAccount + Account ,
38
+ A : SettlementAccount + Account + Send + Sync ,
40
39
{
41
- type Future = BoxedIlpFuture ;
42
-
43
- fn handle_request ( & mut self , request : IncomingRequest < A > ) -> Self :: Future {
40
+ async fn handle_request ( & mut self , request : IncomingRequest < A > ) -> IlpResult {
44
41
// Only handle the request if the destination address matches the ILP address
45
42
// of the settlement engine being used for this account
46
43
if let Some ( settlement_engine_details) = request. from . settlement_engine_details ( ) {
@@ -67,73 +64,94 @@ where
67
64
. header ( "Idempotency-Key" , idempotency_uuid. clone ( ) )
68
65
. body ( message. clone ( ) )
69
66
. send ( )
67
+ . compat ( ) // Wrap to a 0.1 future
70
68
} ;
69
+ // TODO: futures-retry is still not on futures 0.3. As a result, we wrap our action in a
70
+ // 0.1 future, and then wrap the Retry future in a 0.3 future to use async/await.
71
71
72
- return Box :: new ( Retry :: spawn ( ExponentialBackoff :: from_millis ( 10 ) . take ( 10 ) , action)
73
- . map_err ( move |error| {
74
- error ! ( "Error sending message to settlement engine: {:?}" , error) ;
75
- RejectBuilder {
76
- code : ErrorCode :: T00_INTERNAL_ERROR ,
77
- message : b"Error sending message to settlement engine" ,
78
- data : & [ ] ,
79
- triggered_by : Some ( & SE_ILP_ADDRESS ) ,
80
- } . build ( )
81
- } )
82
- . and_then ( move |response| {
83
- let status = response. status ( ) ;
84
- if status. is_success ( ) {
85
- Either :: A ( response. into_body ( ) . concat2 ( ) . map_err ( move |err| {
86
- error ! ( "Error concatenating settlement engine response body: {:?}" , err) ;
72
+ let response = Retry :: spawn ( ExponentialBackoff :: from_millis ( 10 ) . take ( 10 ) , action)
73
+ . compat ( )
74
+ . map_err ( move |error| {
75
+ error ! ( "Error sending message to settlement engine: {:?}" , error) ;
76
+ RejectBuilder {
77
+ code : ErrorCode :: T00_INTERNAL_ERROR ,
78
+ message : b"Error sending message to settlement engine" ,
79
+ data : & [ ] ,
80
+ triggered_by : Some ( & SE_ILP_ADDRESS ) ,
81
+ }
82
+ . build ( )
83
+ } )
84
+ . await ?;
85
+ let status = response. status ( ) ;
86
+ if status. is_success ( ) {
87
+ let body = response
88
+ . bytes ( )
89
+ . map_err ( |err| {
90
+ error ! (
91
+ "Error concatenating settlement engine response body: {:?}" ,
92
+ err
93
+ ) ;
87
94
RejectBuilder {
88
95
code : ErrorCode :: T00_INTERNAL_ERROR ,
89
96
message : b"Error getting settlement engine response" ,
90
97
data : & [ ] ,
91
98
triggered_by : Some ( & SE_ILP_ADDRESS ) ,
92
- } . build ( )
99
+ }
100
+ . build ( )
93
101
} )
94
- . and_then ( |body| {
95
- Ok ( FulfillBuilder {
96
- fulfillment : & PEER_FULFILLMENT ,
97
- data : body. as_ref ( ) ,
98
- } . build ( ) )
99
- } ) )
102
+ . await ?;
103
+
104
+ return Ok ( FulfillBuilder {
105
+ fulfillment : & PEER_FULFILLMENT ,
106
+ data : body. as_ref ( ) ,
107
+ }
108
+ . build ( ) ) ;
109
+ } else {
110
+ error ! (
111
+ "Settlement engine rejected message with HTTP error code: {}" ,
112
+ response. status( )
113
+ ) ;
114
+ let code = if status. is_client_error ( ) {
115
+ ErrorCode :: F00_BAD_REQUEST
100
116
} else {
101
- error ! ( "Settlement engine rejected message with HTTP error code: {}" , response. status( ) ) ;
102
- let code = if status. is_client_error ( ) {
103
- ErrorCode :: F00_BAD_REQUEST
104
- } else {
105
- ErrorCode :: T00_INTERNAL_ERROR
106
- } ;
107
- Either :: B ( err ( RejectBuilder {
108
- code,
109
- message : format ! ( "Settlement engine rejected request with error code: {}" , response. status( ) ) . as_str ( ) . as_ref ( ) ,
110
- data : & [ ] ,
111
- triggered_by : Some ( & SE_ILP_ADDRESS ) ,
112
- } . build ( ) ) )
117
+ ErrorCode :: T00_INTERNAL_ERROR
118
+ } ;
119
+
120
+ return Err ( RejectBuilder {
121
+ code,
122
+ message : format ! (
123
+ "Settlement engine rejected request with error code: {}" ,
124
+ response. status( )
125
+ )
126
+ . as_str ( )
127
+ . as_ref ( ) ,
128
+ data : & [ ] ,
129
+ triggered_by : Some ( & SE_ILP_ADDRESS ) ,
113
130
}
114
- } ) ) ;
131
+ . build ( ) ) ;
132
+ }
115
133
}
116
134
}
117
- Box :: new ( self . next . handle_request ( request) )
135
+ self . next . handle_request ( request) . await
118
136
}
119
137
}
120
138
121
139
#[ cfg( test) ]
122
140
mod tests {
123
141
use super :: * ;
124
142
use crate :: api:: fixtures:: { BODY , DATA , SERVICE_ADDRESS , TEST_ACCOUNT_0 } ;
125
- use crate :: api:: test_helpers:: { block_on , mock_message, test_service} ;
143
+ use crate :: api:: test_helpers:: { mock_message, test_service} ;
126
144
use interledger_packet:: { Address , Fulfill , PrepareBuilder , Reject } ;
127
145
use std:: str:: FromStr ;
128
146
use std:: time:: SystemTime ;
129
147
130
- #[ test]
131
- fn settlement_ok ( ) {
148
+ #[ tokio :: test]
149
+ async fn settlement_ok ( ) {
132
150
// happy case
133
151
let m = mock_message ( 200 ) . create ( ) ;
134
152
let mut settlement = test_service ( ) ;
135
- let fulfill: Fulfill = block_on (
136
- settlement . handle_request ( IncomingRequest {
153
+ let fulfill: Fulfill = settlement
154
+ . handle_request ( IncomingRequest {
137
155
from : TEST_ACCOUNT_0 . clone ( ) ,
138
156
prepare : PrepareBuilder {
139
157
amount : 0 ,
@@ -143,22 +161,22 @@ mod tests {
143
161
execution_condition : & [ 0 ; 32 ] ,
144
162
}
145
163
. build ( ) ,
146
- } ) ,
147
- )
148
- . unwrap ( ) ;
164
+ } )
165
+ . await
166
+ . unwrap ( ) ;
149
167
150
168
m. assert ( ) ;
151
169
assert_eq ! ( fulfill. data( ) , BODY . as_bytes( ) ) ;
152
170
assert_eq ! ( fulfill. fulfillment( ) , & [ 0 ; 32 ] ) ;
153
171
}
154
172
155
- #[ test]
156
- fn gets_forwarded_if_destination_not_engine_ ( ) {
173
+ #[ tokio :: test]
174
+ async fn gets_forwarded_if_destination_not_engine_ ( ) {
157
175
let m = mock_message ( 200 ) . create ( ) . expect ( 0 ) ;
158
176
let mut settlement = test_service ( ) ;
159
177
let destination = Address :: from_str ( "example.some.address" ) . unwrap ( ) ;
160
- let reject: Reject = block_on (
161
- settlement . handle_request ( IncomingRequest {
178
+ let reject: Reject = settlement
179
+ . handle_request ( IncomingRequest {
162
180
from : TEST_ACCOUNT_0 . clone ( ) ,
163
181
prepare : PrepareBuilder {
164
182
amount : 0 ,
@@ -168,24 +186,24 @@ mod tests {
168
186
execution_condition : & [ 0 ; 32 ] ,
169
187
}
170
188
. build ( ) ,
171
- } ) ,
172
- )
173
- . unwrap_err ( ) ;
189
+ } )
190
+ . await
191
+ . unwrap_err ( ) ;
174
192
175
193
m. assert ( ) ;
176
194
assert_eq ! ( reject. code( ) , ErrorCode :: F02_UNREACHABLE ) ;
177
195
assert_eq ! ( reject. triggered_by( ) . unwrap( ) , SERVICE_ADDRESS . clone( ) ) ;
178
196
assert_eq ! ( reject. message( ) , b"No other incoming handler!" as & [ u8 ] , ) ;
179
197
}
180
198
181
- #[ test]
182
- fn account_does_not_have_settlement_engine ( ) {
199
+ #[ tokio :: test]
200
+ async fn account_does_not_have_settlement_engine ( ) {
183
201
let m = mock_message ( 200 ) . create ( ) . expect ( 0 ) ;
184
202
let mut settlement = test_service ( ) ;
185
203
let mut acc = TEST_ACCOUNT_0 . clone ( ) ;
186
204
acc. no_details = true ; // Hide the settlement engine data from the account
187
- let reject: Reject = block_on (
188
- settlement . handle_request ( IncomingRequest {
205
+ let reject: Reject = settlement
206
+ . handle_request ( IncomingRequest {
189
207
from : acc. clone ( ) ,
190
208
prepare : PrepareBuilder {
191
209
amount : 0 ,
@@ -195,25 +213,25 @@ mod tests {
195
213
execution_condition : & [ 0 ; 32 ] ,
196
214
}
197
215
. build ( ) ,
198
- } ) ,
199
- )
200
- . unwrap_err ( ) ;
216
+ } )
217
+ . await
218
+ . unwrap_err ( ) ;
201
219
202
220
m. assert ( ) ;
203
221
assert_eq ! ( reject. code( ) , ErrorCode :: F02_UNREACHABLE ) ;
204
222
assert_eq ! ( reject. triggered_by( ) . unwrap( ) , SERVICE_ADDRESS . clone( ) ) ;
205
223
assert_eq ! ( reject. message( ) , b"No other incoming handler!" ) ;
206
224
}
207
225
208
- #[ test]
209
- fn settlement_engine_rejects ( ) {
226
+ #[ tokio :: test]
227
+ async fn settlement_engine_rejects ( ) {
210
228
// for whatever reason the engine rejects our request with a 500 code
211
229
let error_code = 500 ;
212
230
let error_str = "Internal Server Error" ;
213
231
let m = mock_message ( error_code) . create ( ) ;
214
232
let mut settlement = test_service ( ) ;
215
- let reject: Reject = block_on (
216
- settlement . handle_request ( IncomingRequest {
233
+ let reject: Reject = settlement
234
+ . handle_request ( IncomingRequest {
217
235
from : TEST_ACCOUNT_0 . clone ( ) ,
218
236
prepare : PrepareBuilder {
219
237
amount : 0 ,
@@ -223,9 +241,9 @@ mod tests {
223
241
execution_condition : & [ 0 ; 32 ] ,
224
242
}
225
243
. build ( ) ,
226
- } ) ,
227
- )
228
- . unwrap_err ( ) ;
244
+ } )
245
+ . await
246
+ . unwrap_err ( ) ;
229
247
230
248
m. assert ( ) ;
231
249
assert_eq ! ( reject. code( ) , ErrorCode :: T00_INTERNAL_ERROR ) ;
0 commit comments