Skip to content

Commit

Permalink
chore(volo-http): refactor ClientBuilder, Target and CallOpt (#528
Browse files Browse the repository at this point in the history
)

This commit refactors `ClientBuilder`, `Target` and `CallOpt`.

`ClientBuilder`
- `ClientBuilder::caller_name` and `ClientBuilder::callee_name` are
  renamed to `ClientBuilder::user_agent` and
  `ClientBuilder::default_host`
- `ClientBuilder::build` will add some default layers
  - `Timeout`: Apply current timeout for the request
  - `Host`: `Target::gen_host` is removed and `Host` will not be
    inserted to request headers by default. This layer can insert it
    if there is no `Host`
  - `UserAgent`: Insert `User-Agent` to request headers
- Add `ClientBuilder::build_without_extra_layers` for create a client
  without the above layers
- `ClientBuilder` related methods will not return `Result`, error will
  be recorded and thrown in `build` or `build_without_extra_layers`

`Target`
- Remove `https: bool` field, use `scheme: Scheme` instead
- Remove `TargetParser` and `parse_target`, use `volo::client::Apply`
  instead
- Remove `gen_host`, use `Host` layer instead

`CallOpt`
- `CallOpt` uses the same usage as Volo-Thrift,
  `RequestBuilder::with_callopt` will create a `WithOptService` for
  using it
- Due to the above reason, it will no longer depend on `TargetParser`

Signed-off-by: Yu Li <liyu.yukiteru@bytedance.com>
  • Loading branch information
yukiiiteru authored Nov 11, 2024
1 parent d6af79e commit 9cd838e
Show file tree
Hide file tree
Showing 19 changed files with 1,607 additions and 1,383 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions examples/src/http/example-http-client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ async fn main() -> Result<(), BoxError> {
let client = {
let mut builder = ClientBuilder::new();
builder
.caller_name("example.http.client")
.callee_name("example.http.server")
.user_agent("example.http.client")
.default_host("example.http.server")
// set default target address
.address("127.0.0.1:8080".parse::<SocketAddr>().unwrap())
.header("Test", "Test")?;
builder.build()
.header("Test", "Test");
builder.build()?
};

// set host and override the default one
Expand Down Expand Up @@ -98,7 +98,7 @@ async fn main() -> Result<(), BoxError> {
);

// an empty client
let client = ClientBuilder::new().build();
let client = ClientBuilder::new().build()?;
println!(
"{}",
client
Expand Down
2 changes: 1 addition & 1 deletion volo-http/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "volo-http"
version = "0.3.0-rc.1"
version = "0.3.0-rc.2"
edition.workspace = true
homepage.workspace = true
repository.workspace = true
Expand Down
68 changes: 51 additions & 17 deletions volo-http/src/client/callopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@
//!
//! See [`CallOpt`] for more details.
use std::time::Duration;

use faststr::FastStr;
use metainfo::{FastStrMap, TypeMap};
use volo::{client::Apply, context::Context};

use crate::{context::ClientContext, error::ClientError};

/// Call options for requests
///
/// It can be set to a [`Client`][Client] or a [`RequestBuilder`][RequestBuilder]. The
/// [`TargetParser`][TargetParser] will handle [`Target`][Target] and the [`CallOpt`] for
/// applying information to the [`Endpoint`][Endpoint].
///
/// [Client]: crate::client::Client
/// [RequestBuilder]: crate::client::RequestBuilder
/// [TargetParser]: crate::client::target::TargetParser
/// [Target]: crate::client::target::Target
/// [Endpoint]: volo::context::Endpoint
#[derive(Debug, Default)]
pub struct CallOpt {
/// `tags` is used to store additional information of the endpoint.
/// Timeout of the whole request
///
/// This timeout includes connect, sending request headers, receiving response headers, but
/// without receiving streaming data.
pub timeout: Option<Duration>,
/// Additional information of the endpoint.
///
/// Users can use `tags` to store custom data, such as the datacenter name or the region name,
/// which can be used by the service discoverer.
pub tags: TypeMap,
/// `faststr_tags` is a optimized typemap to store additional information of the endpoint.
/// A optimized typemap for storing additional information of the endpoint.
///
/// Use [`FastStrMap`] instead of [`TypeMap`] can reduce the Box allocation.
///
Expand All @@ -32,13 +32,24 @@ pub struct CallOpt {
}

impl CallOpt {
/// Create a new [`CallOpt`]
/// Create a new [`CallOpt`].
#[inline]
pub fn new() -> Self {
Self::default()
}

/// Check if [`CallOpt`] tags contain entry
/// Set a timeout for the [`CallOpt`].
pub fn set_timeout(&mut self, timeout: Duration) {
self.timeout = Some(timeout);
}

/// Consume current [`CallOpt`] and return a new one with the given timeout.
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}

/// Check if [`CallOpt`] tags contain entry.
#[inline]
pub fn contains<T: 'static>(&self) -> bool {
self.tags.contains::<T>()
Expand All @@ -50,7 +61,7 @@ impl CallOpt {
self.tags.insert(val);
}

/// Insert a tag into this [`CallOpt`] and return self.
/// Consume current [`CallOpt`] and return a new one with the tag.
#[inline]
pub fn with<T: Send + Sync + 'static>(mut self, val: T) -> Self {
self.tags.insert(val);
Expand All @@ -63,7 +74,7 @@ impl CallOpt {
self.tags.get::<T>()
}

/// Check if [`CallOpt`] tags contain entry
/// Check if [`CallOpt`] tags contain entry.
#[inline]
pub fn contains_faststr<T: 'static>(&self) -> bool {
self.faststr_tags.contains::<T>()
Expand All @@ -75,7 +86,7 @@ impl CallOpt {
self.faststr_tags.insert::<T>(val);
}

/// Insert a tag into this [`CallOpt`] and return self.
/// Consume current [`CallOpt`] and return a new one with the tag.
#[inline]
pub fn with_faststr<T: Send + Sync + 'static>(mut self, val: FastStr) -> Self {
self.faststr_tags.insert::<T>(val);
Expand All @@ -88,3 +99,26 @@ impl CallOpt {
self.faststr_tags.get::<T>()
}
}

impl Apply<ClientContext> for CallOpt {
type Error = ClientError;

fn apply(self, cx: &mut ClientContext) -> Result<(), Self::Error> {
{
let callee = cx.rpc_info_mut().callee_mut();
if !self.tags.is_empty() {
callee.tags.extend(self.tags);
}
if !self.faststr_tags.is_empty() {
callee.faststr_tags.extend(self.faststr_tags);
}
}
{
let config = cx.rpc_info_mut().config_mut();
if self.timeout.is_some() {
config.set_timeout(self.timeout);
}
}
Ok(())
}
}
56 changes: 3 additions & 53 deletions volo-http/src/client/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,7 @@ use volo::{
net::Address,
};

use super::{target::RemoteTargetAddress, Target};
#[cfg(feature = "__tls")]
use crate::client::transport::TlsTransport;
use crate::{
client::callopt::CallOpt,
error::client::{bad_host_name, no_address},
utils::consts,
};
use crate::error::client::{bad_host_name, no_address};

/// The port for `DnsResolver`, and only used for `DnsResolver`.
///
Expand All @@ -33,6 +26,7 @@ use crate::{
///
/// For setting port to `DnsResolver`, you can insert it into `Endpoint` of `callee` in
/// `ClientContext`, the resolver will apply it.
#[derive(Clone, Copy, Debug, Default)]
pub struct Port(pub u16);

impl Deref for Port {
Expand Down Expand Up @@ -99,14 +93,7 @@ impl Discover for DnsResolver {
let port = match endpoint.get::<Port>() {
Some(port) => port.0,
None => {
#[cfg(feature = "__tls")]
if endpoint.contains::<TlsTransport>() {
consts::HTTPS_DEFAULT_PORT
} else {
consts::HTTP_DEFAULT_PORT
}
#[cfg(not(feature = "__tls"))]
consts::HTTP_DEFAULT_PORT
unreachable!();
}
};

Expand All @@ -130,40 +117,3 @@ impl Discover for DnsResolver {
None
}
}

/// [`TargetParser`][TargetParser] for parsing [`Target`] and [`CallOpt`] to [`Endpoint`]
///
/// Because [`LoadBalance`][LoadBalance] accepts only [`Endpoint`], but we should create an HTTP
/// target through [`Target`], the [`parse_target`] can parse them and apply to [`Endpoint`] for
/// [LoadBalance] using.
///
/// [TargetParser]: crate::client::target::TargetParser
/// [LoadBalance]: volo::loadbalance::LoadBalance
pub fn parse_target(target: Target, _: Option<&CallOpt>, endpoint: &mut Endpoint) {
match target {
Target::None => (),
Target::Remote(rt) => {
let port = rt.port();

#[cfg(feature = "__tls")]
if rt.is_https() {
endpoint.insert(TlsTransport);
}

match rt.addr {
RemoteTargetAddress::Ip(ip) => {
let sa = SocketAddr::new(ip, port);
endpoint.set_address(Address::Ip(sa));
}
RemoteTargetAddress::Name(host) => {
endpoint.insert(Port(port));
endpoint.set_service_name(host);
}
}
}
#[cfg(target_family = "unix")]
Target::Local(unix_socket) => {
endpoint.set_address(Address::Unix(unix_socket.clone()));
}
}
}
Loading

0 comments on commit 9cd838e

Please sign in to comment.