use std::str::FromStr;
use std::sync::Arc;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use futures::{AsyncSink};
use futures::sink::Sink;
use tk_http::Status;
use tk_http::server::Head;
use serde_json::{self, Value as Json};
use http_pools::{REQUESTS, FAILED_503};
use intern::SessionId;
use config::chat::Chat;
use incoming::{Input};
use chat::{Cid, MessageError, CloseReason, ConnectionSender};
use chat::backend;
use chat::message::AuthData;
use chat::processor::{Action};
use chat::ConnectionMessage::{StopSocket};
fn auth_data(handshake: &Head) -> Result<AuthData, Status> {
let mut cookie = None;
let mut auth = None;
for (key, value) in handshake.headers() {
if key.eq_ignore_ascii_case("Cookie") {
if cookie.is_some() {
debug!("Duplicate Cookie header");
return Err(Status::BadRequest);
}
cookie = Some(String::from_utf8_lossy(value).into_owned());
} else if key.eq_ignore_ascii_case("Authorization") {
if auth.is_some() {
debug!("Duplicate Authorization header");
return Err(Status::BadRequest);
}
auth = Some(String::from_utf8_lossy(value).into_owned());
}
}
let url_qs = handshake.path().expect("invalid path for websocket hanshake")
.splitn(2, "?").nth(1).unwrap_or("").to_string();
Ok(AuthData {
http_cookie: cookie,
http_authorization: auth,
url_querystring: url_qs,
})
}
pub fn start_authorize(inp: &Input, conn_id: Cid, settings: &Arc<Chat>,
messages: ConnectionSender)
{
let pool = inp.runtime.session_pools
.processor.pool(&settings.session_pool);
pool.send(Action::NewConnection {
conn_id: conn_id,
channel: messages.clone(),
});
let dest = settings.message_handlers
.resolve("tangle.authorize_connection");
let dest_settings = match inp.config.http_destinations.get(&dest.upstream)
{
Some(h) => h,
None => {
error!("No such destination {:?}", dest.upstream);
messages.send(StopSocket(CloseReason::AuthHttp(
Status::InternalServerError)));
return;
}
};
let path: Cow<_> = if dest.path == "/" {
"/tangle/authorize_connection".into()
} else {
(dest.path.to_string() + "/tangle/authorize_connection").into()
};
let mut up = inp.runtime.http_pools.upstream(&dest.upstream);
let auth_data = match auth_data(inp.headers) {
Ok(data) => data,
Err(status) => {
messages.send(StopSocket(CloseReason::AuthHttp(status)));
return;
}
};
let codec = Box::new(backend::AuthCodec::new(path.into_owned(),
conn_id, auth_data, pool.clone(),
dest_settings,
messages.clone(),
inp.runtime.server_id.clone(),
));
match up.get_mut().get_mut() {
Some(pool) => {
match pool.start_send(codec) {
Ok(AsyncSink::NotReady(_codec)) => {
FAILED_503.incr(1);
messages.send(StopSocket(CloseReason::AuthHttp(
Status::ServiceUnavailable)));
}
Ok(AsyncSink::Ready) => {
REQUESTS.incr(1);
debug!("Sent /tangle/authorize_connection to proxy");
}
Err(e) => {
error!("Error sending to pool {:?}: {}", dest.upstream, e);
messages.send(StopSocket(CloseReason::AuthHttp(
Status::ServiceUnavailable)));
}
}
}
None => {
error!("No such destination {:?}", dest.upstream);
messages.send(StopSocket(CloseReason::AuthHttp(Status::NotFound)));
}
}
}
pub fn good_status(status: Status) -> bool {
matches!(status,
Status::BadRequest|
Status::Unauthorized|
Status::Forbidden|
Status::NotFound|
Status::Gone|
Status::ServiceUnavailable
)
}
pub fn parse_userinfo(data: &[u8])
-> Result<(SessionId, Json), MessageError>
{
use super::error::MessageError::ValidationError;
let data: Json = serde_json::from_slice(data)?;
let ssid = match data.get("user_id") {
Some(&Json::String(ref ssid)) => {
SessionId::from_str(ssid.as_str())
.map_err(|_| ValidationError("Invalid user_id".into()))?
}
_ => {
return Err(ValidationError(
"user_id is missing or invalid".into()))
}
};
Ok((ssid, data))
}