Skip to content

Commit

Permalink
feat(Hub): Support custom connectors
Browse files Browse the repository at this point in the history
Switch the constraints on Hub types to use public traits based on
tower::service, as recommended by Hyper. This enables support for
custom connectors beyond hyper_rustls::HttpsConnector

Closes Byron#337.
  • Loading branch information
kylegentle committed Jun 1, 2022
1 parent 0b52431 commit 29a1aeb
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 25 deletions.
3 changes: 3 additions & 0 deletions etc/api/type-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ cargo:
doc_base_url: https://docs.rs
dependencies:
- hyper = "^ 0.14"
- http = "^0.2"
- tokio = "^1.0"
- tower-service = "^0.3.1"
- url = "= 1.7"
2 changes: 2 additions & 0 deletions etc/api/type-cli.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ cargo:
dependencies:
- strsim = "^0.5"
- clap = "^2.0"
- http = "^0.2"
- hyper = { version = "0.14", features = ["full"] }
- tokio = { version = "^ 1.0", features = ["full"] }
- tower-service = "^0.3.1"
11 changes: 8 additions & 3 deletions src/mako/api/api.rs.mako
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ use std::collections::HashMap;
use std::cell::RefCell;
use std::default::Default;
use std::collections::BTreeMap;
use std::error::Error as StdError;
use serde_json as json;
use std::io;
use std::fs;
use std::mem;
use std::thread::sleep;

use http::Uri;
use hyper::client::connect::Connection;
use tokio::io::{AsyncRead, AsyncWrite};
use tower_service::Service;
use crate::client;

// ##############
Expand All @@ -49,8 +54,8 @@ ${lib.hub_usage_example(c)}\
</%block>
#[derive(Clone)]
pub struct ${hub_type}${ht_params} {
pub client: hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>, hyper::body::Body>,
pub auth: oauth2::authenticator::Authenticator<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>>,
pub client: hyper::Client<S, hyper::body::Body>,
pub auth: oauth2::authenticator::Authenticator<S>,
_user_agent: String,
_base_url: String,
_root_url: String,
Expand All @@ -60,7 +65,7 @@ impl<'a, ${', '.join(HUB_TYPE_PARAMETERS)}> client::Hub for ${hub_type}${ht_para

impl<'a, ${', '.join(HUB_TYPE_PARAMETERS)}> ${hub_type}${ht_params} {

pub fn new(client: hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>, hyper::body::Body>, authenticator: oauth2::authenticator::Authenticator<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>>) -> ${hub_type}${ht_params} {
pub fn new(client: hyper::Client<S, hyper::body::Body>, authenticator: oauth2::authenticator::Authenticator<S>) -> ${hub_type}${ht_params} {
${hub_type} {
client,
auth: authenticator,
Expand Down
14 changes: 10 additions & 4 deletions src/mako/api/lib/mbuild.mako
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,13 @@ pub struct ${ThisType}
impl${mb_tparams} ${CALL_BUILDER_MARKERT_TRAIT} for ${ThisType} {}
impl${mb_tparams} ${ThisType} {
impl${mb_tparams} ${ThisType}
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
% if api.get('no_upload_prefix') is not None and ThisType.startswith(api.no_upload_prefix):
${self._action_fn(c, resource, method, m, params, request_value, parts, doit_without_upload = True)}\
% endif
Expand Down Expand Up @@ -179,9 +185,9 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\
/// Usually there is more than one suitable scope to authorize an operation, some of which may
/// encompass more rights than others. For example, for listing resources, a *read-only* scope will be
/// sufficient, a read-write scope will do as well.
pub fn ${ADD_SCOPE_FN}<T, S>(mut self, scope: T) -> ${ThisType}
where T: Into<Option<S>>,
S: AsRef<str> {
pub fn ${ADD_SCOPE_FN}<T, St>(mut self, scope: T) -> ${ThisType}
where T: Into<Option<St>>,
St: AsRef<str> {
match scope.into() {
Some(scope) => self.${api.properties.scopes}.insert(scope.as_ref().to_string(), ()),
None => None,
Expand Down
31 changes: 19 additions & 12 deletions src/mako/cli/lib/engine.mako
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,36 @@ use client::{InvalidOptionsError, CLIError, arg_from_str, writer_from_opts, pars
calltype_from_str, remove_json_null_values, ComplexType, JsonType, JsonTypeInfo};
use std::default::Default;
use std::error::Error as StdError;
use std::str::FromStr;
use serde_json as json;
use clap::ArgMatches;
use http::Uri;
use hyper::client::connect::Connection;
use tokio::io::{AsyncRead, AsyncWrite};
use tower_service::Service;
enum DoitError {
IoError(String, io::Error),
ApiError(Error),
}
struct Engine<'n> {
struct Engine<'n, S> {
opt: ArgMatches<'n>,
hub: ${hub_type_name},
hub: ${hub_type_name}<S>,
gp: ${"Vec<&'static str>"},
gpm: Vec<(&'static str, &'static str)>,
}
impl<'n> Engine<'n> {
impl<'n, S> Engine<'n, S>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
% for resource in sorted(c.rta_map.keys()):
% for method in sorted(c.rta_map[resource]):
async fn ${call_method_ident(resource, method)}(&self, opt: &ArgMatches<'n>, dry_run: bool, err: &mut InvalidOptionsError)
Expand Down Expand Up @@ -102,7 +113,7 @@ impl<'n> Engine<'n> {
}
// Please note that this call will fail if any part of the opt can't be handled
async fn new(opt: ArgMatches<'n>) -> Result<Engine<'n>, InvalidOptionsError> {
async fn new(opt: ArgMatches<'n>, connector: S) -> Result<Engine<'n, S>, InvalidOptionsError> {
let (config_dir, secret) = {
let config_dir = match client::assure_config_dir_exists(opt.value_of("${CONFIG_DIR_ARG}").unwrap_or("${CONFIG_DIR}")) {
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
Expand All @@ -116,18 +127,14 @@ impl<'n> Engine<'n> {
}
};
let auth = oauth2::InstalledFlowAuthenticator::builder(
let client = hyper::Client::builder().build(connector);
let auth = oauth2::InstalledFlowAuthenticator::with_client(
secret,
oauth2::InstalledFlowReturnMethod::HTTPRedirect,
client.clone(),
).persist_tokens_to_disk(format!("{}/${util.program_name()}", config_dir)).build().await.unwrap();
let client = hyper::Client::builder().build(
hyper_rustls::HttpsConnectorBuilder::new().with_native_roots()
.https_or_http()
.enable_http1()
.enable_http2()
.build()
);
<% gpm = gen_global_parameter_names(parameters) %>\
let engine = Engine {
opt: opt,
Expand Down
10 changes: 8 additions & 2 deletions src/mako/cli/main.rs.mako
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ async fn main() {
${argparse.new(c) | indent_all_but_first_by(1)}\
let matches = app.get_matches();

let debug = matches.is_present("${DEBUG_FLAG}");
match Engine::new(matches).await {
let debug = matches.is_present("a${DEBUG_FLAG}");
let connector = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots()
.https_or_http()
.enable_http1()
.enable_http2()
.build();

match Engine::new(matches, connector).await {
Err(err) => {
exit_status = err.exit_code;
writeln!(io::stderr(), "{}", err).ok();
Expand Down
2 changes: 1 addition & 1 deletion src/mako/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
'%': 1,
}

HUB_TYPE_PARAMETERS = ()
HUB_TYPE_PARAMETERS = ('S',)

def items(p):
if isinstance(p, dict):
Expand Down
25 changes: 22 additions & 3 deletions src/rust/api/client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::error;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io::{self, Cursor, Read, Seek, SeekFrom, Write};
use std::str::FromStr;
Expand All @@ -7,7 +8,10 @@ use std::time::Duration;

use itertools::Itertools;

use http::Uri;

use hyper::body::Buf;
use hyper::client::connect::Connection;
use hyper::header::{HeaderMap, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT};
use hyper::Method;
use hyper::StatusCode;
Expand All @@ -16,6 +20,9 @@ use mime::{Attr, Mime, SubLevel, TopLevel, Value};

use serde_json as json;

use tokio::io::{AsyncRead, AsyncWrite};
use tower_service::Service;

const LINE_ENDING: &str = "\r\n";

pub enum Retry {
Expand Down Expand Up @@ -564,9 +571,15 @@ impl RangeResponseHeader {
}

/// A utility type to perform a resumable upload from start to end.
pub struct ResumableUploadHelper<'a, A: 'a> {
pub struct ResumableUploadHelper<'a, A: 'a, S>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
pub client: &'a hyper::client::Client<
hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>,
S,
hyper::body::Body,
>,
pub delegate: &'a mut dyn Delegate,
Expand All @@ -580,7 +593,13 @@ pub struct ResumableUploadHelper<'a, A: 'a> {
pub content_length: u64,
}

impl<'a, A> ResumableUploadHelper<'a, A> {
impl<'a, A, S> ResumableUploadHelper<'a, A, S>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
async fn query_transfer_status(
&mut self,
) -> std::result::Result<u64, hyper::Result<hyper::Response<hyper::body::Body>>> {
Expand Down

0 comments on commit 29a1aeb

Please sign in to comment.