Skip to content

Commit 415edb8

Browse files
bjgillwing328
authored andcommitted
[rust-server] Enhance middleware support (+ perf fix) (#8114)
* Revert change to use a new hyper client on every request * Fix some formatting * Update sample after fixing formatting * Add constant with API version * Use semver::Version for ApiVersion * Go back to API version as a string * Rust composite services * added context type parameter to API trait * use Has<XSpanId> trait * added context type parameter to Service and Client structs * made AuthData in Context an option in client * updated client example using generic contexts * added generic context parameters in server/auth * use ExtendsWith with associated types * added (fake) X-Span-ID in auth::Service * updated server example with generic contexts * use real X-Span-ID in auth wrapper service and remove from main server code * only require Has<Option<Authorization>> if API has auth methods * tidy up swagger imports * Actually use the version from the swagger file * remove old comments * add AuthData/Authorization requirements only when AuthMethods are present * updated auth to use new version of the Has trait * update example code to use new Has trait * updated examples to use version of AllowAllAuthenticator that consumes AuthData * update examples to use macros for constructing contexts * use new versions of context traits * autogen sample * rename EmpContext to EmptyContext * fix indentation * remove unecessary uses of Context, and rename existing ones to ContextBuilder * replace Has::<T>::get(&context) with (&context as &Has<T>).get() * remove github dependency for swagger-rs * tidy up swagger entry in Cargo.toml * Update to swagger-rs 0.12.0, and remove warning-inducing extra parentheses * Update petstore examples * Bump to swagger-rs 0.12.1
1 parent 8ab1cbc commit 415edb8

File tree

22 files changed

+772
-744
lines changed

22 files changed

+772
-744
lines changed

modules/swagger-codegen/src/main/resources/rust-server/Cargo.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ chrono = { version = "0.4", features = ["serde"] }
1919
futures = "0.1"
2020
hyper = {version = "0.11", optional = true}
2121
hyper-tls = {version = "0.1.2", optional = true}
22-
swagger = "0.10.0"
22+
swagger = "0.12.1"
2323

2424
# Not required by example server.
2525
#

modules/swagger-codegen/src/main/resources/rust-server/client-mod.mustache

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use std::collections::{HashMap, BTreeMap};
3838
#[allow(unused_imports)]
3939
use swagger;
4040

41-
use swagger::{Context, ApiError, XSpanId};
41+
use swagger::{ApiError, XSpanId, XSpanIdString, Has, AuthData};
4242

4343
use {Api{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
4444
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
@@ -67,8 +67,7 @@ fn into_base_path(input: &str, correct_scheme: Option<&'static str>) -> Result<S
6767
/// A client that implements the API by making HTTP calls out to a server.
6868
#[derive(Clone)]
6969
pub struct Client {
70-
hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
71-
handle: Arc<Handle>,
70+
hyper_client: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
7271
base_path: String,
7372
}
7473

@@ -173,25 +172,13 @@ impl Client {
173172
where
174173
C: hyper::client::Connect + hyper::client::Service,
175174
{
176-
let hyper_client = {
177-
move |handle: &Handle| -> Box<
178-
hyper::client::Service<
179-
Request = hyper::Request<hyper::Body>,
180-
Response = hyper::Response,
181-
Error = hyper::Error,
182-
Future = hyper::client::FutureResponse,
183-
>,
184-
> {
185-
let connector = connector_fn(handle);
186-
Box::new(hyper::Client::configure().connector(connector).build(
187-
handle,
188-
))
189-
}
190-
};
175+
let connector = connector_fn(&handle);
176+
let hyper_client = Box::new(hyper::Client::configure().connector(connector).build(
177+
&handle,
178+
));
191179
192180
Ok(Client {
193181
hyper_client: Arc::new(hyper_client),
194-
handle: Arc::new(handle),
195182
base_path: into_base_path(base_path, protocol)?,
196183
})
197184
}
@@ -205,22 +192,21 @@ impl Client {
205192
/// The reason for this function's existence is to support legacy test code, which did mocking at the hyper layer.
206193
/// This is not a recommended way to write new tests. If other reasons are found for using this function, they
207194
/// should be mentioned here.
208-
pub fn try_new_with_hyper_client(hyper_client: Arc<Fn(&Handle) -> Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>> + Sync + Send>,
195+
pub fn try_new_with_hyper_client(hyper_client: Arc<Box<hyper::client::Service<Request=hyper::Request<hyper::Body>, Response=hyper::Response, Error=hyper::Error, Future=hyper::client::FutureResponse>>>,
209196
handle: Handle,
210197
base_path: &str)
211198
-> Result<Client, ClientInitError>
212199
{
213200
Ok(Client {
214201
hyper_client: hyper_client,
215-
handle: Arc::new(handle),
216202
base_path: into_base_path(base_path, None)?,
217203
})
218204
}
219205
}
220206

221-
impl Api for Client {
207+
impl<C> Api<C> for Client where C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<AuthData>>{{/hasAuthMethods}}{
222208
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
223-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
209+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, param_{{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
224210
{{#queryParams}}{{#-first}}
225211
// Query parameters
226212
{{/-first}}{{#required}} let query_{{paramName}} = format!("{{baseName}}={{=<% %>=}}{<% paramName %>}<%={{ }}=%>&", {{paramName}}=param_{{paramName}}{{#isListContainer}}.join(","){{/isListContainer}}{{^isListContainer}}.to_string(){{/isListContainer}});
@@ -306,9 +292,9 @@ impl Api for Client {
306292

307293
request.headers_mut().set(ContentType(mimetypes::requests::{{#vendorExtensions}}{{uppercase_operation_id}}{{/vendorExtensions}}.clone()));
308294
{{/bodyParam}}
309-
context.x_span_id.as_ref().map(|header| request.headers_mut().set(XSpanId(header.clone())));
310-
{{#authMethods}}{{#isBasic}} context.auth_data.as_ref().map(|auth_data| {
311-
if let &swagger::AuthData::Basic(ref basic_header) = auth_data {
295+
request.headers_mut().set(XSpanId((context as &Has<XSpanIdString>).get().0.clone()));
296+
{{#authMethods}}{{#isBasic}} (context as &Has<Option<AuthData>>).get().as_ref().map(|auth_data| {
297+
if let &AuthData::Basic(ref basic_header) = auth_data {
312298
request.headers_mut().set(hyper::header::Authorization(
313299
basic_header.clone(),
314300
))
@@ -326,8 +312,7 @@ impl Api for Client {
326312
request.set_body(body_string.into_bytes());
327313
{{/hasFile}}{{/vendorExtensions}}
328314

329-
let hyper_client = (self.hyper_client)(&*self.handle);
330-
Box::new(hyper_client.call(request)
315+
Box::new(self.hyper_client.call(request)
331316
.map_err(|e| ApiError(format!("No response received: {}", e)))
332317
.and_then(|mut response| {
333318
match response.status().as_u16() {

modules/swagger-codegen/src/main/resources/rust-server/example-client.mustache

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ extern crate {{externCrateName}};
44
#[allow(unused_extern_crates)]
55
extern crate futures;
66
#[allow(unused_extern_crates)]
7+
#[macro_use]
78
extern crate swagger;
89
#[allow(unused_extern_crates)]
910
extern crate uuid;
1011
extern crate clap;
1112
extern crate tokio_core;
1213

14+
use swagger::{ContextBuilder, EmptyContext, XSpanIdString, Has, Push, AuthData};
15+
1316
#[allow(unused_imports)]
1417
use futures::{Future, future, Stream, stream};
1518
use tokio_core::reactor;
@@ -60,15 +63,16 @@ fn main() {
6063
.expect("Failed to create HTTP client")
6164
};
6265

63-
// Using a non-default `Context` is not required; this is just an example!
64-
let client = client.with_context({{externCrateName}}::Context::new_with_span_id(self::uuid::Uuid::new_v4().to_string()));
66+
let context: make_context_ty!(ContextBuilder, EmptyContext, Option<AuthData>, XSpanIdString) =
67+
make_context!(ContextBuilder, EmptyContext, None, XSpanIdString(self::uuid::Uuid::new_v4().to_string()));
68+
let client = client.with_context(context);
6569

6670
match matches.value_of("operation") {
6771
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
6872
{{#vendorExtensions}}{{#noClientExample}}// Disabled because there's no example.
6973
// {{/noClientExample}}Some("{{operationId}}") => {
7074
{{#noClientExample}}// {{/noClientExample}} let result = core.run(client.{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^-first}}, {{/-first}}{{#vendorExtensions}}{{{example}}}{{/vendorExtensions}}{{/allParams}}));
71-
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
75+
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} println!("{:?} (X-Span-ID: {:?})", result, (client.context() as &Has<XSpanIdString>).get().clone());
7276
{{#vendorExtensions}}{{#noClientExample}}// {{/noClientExample}}{{/vendorExtensions}} },
7377
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
7478
_ => {

modules/swagger-codegen/src/main/resources/rust-server/example-server.mustache

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use hyper::server::Http;
2929
use tokio_proto::TcpServer;
3030
use clap::{App, Arg};
3131
use swagger::auth::AllowAllAuthenticator;
32+
use swagger::EmptyContext;
3233

3334
mod server_lib;
3435

@@ -54,9 +55,9 @@ fn main() {
5455
.get_matches();
5556
5657
let service_fn =
57-
{{externCrateName}}::server::auth::NewService::new(
58+
{{externCrateName}}::server::auth::NewService::<_, EmptyContext>::new(
5859
AllowAllAuthenticator::new(
59-
server_lib::NewService,
60+
server_lib::NewService::new(),
6061
"cosmo"
6162
)
6263
);

modules/swagger-codegen/src/main/resources/rust-server/example-server_lib.mustache

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,31 @@ mod errors {
88

99
pub use self::errors::*;
1010
use std::io;
11+
use std::clone::Clone;
12+
use std::marker::PhantomData;
1113
use hyper;
1214
use {{externCrateName}};
15+
use swagger::{Has, XSpanIdString};
16+
use swagger::auth::Authorization;
1317

14-
pub struct NewService;
18+
pub struct NewService<C>{
19+
marker: PhantomData<C>
20+
}
21+
22+
impl<C> NewService<C>{
23+
pub fn new() -> Self {
24+
NewService{marker:PhantomData}
25+
}
26+
}
1527

16-
impl hyper::server::NewService for NewService {
17-
type Request = (hyper::Request, {{externCrateName}}::Context);
28+
impl<C> hyper::server::NewService for NewService<C> where C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Clone + 'static {
29+
type Request = (hyper::Request, C);
1830
type Response = hyper::Response;
1931
type Error = hyper::Error;
20-
type Instance = {{externCrateName}}::server::Service<server::Server>;
32+
type Instance = {{externCrateName}}::server::Service<server::Server<C>, C>;
2133

2234
/// Instantiate a new server.
2335
fn new_service(&self) -> io::Result<Self::Instance> {
24-
Ok({{externCrateName}}::server::Service::new(server::Server))
36+
Ok({{externCrateName}}::server::Service::new(server::Server::new()))
2537
}
2638
}

modules/swagger-codegen/src/main/resources/rust-server/example-server_server.mustache

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,33 @@ use chrono;
77
{{#apiHasFile}}use futures::Stream;{{/apiHasFile}}
88
use std::collections::HashMap;
99
{{#apiHasFile}}use std::io::Error;{{/apiHasFile}}
10+
use std::marker::PhantomData;
1011
{{#apiUsesUuid}}use uuid;{{/apiUsesUuid}}
1112
use swagger;
13+
use swagger::{Has, XSpanIdString};
1214

13-
use {{externCrateName}}::{Api, ApiError, Context{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
15+
use {{externCrateName}}::{Api, ApiError{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}},
1416
{{operationId}}Response{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
1517
};
1618
use {{externCrateName}}::models;
1719

1820
#[derive(Copy, Clone)]
19-
pub struct Server;
21+
pub struct Server<C> {
22+
marker: PhantomData<C>,
23+
}
24+
25+
impl<C> Server<C> {
26+
pub fn new() -> Self {
27+
Server{marker: PhantomData}
28+
}
29+
}
2030

21-
impl Api for Server {
31+
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString>{
2232
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
2333
{{#summary}} /// {{{summary}}}{{/summary}}
24-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
34+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {
2535
let context = context.clone();
26-
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.x_span_id.unwrap_or(String::from("<none>")).clone());{{#allParams}}{{#isFile}}
36+
println!("{{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}({{#allParams}}{{^isFile}}{{#vendorExtensions}}{{{formatString}}}{{/vendorExtensions}}{{#hasMore}}, {{/hasMore}}{{/isFile}}{{/allParams}}) - X-Span-ID: {:?}"{{#allParams}}{{^isFile}}, {{paramName}}{{/isFile}}{{/allParams}}, context.get().0.clone());{{#allParams}}{{#isFile}}
2737
let _ = {{paramName}}; //Suppresses unused param warning{{/isFile}}{{/allParams}}
2838
Box::new(futures::failed("Generic failure".into()))
2939
}

modules/swagger-codegen/src/main/resources/rust-server/lib.mustache

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub use futures::Future;
3030
#[cfg(any(feature = "client", feature = "server"))]
3131
mod mimetypes;
3232

33-
pub use swagger::{ApiError, Context, ContextWrapper};
33+
pub use swagger::{ApiError, ContextWrapper};
3434

3535
pub const BASE_PATH: &'static str = "{{basePathWithoutHost}}";
3636
pub const API_VERSION: &'static str = "{{appVersion}}";
@@ -48,10 +48,10 @@ pub enum {{operationId}}Response {
4848
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
4949

5050
/// API
51-
pub trait Api {
51+
pub trait Api<C> {
5252
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
5353
{{#summary}} /// {{{summary}}}{{/summary}}
54-
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &Context) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
54+
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}, context: &C) -> Box<Future<Item={{operationId}}Response, Error=ApiError>>;
5555
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
5656
}
5757

@@ -64,18 +64,18 @@ pub trait ApiNoContext {
6464
}
6565

6666
/// Trait to extend an API to make it easy to bind it to a context.
67-
pub trait ContextWrapperExt<'a> where Self: Sized {
67+
pub trait ContextWrapperExt<'a, C> where Self: Sized {
6868
/// Binds this API to a context.
69-
fn with_context(self: &'a Self, context: Context) -> ContextWrapper<'a, Self>;
69+
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C>;
7070
}
7171

72-
impl<'a, T: Api + Sized> ContextWrapperExt<'a> for T {
73-
fn with_context(self: &'a T, context: Context) -> ContextWrapper<'a, T> {
74-
ContextWrapper::<T>::new(self, context)
72+
impl<'a, T: Api<C> + Sized, C> ContextWrapperExt<'a, C> for T {
73+
fn with_context(self: &'a T, context: C) -> ContextWrapper<'a, T, C> {
74+
ContextWrapper::<T, C>::new(self, context)
7575
}
7676
}
7777

78-
impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
78+
impl<'a, T: Api<C>, C> ApiNoContext for ContextWrapper<'a, T, C> {
7979
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}
8080
{{#summary}} /// {{{summary}}}{{/summary}}
8181
fn {{#vendorExtensions}}{{operation_id}}{{/vendorExtensions}}(&self{{#allParams}}, {{paramName}}: {{^required}}{{#isFile}}Box<Future<Item={{/isFile}}Option<{{/required}}{{#isListContainer}}&{{/isListContainer}}{{{dataType}}}{{^required}}>{{#isFile}}, Error=Error> + Send>{{/isFile}}{{/required}}{{/allParams}}) -> Box<Future<Item={{operationId}}Response, Error=ApiError>> {

0 commit comments

Comments
 (0)