I am trying to get this exercise to run but keep getting a (permissions?) error on my AccountSid, which should be correct.
thread 'main' panicked at 'Something went wrong, ResponseError(ResponseContent { status: 401, content: "{\"code\": 20003, \"detail\": \"Your AccountSid or AuthToken was incorrect.\", \"message\": \"Authentication Error - invalid username\", \"more_info\": \"https://www.twilio.com/docs/errors/20003\", \"status\": 401}", entity: Some(UnknownValue(Object({"code": Number(20003), "detail": String("Your AccountSid or AuthToken was incorrect."), "message": String("Authentication Error - invalid username"), "more_info": String("https://www.twilio.com/docs/errors/20003"), "status": Number(401)}))) })'
Initially, I got a "this function takes 21 arguments but 18 arguments were supplied" error but fixed it by supplying the correct amount of None arguments to twilio_api::create_message
After that I get this AccoutSid error.
Here is the walk-through: https://www.twilio.com/docs/openapi/generating-a-rust-client-for-twilios-api
And here is my code for the exercise. I have the .env filled out correctly.
use dotenv;
use openapi::apis::{configuration::Configuration, default_api as twilio_api};
use std::env;
#[tokio::main]
async fn main() {
// Securely import sensitive credentials and values from `.env` instead of inlining their values.
dotenv::dotenv().expect("Failed to read .env file");
let account_sid = env::var("TWILIO_ACCOUNT_SID").expect("Failed to parse Account SID");
let api_key = env::var("TWILIO_API_KEY").expect("Failed to parse API Key");
let api_key_secret = env::var("TWILIO_API_KEY_SECRET").expect("Failed to parse API Key Secret");
let from = env::var("TWILIO_PHONE_NUMBER").expect("Failed to parse 'from' number");
let to = env::var("TO_NUMBER").expect("Failed to parse 'to' number");
// Create a new, default client configuration.
let mut twilio_config = Configuration::default();
// Apply your Basic Auth credentials to the client configuration.
twilio_config.basic_auth = Some((api_key, Some(api_key_secret)));
// Asynchronously send the message "Ahoy, Rustacean! 🦀" to the `to` number from your Twilio phone number.
let message = twilio_api::create_message(
&twilio_config,
&account_sid,
&to,
None,
None,
None,
Some("Ahoy, Rustacean! 🦀"),
None,
None,
Some(&from),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
let result = match message {
Ok(result) => result,
Err(error) => panic!("Something went wrong, {:?}", error),
};
// Once successful, print out the SID of the sent message.
println!("{:?}", result.sid);
}
Just got through the same error:
Install cargo-edit like the doc recommends, and use cargo add to download the crates. This resolved my error.
After that I had another issue with the parameters given in the example code, so I changed it like this:
let body = "SMS test was successful!".to_string();
let message = twilio_api::create_message(
&twilio_config, // 1
&account_sid, // 2
&to, // 3
None, // 4
None, // 5
None, // 6
None, // 7
None, // 8
None, // 9
None, // 10
None, // 11
None, // 12
None, // 13
None, // 14
None, // 15
None, // 16
None, // 17
Some(&from), // 18
None, // 19
Some(&body), // 20
None, // 21
)
.await;
Related
My code looks like the following:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::get(&url).await {
// Ok and Err statements here!
}
But, the problem here is that it gives an error for URLs with invalid or self-signed SSL certificate. So, I tried to do the following:
let fetches = futures::stream::iter(
hosts.into_iter().map(|url| {
async move {
match reqwest::Client::builder().danger_accept_invalid_certs(true).build().unwrap().get(&url).await {
// Ok and Err statements here!
}
When I try to build it with Cargo, it says "error[E0277]: `RequestBuilder` is not a future".
So, how can I make my code accept invalid certificates?
Unlike the top-level get() function, which returns a Response, the Client::get() method which you call in the second snippet, returns a RequestBuilder, which you must send() to actually communicate.
Adding the missing send() allows the code to compile (playgropund):
fn main() {
let hosts: Vec<String> = vec![];
let fetches = futures::stream::iter(hosts.into_iter().map(|url| async move {
match reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap()
.get(&url)
.send()
.await
{
Ok(x) => x,
Err(x) => panic!(),
}
}));
}
I have an actix-web server using HttpAuthentication middleware to authenticate all requests. The server runs fine and responds correctly to most requests, but occasionally certain requests trigger the error:
thread 'actix-rt:worker:2' panicked at 'AuthenticationMiddleware was called already
A request to the same endpoint will only trigger the error some of the time, so I am not sure what the root cause is.
My main() function (with only the relevant code included is:
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
// Some configuration code here
HttpServer::new(move || {
App::new()
.wrap(
HttpAuthentication::basic(validator)
)
// other code here
})
.bind(ip)?
.run()
.await
}
The validator function passed as the process_fn argument to HttpAuthentication::basic is:
async fn validator(
req: ServiceRequest,
credentials: BasicAuth,
) -> Result<ServiceRequest, Error> {
let config = req.app_data::<Config>()
.map(|data| data.get_ref().clone())
.unwrap_or_else(Default::default)
.scope("urn:example:channel=HBO&urn:example:rating=G,PG-13");
let username = env::var("USERNAME")
.expect("USERNAME must be set");
let password = env::var("PASSWORD")
.expect("USERNAME must be set");
if credentials.user_id().deref() == username {
match credentials.password() {
Some(pass) => {
if pass.deref() == password {
Ok(req)
} else {
Err(AuthenticationError::from(config).into())
}
}
None => Err(AuthenticationError::from(config).into())
}
} else {
Err(AuthenticationError::from(config).into())
}
}
This function essentially is just checking for the validity of the basic authentication username and password sent in the request. As I understand it, this should be wrapping every endpoint on the server and only allowing authenticated requests through.
What I do not understand is why I am getting this runtime error. Does anyone have any ideas as to why this is happening?
This is my first attempt at writing a small webservice with rust, using actix-web.
The code below is a request handler that is intended to do three things, insert an entry in the database, send an email if that db call was successful, and then return a json payload as the response.
data.dal (database call) and data.email_service are references to Actors.
The issue: is I am unable to capture the error returned by data.dal. Any attempt to reconfigure the below code seems to give me an error stating the compiler wasn't able to find a conversion from Actix Mailbox to [Type].
Is there an alternate/better way to rewrite this? Basically when the request is issued, I'd like to be able to call Actor A. And if the result from A is Ok then call Actor B. If the results from both are okay return a JSON payload. If either A or B return an error (can have different error types), return an custom error message.
pub fn register_email(
invitation: Json<EmailInvitationInput>,
data: web::Data<AppState>,
) -> impl Future<Item=HttpResponse, Error=Error> {
let m = dal::queries::CreateEmailInvitation { email: invitation.email.clone() };
data.dal.send(m)
.from_err()
.and_then(move |res| {
let invite = res.unwrap();
let email_input = email::SendLoginLink {
from: "from_email".to_string(),
to: "to_email".to_string(),
};
data.email_service.send(email_input)
.from_err()
.and_then(move |res| match res {
Ok(_) => {
Ok(HttpResponse::Ok().json(EmailInvitationOutput { expires_at: invite.expires_at }))
}
Err(err) => {
debug!("{:#?}", err);
Ok(ServiceError::InternalServerError.error_response())
}
})
})
}
What I usually do is to have an Error type that agglomerates all different errors, the coercion to this type can be achieved implicitly by declaring the appropriate From implementations and what you are doing from_err() but here I am being explicit:
I haven't tested this code snippet but this is how I have done it in projects I'm working on that use Actix:
data.dal.send(m)
.map_err(Error::Mailbox)
.and_then(|res| res.map_err(Error::Service))
.and_then(move |invite| {
let email_input = email::SendLoginLink {
from: "from_email".to_string(),
to: "to_email".to_string(),
};
data.email_service.send(email_input)
.map_err(Error::Mailbox)
.and_then(|res| res.map_err(Error::Service))
.and_then(move |res| HttpResponse::Ok().json(EmailInvitationOutput { expires_at: invite.expires_at }))
})
.or_else(|err| {
debug!("{:#?}", err);
ServiceError::InternalServerError.error_response()
})
(I'm assuming ServiceError implements IntoFuture just like HttpResponse does)
I'm trying to use windows-service to run an actix web app. It provides a nice API and mostly works. I can start my service just fine. However, when I try to stop my service, I get the following error: Error 109: The pipe has been ended (it does stop the service however).
I'm mostly just using the example provided for windows-service, but here is the relevant code (for context and all the wrapper functions, check out https://github.com/mullvad/windows-service-rs/blob/master/examples/ping_service.rs):
pub fn run_service() -> Result<()> {
fn hi() -> impl actix_web::Responder {
"Hello!\r\n"
}
let sys = actix_rt::System::new("test");
actix_web::HttpServer::new(move || {
actix_web::App::new()
.route("/", actix_web::web::get().to(hi))
})
.bind("0.0.0.0:3000").unwrap()
.start();
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
ServiceControl::Stop => {
actix_rt::System::with_current(|s| s.stop());
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
sys.run().unwrap();
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
Ok(())
}
If I put the System::stop in a thread::spawn, I get a different error: The service did not return an error. This could be an internal Windows error or an internal service error. In this case it does not stop the service.
I've put in some logging, and it doesn't look like the code ever gets past the sys.run().unwrap(), which is strange.
Any thoughts? I've never used the Windows Service API before so I don't really know what I'm doing.
EDIT
I figured out what the main issue is: I have to notify Windows the service has stopped before stopping the service. I put together a clunky way to make it work:
std::thread::spawn(move || {
loop {
if shutdown_signal.load(Ordering::Relaxed) {
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
}).unwrap();
actix_rt::System::current().stop();
break;
}
}
});
sys.run().unwrap();
// ...
where shutdown_signal is an AtomicBool I set to true in the event handler. I'm going to see if I can do this instead somehow through actix_rt.
Answering my own question. I think this is the best way to handle it, though I would be happy to see other solutions!
pub fn run_service() -> Result<()> {
use futures::Future;
fn hi() -> impl actix_web::Responder {
"Hello!\r\n"
}
let sys = actix_rt::System::new("test");
actix_web::HttpServer::new(move || {
actix_web::App::new()
.route("/", actix_web::web::get().to(hi))
})
.bind("0.0.0.0:3000").unwrap()
.start();
let (mut send_stop, recv_stop) = {
let (p, c) = futures::sync::oneshot::channel::<()>();
(Some(p), c)
};
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
ServiceControl::Stop => {
send_stop.take().unwrap().send(()).unwrap();
ServiceControlHandlerResult::NoError
}
_ => ServiceControlHandlerResult::NotImplemented,
}
};
let status_handle = service_control_handler::register(SERVICE_NAME, event_handler)?;
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
})?;
actix_rt::spawn(recv_stop.map(move |_| {
status_handle.set_service_status(ServiceStatus {
service_type: SERVICE_TYPE,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
}).unwrap();
actix_rt::System::current().stop()
}).map_err(|_| ()));
sys.run().unwrap();
Ok(())
}
I'm building a graphql api with Rust and Warp. I've looked through the docs, but I have still not figured out how to chain the filters, especially for checking the authorization in request header.
let context_extractor = warp::any()
// this code rejects all request which doesn't contain the authorization in header
// I'd like to make to check if authorization in header
.and(warp::header::<String>("authorization"))
.map(|token: String| -> Context {
let token_data = match verify_jwt(token) {
Ok(t) => t,
Err(_) => return Context { user_id: 0 },
};
Context {
user_id: token_data.claims.user_id,
}
});
let handle_request = move |context: Context,
request: juniper::http::GraphQLRequest|
-> Result<Vec<u8>, serde_json::Error> {
serde_json::to_vec(&request.execute(&schema, &context))
};
warp::post2()
.and(warp::path(path.into()))
.and(context_extractor)
.and(warp::body::json())
.map(handle_request)
.map(build_response)
.boxed()
This is my part of code. It works fine, but there is one problem. I've set up one route context_extractor with .and(warp::header::<String>("authorization"), then it rejects all requests which doesn't contain authorization in header.
How can I make
if request header has a authorization in header, then return Context with the proper user_id
if not, return Context with user_id: 0?
I've found the solution in github issues of Warp.
here is a small snippet.
let context_extractor = warp::any().and(
warp::header::<String>("authorization")
.map(|token: String| -> Context {
let token_data = match verify_jwt(token) {
Ok(t) => t,
Err(_) => return Context { user_id: 0 },
};
Context {
user_id: token_data.claims.user_id,
}
})
.or(warp::any().map(|| Context { user_id: 0 }))
.unify(),
);