Skip to content

Commit

Permalink
feat: Switch channelIDs to base64 (#48)
Browse files Browse the repository at this point in the history
* feat: Switch channelIDs to base64

* change channelID to random char string
* fix group add (again)
* reduce number of times metadata fetched
* remove ChannelType, ChannelId
* change channel id to "session_id" to be clear where it comes from
* convert `From<String>` trait to `from_str() -> Result`

Closes #47
  • Loading branch information
jrconlin authored Nov 2, 2018
1 parent 3a521c7 commit ff3b1ec
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 97 deletions.
12 changes: 11 additions & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion channelserver/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[package]
name = "channelserver"
version = "0.5.0"
version = "0.6.0"
authors = ["jr conlin<src+pairsona@jrconlin.com"]

[dependencies]
base64 = "0.10"
config = "0.9"
failure = "0.1"
rand = "0.5"
Expand Down
67 changes: 67 additions & 0 deletions channelserver/src/channelid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::fmt;

use base64;
use rand::RngCore;
use serde::ser::{Serialize, Serializer};

const CHANNELID_LEN: usize = 16;

#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ChannelID {
value: [u8; CHANNELID_LEN],
}

impl ChannelID {
pub fn to_string(self) -> String {
base64::encode_config(&self.value, base64::URL_SAFE_NO_PAD)
}

pub fn from_str<'a>(string: &'a str) -> Result<ChannelID, base64::DecodeError> {
let bytes = base64::decode_config(string, base64::URL_SAFE_NO_PAD)?;
let mut array = [0; 16];
array.copy_from_slice(&bytes[..16]);
Ok(ChannelID { value: array })
}
}

impl Default for ChannelID {
fn default() -> Self {
let mut rng = rand::thread_rng();
let mut bytes = [0; CHANNELID_LEN];
rng.fill_bytes(&mut bytes);
Self { value: bytes }
}
}

impl fmt::Display for ChannelID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// calling to_string() causes a stack overflow.
let as_b64 = base64::encode_config(&self.value, base64::URL_SAFE_NO_PAD);
write!(f, "{}", as_b64)
}
}

impl Serialize for ChannelID {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_parse() {
let raw_id = "j6jLPVPeQR6diyrkQinRAQ";
// From URLSafe b64
let chan = ChannelID::from_str(raw_id).unwrap();
assert!(chan.to_string() == raw_id.to_owned());
ChannelID::from_str("invalid").expect_err("rejected");
let output = format!("{}", chan);
assert_eq!("j6jLPVPeQR6diyrkQinRAQ".to_owned(), output);
}
}
44 changes: 26 additions & 18 deletions channelserver/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//#![feature(custom_derive, try_from)]
#![allow(unused_variables)]
extern crate base64;
extern crate byteorder;
extern crate bytes;
extern crate config;
Expand Down Expand Up @@ -35,8 +36,8 @@ use std::time::{Duration, Instant};
use actix::Arbiter;
use actix_web::server::HttpServer;
use actix_web::{fs, http, ws, App, Error, HttpRequest, HttpResponse};
use uuid::Uuid;

mod channelid;
mod logging;
mod meta;
mod metrics;
Expand All @@ -48,40 +49,48 @@ mod settings;
/*
* based on the Actix websocket example ChatServer
*/
fn channel_route_base(
req: &HttpRequest<session::WsChannelSessionState>,
) -> Result<HttpResponse, Error> {
info!(&req.state().log.log, "### Channel Route Base!");
channel_route(req)
}

/// Entry point for our route
fn channel_route(req: &HttpRequest<session::WsChannelSessionState>) -> Result<HttpResponse, Error> {
// not sure if it's possible to have actix_web parse the path and have a properly
// scoped request, since the calling structure is different for the two, so
// manually extracting the id from the path.
info!(&req.state().log.log, "@@@ Channel Route!");
let mut path: Vec<_> = req.path().split("/").collect();
let meta_info = meta::SenderData::from(req.clone());
let mut initial_connect = true;
let channel = match path.pop() {
Some(id) => {
// if the id is valid, but not present, treat it like a "None"
if id.len() == 0 {
Uuid::new_v4()
channelid::ChannelID::default()
} else {
initial_connect = false;
Uuid::parse_str(id).unwrap_or_else(|e| {
warn!(&req.state().log.log,
"Invalid ChannelID specified: {:?}", e;
"remote_ip" => &meta_info.remote);
Uuid::nil()
})
let channel_id = match channelid::ChannelID::from_str(id) {
Ok(channelid) => channelid,
Err(err) => {
warn!(&req.state().log.log,
"Invalid ChannelID specified: {:?}", id;
"remote_ip" => &meta_info.remote);
return Ok(HttpResponse::new(http::StatusCode::NOT_FOUND));
}
};
channel_id
}
}
None => Uuid::new_v4(),
None => channelid::ChannelID::default(),
};
if channel == Uuid::nil() {
return Ok(HttpResponse::new(http::StatusCode::NOT_FOUND));
}
info!(
&req.state().log.log,
"Creating session for {} channel: \"{}\"",
if initial_connect {"new"} else {"candiate"},
channel.to_simple().to_string();
channel.to_string();
"remote_ip" => &meta_info.remote
);

Expand Down Expand Up @@ -120,10 +129,10 @@ fn show_version(req: &HttpRequest<session::WsChannelSessionState>) -> Result<Htt

fn build_app(app: App<session::WsChannelSessionState>) -> App<session::WsChannelSessionState> {
let mut mapp = app
// connecting to an empty channel creates a new one.
.resource("/v1/ws/", |r| r.route().f(channel_route_base))
// websocket to an existing channel
.resource("/v1/ws/{channel}", |r| r.route().f(channel_route))
// connecting to an empty channel creates a new one.
.resource("/v1/ws/", |r| r.route().f(channel_route))
.resource("/__version__", |r| {
r.method(http::Method::GET).f(show_version)
})
Expand Down Expand Up @@ -186,8 +195,7 @@ fn main() {
} else {
logging::MozLogger::new_json()
};
let metrics = metrics::metrics_from_opts(
&msettings, logging).unwrap();
let metrics = metrics::metrics_from_opts(&msettings, logging).unwrap();
let iploc = maxminddb::Reader::open(&db_loc).unwrap_or_else(|x| {
use std::process::exit;
println!("Could not read geoip database {:?}", x);
Expand Down Expand Up @@ -221,7 +229,7 @@ mod test {

use actix_web::test;
use actix_web::HttpMessage;
use cadence::{StatsdClient, NopMetricSink};
use cadence::{NopMetricSink, StatsdClient};

use super::*;
fn get_server() -> test::TestServer {
Expand Down
Loading

0 comments on commit ff3b1ec

Please sign in to comment.