mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-28 00:00:20 +02:00
Backport impl tower::Layer for Extension (#805)
* Implement `tower::Layer` for `Extension` (#801) * Implement `tower::Layer` for `Extension` * changelog * Deprecate `AddExtensionLayer` * changelog * Add missing #[allow(deprecated)]
This commit is contained in:
+5
-1
@@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
- None.
|
- **added:** Implement `tower::Layer` for `Extension` ([#801])
|
||||||
|
- **changed:** Deprecate `AddExtensionLayer`. Use `Extension` instead ([#805])
|
||||||
|
|
||||||
|
[#801]: https://github.com/tokio-rs/axum/pull/801
|
||||||
|
[#805]: https://github.com/tokio-rs/axum/pull/805
|
||||||
|
|
||||||
# 0.4.6 (22. February, 2022)
|
# 0.4.6 (22. February, 2022)
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,15 @@ use tower_service::Service;
|
|||||||
///
|
///
|
||||||
/// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
|
/// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.4.7",
|
||||||
|
note = "Use `axum::Extension` instead. It implements `tower::Layer`"
|
||||||
|
)]
|
||||||
pub struct AddExtensionLayer<T> {
|
pub struct AddExtensionLayer<T> {
|
||||||
value: T,
|
value: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl<T> AddExtensionLayer<T> {
|
impl<T> AddExtensionLayer<T> {
|
||||||
/// Create a new [`AddExtensionLayer`].
|
/// Create a new [`AddExtensionLayer`].
|
||||||
pub fn new(value: T) -> Self {
|
pub fn new(value: T) -> Self {
|
||||||
@@ -23,6 +28,7 @@ impl<T> AddExtensionLayer<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
impl<S, T> Layer<S> for AddExtensionLayer<T>
|
impl<S, T> Layer<S> for AddExtensionLayer<T>
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
@@ -45,12 +51,17 @@ where
|
|||||||
/// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
|
/// [request extensions]: https://docs.rs/http/latest/http/struct.Extensions.html
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct AddExtension<S, T> {
|
pub struct AddExtension<S, T> {
|
||||||
inner: S,
|
pub(crate) inner: S,
|
||||||
value: T,
|
pub(crate) value: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> AddExtension<S, T> {
|
impl<S, T> AddExtension<S, T> {
|
||||||
/// Create a new [`AddExtensionLayer`].
|
/// Create a new [`AddExtensionLayer`].
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.4.7",
|
||||||
|
note = "Use `axum::Extension` instead. It implements `tower::Layer`"
|
||||||
|
)]
|
||||||
|
#[allow(deprecated)]
|
||||||
pub fn layer(value: T) -> AddExtensionLayer<T> {
|
pub fn layer(value: T) -> AddExtensionLayer<T> {
|
||||||
AddExtensionLayer::new(value)
|
AddExtensionLayer::new(value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
//! [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
||||||
|
|
||||||
use super::{Extension, FromRequest, RequestParts};
|
use super::{Extension, FromRequest, RequestParts};
|
||||||
use crate::{AddExtension, AddExtensionLayer};
|
use crate::AddExtension;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use hyper::server::conn::AddrStream;
|
use hyper::server::conn::AddrStream;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -104,7 +104,7 @@ where
|
|||||||
|
|
||||||
fn call(&mut self, target: T) -> Self::Future {
|
fn call(&mut self, target: T) -> Self::Future {
|
||||||
let connect_info = ConnectInfo(C::connect_info(target));
|
let connect_info = ConnectInfo(C::connect_info(target));
|
||||||
let svc = AddExtensionLayer::new(connect_info).layer(self.svc.clone());
|
let svc = Extension(connect_info).layer(self.svc.clone());
|
||||||
ResponseFuture::new(ready(Ok(svc)))
|
ResponseFuture::new(ready(Ok(svc)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ use std::ops::Deref;
|
|||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use axum::{
|
/// use axum::{
|
||||||
/// AddExtensionLayer,
|
|
||||||
/// extract::Extension,
|
/// extract::Extension,
|
||||||
/// routing::get,
|
/// routing::get,
|
||||||
/// Router,
|
/// Router,
|
||||||
@@ -31,7 +30,7 @@ use std::ops::Deref;
|
|||||||
/// let app = Router::new().route("/", get(handler))
|
/// let app = Router::new().route("/", get(handler))
|
||||||
/// // Add middleware that inserts the state into all incoming request's
|
/// // Add middleware that inserts the state into all incoming request's
|
||||||
/// // extensions.
|
/// // extensions.
|
||||||
/// .layer(AddExtensionLayer::new(state));
|
/// .layer(Extension(state));
|
||||||
/// # async {
|
/// # async {
|
||||||
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||||
/// # };
|
/// # };
|
||||||
@@ -57,7 +56,7 @@ where
|
|||||||
.get::<T>()
|
.get::<T>()
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
MissingExtension::from_err(format!(
|
MissingExtension::from_err(format!(
|
||||||
"Extension of type `{}` was not found. Perhaps you forgot to add it?",
|
"Extension of type `{}` was not found. Perhaps you forgot to add it? See `axum::extract::Extension`.",
|
||||||
std::any::type_name::<T>()
|
std::any::type_name::<T>()
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
@@ -74,3 +73,17 @@ impl<T> Deref for Extension<T> {
|
|||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T> tower_layer::Layer<S> for Extension<T>
|
||||||
|
where
|
||||||
|
T: Clone + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type Service = crate::AddExtension<S, T>;
|
||||||
|
|
||||||
|
fn layer(&self, inner: S) -> Self::Service {
|
||||||
|
crate::AddExtension {
|
||||||
|
inner,
|
||||||
|
value: self.0.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -218,9 +218,10 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
body::Body,
|
body::Body,
|
||||||
|
extract::Extension,
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
test_helpers::*,
|
test_helpers::*,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use http::{Method, Request, StatusCode};
|
use http::{Method, Request, StatusCode};
|
||||||
|
|
||||||
@@ -253,11 +254,7 @@ mod tests {
|
|||||||
parts.extensions.get::<Ext>().unwrap();
|
parts.extensions.get::<Ext>().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = TestClient::new(
|
let client = TestClient::new(Router::new().route("/", get(handler)).layer(Extension(Ext)));
|
||||||
Router::new()
|
|
||||||
.route("/", get(handler))
|
|
||||||
.layer(AddExtensionLayer::new(Ext)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let res = client.get("/").header("x-foo", "123").send().await;
|
let res = client.get("/").header("x-foo", "123").send().await;
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|||||||
+7
-7
@@ -173,13 +173,11 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Using request extensions
|
//! ## Using request extensions
|
||||||
//!
|
//!
|
||||||
//! The easiest way to extract state in handlers is using [`AddExtension`]
|
//! The easiest way to extract state in handlers is using [`Extension`](crate::extract::Extension)
|
||||||
//! middleware (applied with [`AddExtensionLayer`]) and the
|
//! as layer and extractor:
|
||||||
//! [`Extension`](crate::extract::Extension) extractor:
|
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use axum::{
|
//! use axum::{
|
||||||
//! AddExtensionLayer,
|
|
||||||
//! extract::Extension,
|
//! extract::Extension,
|
||||||
//! routing::get,
|
//! routing::get,
|
||||||
//! Router,
|
//! Router,
|
||||||
@@ -194,7 +192,7 @@
|
|||||||
//!
|
//!
|
||||||
//! let app = Router::new()
|
//! let app = Router::new()
|
||||||
//! .route("/", get(handler))
|
//! .route("/", get(handler))
|
||||||
//! .layer(AddExtensionLayer::new(shared_state));
|
//! .layer(Extension(shared_state));
|
||||||
//!
|
//!
|
||||||
//! async fn handler(
|
//! async fn handler(
|
||||||
//! Extension(state): Extension<Arc<State>>,
|
//! Extension(state): Extension<Arc<State>>,
|
||||||
@@ -217,7 +215,6 @@
|
|||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! use axum::{
|
//! use axum::{
|
||||||
//! AddExtensionLayer,
|
|
||||||
//! Json,
|
//! Json,
|
||||||
//! extract::{Extension, Path},
|
//! extract::{Extension, Path},
|
||||||
//! routing::{get, post},
|
//! routing::{get, post},
|
||||||
@@ -408,7 +405,10 @@ pub mod routing;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_helpers;
|
mod test_helpers;
|
||||||
|
|
||||||
pub use add_extension::{AddExtension, AddExtensionLayer};
|
pub use add_extension::AddExtension;
|
||||||
|
#[allow(deprecated)]
|
||||||
|
pub use add_extension::AddExtensionLayer;
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
#[cfg(feature = "headers")]
|
#[cfg(feature = "headers")]
|
||||||
|
|||||||
@@ -102,11 +102,11 @@ use tower_service::Service;
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use axum::{
|
/// use axum::{
|
||||||
/// Router,
|
/// Router,
|
||||||
|
/// extract::Extension,
|
||||||
/// http::{Request, StatusCode},
|
/// http::{Request, StatusCode},
|
||||||
/// routing::get,
|
/// routing::get,
|
||||||
/// response::IntoResponse,
|
/// response::IntoResponse,
|
||||||
/// middleware::{self, Next},
|
/// middleware::{self, Next},
|
||||||
/// AddExtensionLayer,
|
|
||||||
/// };
|
/// };
|
||||||
/// use tower::ServiceBuilder;
|
/// use tower::ServiceBuilder;
|
||||||
///
|
///
|
||||||
@@ -129,7 +129,7 @@ use tower_service::Service;
|
|||||||
/// .route("/", get(|| async { /* ... */ }))
|
/// .route("/", get(|| async { /* ... */ }))
|
||||||
/// .layer(
|
/// .layer(
|
||||||
/// ServiceBuilder::new()
|
/// ServiceBuilder::new()
|
||||||
/// .layer(AddExtensionLayer::new(state))
|
/// .layer(Extension(state))
|
||||||
/// .layer(middleware::from_fn(my_middleware)),
|
/// .layer(middleware::from_fn(my_middleware)),
|
||||||
/// );
|
/// );
|
||||||
/// # let app: Router = app;
|
/// # let app: Router = app;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use axum::{
|
|||||||
extract::Extension,
|
extract::Extension,
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use starwars::{QueryRoot, StarWars, StarWarsSchema};
|
use starwars::{QueryRoot, StarWars, StarWarsSchema};
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ async fn main() {
|
|||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(graphql_playground).post(graphql_handler))
|
.route("/", get(graphql_playground).post(graphql_handler))
|
||||||
.layer(AddExtensionLayer::new(schema));
|
.layer(Extension(schema));
|
||||||
|
|
||||||
println!("Playground: http://localhost:3000");
|
println!("Playground: http://localhost:3000");
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use axum::{
|
|||||||
},
|
},
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use futures::{sink::SinkExt, stream::StreamExt};
|
use futures::{sink::SinkExt, stream::StreamExt};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -39,7 +39,7 @@ async fn main() {
|
|||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.route("/websocket", get(websocket_handler))
|
.route("/websocket", get(websocket_handler))
|
||||||
.layer(AddExtensionLayer::new(app_state));
|
.layer(Extension(app_state));
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use axum::{
|
|||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
AddExtensionLayer, Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@@ -41,7 +41,7 @@ async fn main() {
|
|||||||
.route("/users", post(users_create))
|
.route("/users", post(users_create))
|
||||||
// Add our `user_repo` to all request's extensions so handlers can access
|
// Add our `user_repo` to all request's extensions so handlers can access
|
||||||
// it.
|
// it.
|
||||||
.layer(AddExtensionLayer::new(user_repo));
|
.layer(Extension(user_repo));
|
||||||
|
|
||||||
// Run our application
|
// Run our application
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tower::{BoxError, ServiceBuilder};
|
use tower::{BoxError, ServiceBuilder};
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
add_extension::AddExtensionLayer, auth::RequireAuthorizationLayer,
|
auth::RequireAuthorizationLayer, compression::CompressionLayer, trace::TraceLayer,
|
||||||
compression::CompressionLayer, trace::TraceLayer,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -58,7 +57,7 @@ async fn main() {
|
|||||||
.concurrency_limit(1024)
|
.concurrency_limit(1024)
|
||||||
.timeout(Duration::from_secs(10))
|
.timeout(Duration::from_secs(10))
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.layer(AddExtensionLayer::new(SharedState::default()))
|
.layer(Extension(SharedState::default()))
|
||||||
.into_inner(),
|
.into_inner(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use axum::{
|
|||||||
http::{header::SET_COOKIE, HeaderMap},
|
http::{header::SET_COOKIE, HeaderMap},
|
||||||
response::{IntoResponse, Redirect, Response},
|
response::{IntoResponse, Redirect, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use http::header;
|
use http::header;
|
||||||
use oauth2::{
|
use oauth2::{
|
||||||
@@ -49,8 +49,8 @@ async fn main() {
|
|||||||
.route("/auth/authorized", get(login_authorized))
|
.route("/auth/authorized", get(login_authorized))
|
||||||
.route("/protected", get(protected))
|
.route("/protected", get(protected))
|
||||||
.route("/logout", get(logout))
|
.route("/logout", get(logout))
|
||||||
.layer(AddExtensionLayer::new(store))
|
.layer(Extension(store))
|
||||||
.layer(AddExtensionLayer::new(oauth_client));
|
.layer(Extension(oauth_client));
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||||
tracing::debug!("listening on {}", addr);
|
tracing::debug!("listening on {}", addr);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use axum::{
|
|||||||
extract::Extension,
|
extract::Extension,
|
||||||
http::{uri::Uri, Request, Response},
|
http::{uri::Uri, Request, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use hyper::{client::HttpConnector, Body};
|
use hyper::{client::HttpConnector, Body};
|
||||||
use std::{convert::TryFrom, net::SocketAddr};
|
use std::{convert::TryFrom, net::SocketAddr};
|
||||||
@@ -26,7 +26,7 @@ async fn main() {
|
|||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(handler))
|
.route("/", get(handler))
|
||||||
.layer(AddExtensionLayer::new(client));
|
.layer(Extension(client));
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 4000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 4000));
|
||||||
println!("reverse proxy listening on {}", addr);
|
println!("reverse proxy listening on {}", addr);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use axum::{
|
|||||||
},
|
},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
@@ -38,7 +38,7 @@ async fn main() {
|
|||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(handler))
|
.route("/", get(handler))
|
||||||
.layer(AddExtensionLayer::new(store));
|
.layer(Extension(store));
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||||
tracing::debug!("listening on {}", addr);
|
tracing::debug!("listening on {}", addr);
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use tower::{BoxError, ServiceBuilder};
|
use tower::{BoxError, ServiceBuilder};
|
||||||
use tower_http::{add_extension::AddExtensionLayer, trace::TraceLayer};
|
use tower_http::trace::TraceLayer;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -61,7 +61,7 @@ async fn main() {
|
|||||||
}))
|
}))
|
||||||
.timeout(Duration::from_secs(10))
|
.timeout(Duration::from_secs(10))
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
.layer(AddExtensionLayer::new(db))
|
.layer(Extension(db))
|
||||||
.into_inner(),
|
.into_inner(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use axum::{
|
|||||||
extract::{Extension, FromRequest, RequestParts},
|
extract::{Extension, FromRequest, RequestParts},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
routing::get,
|
routing::get,
|
||||||
AddExtensionLayer, Router,
|
Router,
|
||||||
};
|
};
|
||||||
use bb8::{Pool, PooledConnection};
|
use bb8::{Pool, PooledConnection};
|
||||||
use bb8_postgres::PostgresConnectionManager;
|
use bb8_postgres::PostgresConnectionManager;
|
||||||
@@ -36,7 +36,7 @@ async fn main() {
|
|||||||
"/",
|
"/",
|
||||||
get(using_connection_pool_extractor).post(using_connection_extractor),
|
get(using_connection_pool_extractor).post(using_connection_extractor),
|
||||||
)
|
)
|
||||||
.layer(AddExtensionLayer::new(pool));
|
.layer(Extension(pool));
|
||||||
|
|
||||||
// run it with hyper
|
// run it with hyper
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||||
|
|||||||
Reference in New Issue
Block a user