Closed mooware closed 4 years ago
I believe it works if the feature is turned on in the awc crate manually. It's missing here: https://github.com/actix/actix-web/blob/61e492e7e31fa1543f475b3cde465c89cc77f3b7/Cargo.toml#L65-L69
fixed in 1.0.6
Just got bitten by Err(Connect(SslIsNotSupported))
too 😐. For posterity, and until this makes it to the documentation [1], please try the following (a.k.a. "enabling the SSL/TLS feature").
[1]: I am willing to open a PR to improve this. Where would you like this documented exactly?
In your Cargo.toml
:
Using rustls
:
[dependencies]
actix-rt = "1.1.1"
actix-web = { version = "3.0.0-alpha.2", features=["rustls"] }
rustls = "0.17.0"
Using openssl
:
[dependencies]
actix-rt = "1.1.1"
actix-web = { version = "3.0.0-alpha.2", features=["openssl"] }
openssl = "0.10.29"
In your main.rs
:
use actix_web::client::Client;
#[actix_rt::main]
async fn main() {
let client = Client::default();
// Create request builder and send request
let response = client
.get("https://www.rust-lang.org") // <--- notice the "s" in "https://..."
.header("User-Agent", "Actix-web")
.send()
.await; // <- Send http request
println!("Response: {:?}", response);
}
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
Running `target/debug/rust-http-playground`
Response: Ok(
ClientResponse HTTP/2.0 200 OK
headers:
"strict-transport-security": "max-age=63072000"
"x-amz-cf-id": "6gculLlBr5lYOjyFkFEErQpKKSY7w2xC70IvsL3ytGEk5HJoJMEMlQ=="
"x-cache": "Miss from cloudfront"
"x-xss-protection": "1; mode=block"
"x-content-type-options": "nosniff"
"via": "1.1 vegur, 1.1 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)"
"x-amz-cf-pop": "NRT20-C4"
"content-security-policy": "default-src 'self'; frame-ancestors 'self'; img-src 'self' avatars.githubusercontent.com; frame-src 'self' player.vimeo.com"
"content-length": "19220"
"referrer-policy": "no-referrer, strict-origin-when-cross-origin"
"vary": "Accept-Encoding"
"server": "Rocket"
"content-type": "text/html; charset=utf-8"
"date": "Fri, 15 May 2020 07:32:08 GMT"
)
Unfortunately doesn't work to me in actix-web 2.0:
2020-05-15T13:42:33.392505900+03:00 DEBUG trust_dns_resolver::async_resolver::background - trust-dns resolver running
2020-05-15T13:42:33.398505100+03:00 DEBUG trust_dns_proto::xfer::dns_handle - querying: www.rust-lang.org A
2020-05-15T13:42:33.399506400+03:00 DEBUG trust_dns_resolver::name_server::name_server - reconnecting: NameServerConfig { socket_addr: V6([fec0:0:0:ffff::1]:53), protocol: Udp, tls_dns_name: None }
2020-05-15T13:42:33.399506400+03:00 DEBUG trust_dns_resolver::name_server::name_server - reconnecting: NameServerConfig { socket_addr: V6([fec0:0:0:ffff::2]:53), protocol: Udp, tls_dns_name: None }
2020-05-15T13:42:33.399506400+03:00 DEBUG trust_dns_resolver::name_server::connection_provider - connecting: Udp { socket_addr: V6([fec0:0:0:ffff::1]:53), timeout: 5s }
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer - enqueueing message: [Query { name: Name { is_fqdn: false, labels: [www, rust-lang, org] }, query_type: A, query_class: IN }]
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_resolver::name_server::connection_provider - connecting: Udp { socket_addr: V6([fec0:0:0:ffff::2]:53), timeout: 5s }
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer - enqueueing message: [Query { name: Name { is_fqdn: false, labels: [www, rust-lang, org] }, query_type: A, query_class: IN }]
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - connection established: UDP([fec0:0:0:ffff::1]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - sending message via: UDP([fec0:0:0:ffff::1]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - all handles closed, shutting down: UDP([fec0:0:0:ffff::1]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - io_stream is done, shutting down
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - connection established: UDP([fec0:0:0:ffff::2]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - sending message via: UDP([fec0:0:0:ffff::2]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - all handles closed, shutting down: UDP([fec0:0:0:ffff::2]:53)
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::xfer::dns_exchange - io_stream is done, shutting down
2020-05-15T13:42:33.400506700+03:00 DEBUG trust_dns_proto::udp::udp_stream - created socket successfully
2020-05-15T13:42:33.401506100+03:00 DEBUG trust_dns_proto::udp::udp_stream - created socket successfully
[src\clients.rs:28] response = Err(
Connect(
Timeout,
),
)
Windows 10 x64
In actix-web 3.0.0-alpha2 I still get Response: Err(Connect(Timeout))
:
Same with "openssl"
I had to use reqwest
insead, it works just fine to me. I'd love to not introduce extra dependencies but builtin client just doesn't work in my scenarios.
Sorry, I didn't get what do you mean. I need to configure something to make it work?
Sorry, I didn't get what do you mean. I need to configure something to make it work?
@Pzixel I was able to configure https requests by following awc_https
example.
// src/main.rs
use actix_web::client::{Client, Connector};
use openssl::ssl::{SslConnector, SslMethod};
#[actix_rt::main]
async fn main() {
let builder = SslConnector::builder(SslMethod::tls()).unwrap();
let client = Client::build()
.connector(Connector::new().ssl(builder.build()).finish())
.finish();
// Create request builder and send request
let response = client
.get("https://www.rust-lang.org") // <--- notice the "s" in "https://..."
.header("User-Agent", "Actix-web")
.send()
.await; // <- Send http request
println!("Response: {:?}", response);
}
Which returns
ClientResponse HTTP/1.1 200 OK
headers:
"strict-transport-security": "max-age=63072000"
"x-amz-cf-id": "nQUn97FTnU4iEG8giZxGeePvVyqrzp8jYsPspK2OvhrtFEMLIrdYiw=="
"x-cache": "Miss from cloudfront"
"x-xss-protection": "1; mode=block"
"x-content-type-options": "nosniff"
"via": "1.1 vegur, 1.1 650962b00c259fe47c193b15b2fe4b88.cloudfront.net (CloudFront)"
"x-amz-cf-pop": "VIE50-C1"
"content-security-policy": "default-src 'self'; frame-ancestors 'self'; img-src 'self' avatars.githubusercontent.com; frame-src 'self' player.vimeo.com"
"content-length": "19220"
"referrer-policy": "no-referrer, strict-origin-when-cross-origin"
"vary": "Accept-Encoding"
"server": "Rocket"
"content-type": "text/html; charset=utf-8"
"date": "Tue, 02 Jun 2020 10:51:59 GMT"
"connection": "keep-alive"
)
Cool, I will try removing reqwest
from my project then. Thanks. BRB with results (although not immediately, I need to switch to the task some moment later)
My first issue is how do I share client
across threads? Arc<Client>
doesn't seem to work:
error[E0277]: `std::rc::Rc<awc::ClientConfig>` cannot be sent between threads safely
--> src\server.rs:28:18
|
28 | let server = HttpServer::new(move || {
| ^^^^^^^^^^^^^^^ `std::rc::Rc<awc::ClientConfig>` cannot be sent between threads safely
|
= help: within `awc::Client`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<awc::ClientConfig>`
= note: required because it appears within the type `awc::Client`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<awc::Client>`
= note: required because it appears within the type `services::auth_service::AuthService`
= note: required because it appears within the type `[closure@src\server.rs:28:34: 82:6 pool:diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>, everlasting_settings_service:&services::company_settings_service::CompanySettingsService, auth_service:services::auth_service::AuthService]`
= note: required by `actix_web::server::HttpServer::<F, I, S, B>::new`
Or I should keep ClientConfig
instead? It seems to be not public after all...
It can't be shared between threads because it wraps an Rc
But I need some hacky way to use them like thread_local. Since Client
is just a newtype over Rc<ClientConfig>
can we have a newtype for Arc<ClientConfig>
or ClientConfig
itself as public struct so I could construct Arc
and then feed it into Client
where I need?
Currently the only option is create a client per request (with entire config) or have thread_static values with client per thread, which doesn't look good either. I think I'd rather stick with reqwest
for a while until it resolves
What's wrong with:
HttpServer::new(move || {
let client = actix_web::client::Client::default();
App::new().data(client)
})
Which makes one client per thread without thread-local hacks.
I'm using it in middleware so it doesn't work like this. See:
pub fn run_server(
application_url: &str,
auth_service: AuthService,
everlasting_settings_service: &'static CompanySettingsService,
pool: Pool<ConnectionManager<PgConnection>>,
) -> io::Result<impl Future<Output = io::Result<()>>> {
let server = HttpServer::new(move || {
let integrations_service = IntegrationService::new(pool.clone());
App::new()
.data(integrations_service)
.data(everlasting_settings_service)
.authorized_route(
API_PREFIX,
web::get().to(handlers::get_about),
auth_service.clone(),
)
.authorized_route(
API_PREFIX.trim_end_matches('/'),
web::get().to(handlers::get_about),
auth_service.clone(),
)
.authorized_route(
&[API_PREFIX, "v1/Integrations"].concat(),
web::get().to(handlers::get_integrations),
auth_service.clone(),
)
...
impl<T, B> AppExt<T, B> for App<T, B>
where
B: MessageBody,
T: ServiceFactory<
Config = (),
Request = ServiceRequest,
Response = ServiceResponse<B>,
Error = actix_web::Error,
InitError = (),
>,
{
fn authorized_route<P: IntoPattern>(
self,
path: P,
route: Route,
auth_service: AuthService,
) -> Self {
let auth = HttpAuthentication::bearer(move |req, c| auth_service.validator(req, &c));
self.service(resource(path).route(route).wrap(auth))
}
}
Maybe somethink like this library could help: https://docs.rs/archery/0.3.0/archery/
Sorry, I didn't get what do you mean. I need to configure something to make it work?
@Pzixel I was able to configure https requests by following
awc_https
example.// src/main.rs use actix_web::client::{Client, Connector}; use openssl::ssl::{SslConnector, SslMethod}; #[actix_rt::main] async fn main() { let builder = SslConnector::builder(SslMethod::tls()).unwrap(); let client = Client::build() .connector(Connector::new().ssl(builder.build()).finish()) .finish(); // Create request builder and send request let response = client .get("https://www.rust-lang.org") // <--- notice the "s" in "https://..." .header("User-Agent", "Actix-web") .send() .await; // <- Send http request println!("Response: {:?}", response); }
Which returns
ClientResponse HTTP/1.1 200 OK headers: "strict-transport-security": "max-age=63072000" "x-amz-cf-id": "nQUn97FTnU4iEG8giZxGeePvVyqrzp8jYsPspK2OvhrtFEMLIrdYiw==" "x-cache": "Miss from cloudfront" "x-xss-protection": "1; mode=block" "x-content-type-options": "nosniff" "via": "1.1 vegur, 1.1 650962b00c259fe47c193b15b2fe4b88.cloudfront.net (CloudFront)" "x-amz-cf-pop": "VIE50-C1" "content-security-policy": "default-src 'self'; frame-ancestors 'self'; img-src 'self' avatars.githubusercontent.com; frame-src 'self' player.vimeo.com" "content-length": "19220" "referrer-policy": "no-referrer, strict-origin-when-cross-origin" "vary": "Accept-Encoding" "server": "Rocket" "content-type": "text/html; charset=utf-8" "date": "Tue, 02 Jun 2020 10:51:59 GMT" "connection": "keep-alive" )
This is strange, but example never work on my machine, i always get something like:
Response on post: Err(
Connect(
Io(
Custom {
kind: Other,
error: "the handshake failed: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:ssl\\statem\\statem_clnt.c:1915:: unable to get local issuer certificate",
},
),
),
)
The only way to make it work is to disable verification completely (which is not a case to work with)
async fn index(_req: HttpRequest) -> HttpResponse {
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
builder.set_verify(SslVerifyMode::NONE);
let client = Client::build()
.connector(Connector::new().ssl(builder.build()).finish())
.finish();
let now = std::time::Instant::now();
let payload =
client
.get("https://upload.wikimedia.org/wikipedia/commons/f/ff/Pizigani_1367_Chart_10MB.jpg")
.send()
.await
.unwrap()
.body()
.limit(20_000_000) // sets max allowable payload size
.await
.unwrap();
Can anybody verify this example works out of the box on his machine? (https://github.com/actix/examples/blob/22c8eaae87775d3da53ea3a73067c1a228a3a3a4/awc_https/src/main.rs#L8-L12)
Also my "rustup show":
stable-x86_64-pc-windows-gnu (default)
rustc 1.43.1 (8d69840ab 2020-05-04)
I using windows 10 x64
I can confirm it, I get the same error on freshly installed windows and OpenSSL:
I can run that example without issues on macOS. Seems it's some bug with openssl on windows?
Actaully @Zhappa or @Pzixel can you open a new issue for this, please.
Is there a rustls example? I am getting connection timeout with rustls. Openssl is just impossible to compile in windows 10. Tried it for a day.
Somebody please share example how to do it with actix_web 3
FWIW, I was able to use openssl client by following Pzixel's comment: https://github.com/actix/actix-web/issues/1045#issuecomment-637457941. I never got it working it actix-web 3 + rustls + macos though.
I was able to successfully call with https in actix_web 4, awc 3.
actix = "0.13.0"
actix-web = "4"
awc = { version = "3.0.0", features = ["openssl"] }
openssl = "0.10.40"
Help, anyone know how to setup TLS SSL / openssl with actix-web on windows 11? thank you
@victormongi see the examples repo.
I tried to use
actix_web::client::Client
to send a request to an https url. It works with the "ssl" feature, but not with the "rust-tls" feature.I condensed it to this sample code:
With "ssl" it works and prints the response headers. With "rust-tls" I get
Connect(SslIsNotSupported)
.