@@ -20,45 +20,49 @@ extern crate jsonrpc_http_server;
20
20
extern crate jsonrpc_macros;
21
21
22
22
use futures:: Future ;
23
-
23
+ use futures :: future :: Either ;
24
24
use jsonrpc_client_http:: HttpTransport ;
25
25
use jsonrpc_core:: { Error , IoHandler } ;
26
26
use jsonrpc_http_server:: ServerBuilder ;
27
+ use std:: time:: Duration ;
28
+ use tokio_core:: reactor:: { Core , Timeout } ;
27
29
28
30
29
31
// Generate server API trait. Actual implementation at bottom of file.
30
32
build_rpc_trait ! {
31
33
pub trait ServerApi {
32
34
#[ rpc( name = "to_upper" ) ]
33
35
fn to_upper( & self , String ) -> Result <String , Error >;
36
+
37
+ #[ rpc( name = "sleep" ) ]
38
+ fn sleep( & self , u64 ) -> Result <( ) , Error >;
34
39
}
35
40
}
36
41
37
42
// Generate client struct with same API as server.
38
- jsonrpc_client ! ( pub struct ToUpperClient {
43
+ jsonrpc_client ! ( pub struct TestClient {
39
44
pub fn to_upper( & mut self , string: & str ) -> RpcRequest <String >;
45
+ pub fn sleep( & mut self , time: u64 ) -> RpcRequest <( ) >;
40
46
} ) ;
41
47
42
48
43
49
#[ test]
44
50
fn localhost_ping_pong ( ) {
45
- env_logger:: init ( ) . unwrap ( ) ;
51
+ let _ = env_logger:: init ( ) ;
46
52
47
53
// Spawn a server hosting the `ServerApi` API.
48
- let server = spawn_server ( ) ;
49
-
50
- let uri = format ! ( "http://{}" , server. address( ) ) ;
54
+ let ( _server, uri) = spawn_server ( ) ;
51
55
println ! ( "Testing towards server at {}" , uri) ;
52
56
53
57
// Create the Tokio Core event loop that will drive the RPC client and the async requests.
54
- let mut core = tokio_core :: reactor :: Core :: new ( ) . unwrap ( ) ;
58
+ let mut core = Core :: new ( ) . unwrap ( ) ;
55
59
56
60
// Create the HTTP transport handle and create a RPC client with that handle.
57
61
let transport = HttpTransport :: shared ( & core. handle ( ) )
58
62
. unwrap ( )
59
63
. handle ( & uri)
60
64
. unwrap ( ) ;
61
- let mut client = ToUpperClient :: new ( transport) ;
65
+ let mut client = TestClient :: new ( transport) ;
62
66
63
67
// Just calling the method gives back a `RpcRequest`, which is a future
64
68
// that can be used to execute the actual RPC call.
@@ -73,6 +77,34 @@ fn localhost_ping_pong() {
73
77
assert_eq ! ( "FOOBAR" , result2) ;
74
78
}
75
79
80
+ #[ test]
81
+ fn dropped_rpc_request_should_not_crash_transport ( ) {
82
+ let _ = env_logger:: init ( ) ;
83
+
84
+ let ( _server, uri) = spawn_server ( ) ;
85
+
86
+ let mut core = Core :: new ( ) . unwrap ( ) ;
87
+ let transport = HttpTransport :: shared ( & core. handle ( ) )
88
+ . unwrap ( )
89
+ . handle ( & uri)
90
+ . unwrap ( ) ;
91
+ let mut client = TestClient :: new ( transport) ;
92
+
93
+ let rpc = client. sleep ( 5 ) . map_err ( |e| e. to_string ( ) ) ;
94
+ let timeout = Timeout :: new ( Duration :: from_millis ( 100 ) , & core. handle ( ) ) . unwrap ( ) ;
95
+ match core. run ( rpc. select2 ( timeout. map_err ( |e| e. to_string ( ) ) ) ) {
96
+ Ok ( Either :: B ( ( ( ) , _rpc) ) ) => ( ) ,
97
+ _ => panic ! ( "The timeout did not finish first" ) ,
98
+ }
99
+
100
+ // Now, sending a second request should still work. This is a regression test catching a
101
+ // previous error where a dropped `RpcRequest` would crash the future running on the event loop.
102
+ match core. run ( client. sleep ( 0 ) ) {
103
+ Ok ( ( ) ) => ( ) ,
104
+ _ => panic ! ( "Sleep did not return as it should" ) ,
105
+ }
106
+ }
107
+
76
108
77
109
/// Simple struct that will implement the RPC API defined at the top of this file.
78
110
struct Server ;
@@ -81,14 +113,22 @@ impl ServerApi for Server {
81
113
fn to_upper ( & self , s : String ) -> Result < String , Error > {
82
114
Ok ( s. to_uppercase ( ) )
83
115
}
116
+
117
+ fn sleep ( & self , time : u64 ) -> Result < ( ) , Error > {
118
+ println ! ( "Sleeping on server" ) ;
119
+ :: std:: thread:: sleep ( Duration :: from_secs ( time) ) ;
120
+ Ok ( ( ) )
121
+ }
84
122
}
85
123
86
- fn spawn_server ( ) -> jsonrpc_http_server:: Server {
124
+ fn spawn_server ( ) -> ( jsonrpc_http_server:: Server , String ) {
87
125
let server = Server ;
88
126
let mut io = IoHandler :: new ( ) ;
89
127
io. extend_with ( server. to_delegate ( ) ) ;
90
128
91
- ServerBuilder :: new ( io)
129
+ let server = ServerBuilder :: new ( io)
92
130
. start_http ( & "127.0.0.1:0" . parse ( ) . unwrap ( ) )
93
- . unwrap ( )
131
+ . unwrap ( ) ;
132
+ let uri = format ! ( "http://{}" , server. address( ) ) ;
133
+ ( server, uri)
94
134
}
0 commit comments