@@ -9,13 +9,27 @@ use std::path::{Component, Path, PathBuf};
9
9
use std:: sync:: Arc ;
10
10
11
11
use futures:: { future, Future , Stream } ;
12
- use futures_cpupool:: CpuPool ;
13
- use hyper:: { Body , Chunk , Method , Request , Response , Server , StatusCode , Version } ;
12
+ use hyper:: { Body , Chunk , Method , Request , Response , StatusCode , Version } ;
14
13
use log:: error;
15
14
16
15
// Consumers of this library need access to this particular version of `semver`
17
16
pub use semver;
18
17
18
+ /// A builder for a `hyper::Server`
19
+ #[ derive( Debug ) ]
20
+ pub struct Server ;
21
+
22
+ impl Server {
23
+ /// Bind a handler to an address
24
+ pub fn bind < H : conduit:: Handler > (
25
+ addr : & SocketAddr ,
26
+ handler : H ,
27
+ ) -> hyper:: Server < hyper:: server:: conn:: AddrIncoming , Service < H > > {
28
+ let service = Service :: new ( handler) ;
29
+ hyper:: Server :: bind ( & addr) . serve ( service)
30
+ }
31
+ }
32
+
19
33
#[ derive( Debug ) ]
20
34
struct Parts ( http:: request:: Parts ) ;
21
35
@@ -67,7 +81,7 @@ struct ConduitRequest {
67
81
parts : Parts ,
68
82
path : String ,
69
83
body : Cursor < Chunk > ,
70
- extensions : conduit:: Extensions ,
84
+ extensions : conduit:: Extensions , // makes struct non-Send
71
85
}
72
86
73
87
impl conduit:: Request for ConduitRequest {
@@ -157,8 +171,34 @@ impl conduit::Request for ConduitRequest {
157
171
}
158
172
}
159
173
174
+ /// Owned data consumed by the worker thread
175
+ ///
176
+ /// `ConduitRequest` cannot be sent between threads, so the input data is
177
+ /// captured on a core thread and taken by the worker thread.
178
+ struct RequestInfo ( Option < ( Parts , Chunk ) > ) ;
179
+
180
+ impl RequestInfo {
181
+ /// Save the request info that can be sent between threads
182
+ fn new ( parts : http:: request:: Parts , body : Chunk ) -> Self {
183
+ let tuple = ( Parts ( parts) , body) ;
184
+ Self ( Some ( tuple) )
185
+ }
186
+
187
+ /// Take back the request info
188
+ ///
189
+ /// Call this from the worker thread to obtain ownership of the `Send` data
190
+ ///
191
+ /// # Panics
192
+ ///
193
+ /// Panics if called more than once on a value
194
+ fn take ( & mut self ) -> ( Parts , Chunk ) {
195
+ self . 0 . take ( ) . expect ( "called take multiple times" )
196
+ }
197
+ }
198
+
160
199
impl ConduitRequest {
161
- fn new ( parts : Parts , body : Chunk ) -> ConduitRequest {
200
+ fn new ( info : & mut RequestInfo ) -> Self {
201
+ let ( parts, body) = info. take ( ) ;
162
202
let path = parts. 0 . uri . path ( ) . to_string ( ) ;
163
203
let path = Path :: new ( & path) ;
164
204
let path = path
@@ -183,7 +223,7 @@ impl ConduitRequest {
183
223
. to_string_lossy ( )
184
224
. to_string ( ) ; // non-Unicode is replaced with U+FFFD REPLACEMENT CHARACTER
185
225
186
- ConduitRequest {
226
+ Self {
187
227
parts,
188
228
path,
189
229
body : Cursor :: new ( body) ,
@@ -195,15 +235,13 @@ impl ConduitRequest {
195
235
/// Serve a `conduit::Handler` on a thread pool
196
236
#[ derive( Debug ) ]
197
237
pub struct Service < H > {
198
- pool : CpuPool ,
199
238
handler : Arc < H > ,
200
239
}
201
240
202
241
// #[derive(Clone)] results in cloning a ref, and not the Service
203
242
impl < H > Clone for Service < H > {
204
243
fn clone ( & self ) -> Self {
205
244
Service {
206
- pool : self . pool . clone ( ) ,
207
245
handler : self . handler . clone ( ) ,
208
246
}
209
247
}
@@ -230,39 +268,32 @@ impl<H: conduit::Handler> hyper::service::Service for Service<H> {
230
268
231
269
/// Returns a future which buffers the response body and then calls the conduit handler from a thread pool
232
270
fn call ( & mut self , request : Request < Self :: ReqBody > ) -> Self :: Future {
233
- let pool = self . pool . clone ( ) ;
234
271
let handler = self . handler . clone ( ) ;
235
272
236
273
let ( parts, body) = request. into_parts ( ) ;
237
274
let response = body. concat2 ( ) . and_then ( move |full_body| {
238
- pool. spawn_fn ( move || {
239
- let mut request = ConduitRequest :: new ( Parts ( parts) , full_body) ;
240
- let response = handler
241
- . call ( & mut request)
242
- . map ( good_response)
243
- . unwrap_or_else ( |e| error_response ( e. description ( ) ) ) ;
244
-
245
- Ok ( response)
275
+ let mut request_info = RequestInfo :: new ( parts, full_body) ;
276
+ future:: poll_fn ( move || {
277
+ tokio_threadpool:: blocking ( || {
278
+ let mut request = ConduitRequest :: new ( & mut request_info) ;
279
+ handler
280
+ . call ( & mut request)
281
+ . map ( good_response)
282
+ . unwrap_or_else ( |e| error_response ( e. description ( ) ) )
283
+ } )
284
+ . map_err ( |_| panic ! ( "the threadpool shut down" ) )
246
285
} )
247
286
} ) ;
248
287
Box :: new ( response)
249
288
}
250
289
}
251
290
252
291
impl < H : conduit:: Handler > Service < H > {
253
- /// Create a multi-threaded `Service` from a `Handler`
254
- pub fn new ( handler : H , threads : usize ) -> Service < H > {
292
+ fn new ( handler : H ) -> Self {
255
293
Service {
256
- pool : CpuPool :: new ( threads) ,
257
294
handler : Arc :: new ( handler) ,
258
295
}
259
296
}
260
-
261
- /// Run the `Service` bound to a given `SocketAddr`
262
- pub fn run ( & self , addr : SocketAddr ) {
263
- let server = Server :: bind ( & addr) . serve ( self . clone ( ) ) ;
264
- hyper:: rt:: run ( server. map_err ( |e| error ! ( "Server error: {}" , e) ) ) ;
265
- }
266
297
}
267
298
268
299
/// Builds a `hyper::Response` given a `conduit:Response`
0 commit comments