Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 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 Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pocket-relay-client"
version = "0.3.0"
version = "0.3.1"
edition = "2021"
build = "build.rs"
license = "MIT"
Expand Down Expand Up @@ -37,6 +37,7 @@ futures = "0.3"
thiserror = "1"
semver = { version = "1", features = ["serde"] }
hyper = { version = "0.14", features = ["server", "http1", "tcp", "runtime"] }
url = "2.4.1"

log = "0.4"
env_logger = "0.10"
Expand Down
63 changes: 29 additions & 34 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use crate::{
config::{write_config_file, ClientConfig},
constants::{MIN_SERVER_VERSION, SERVER_IDENT},
Expand All @@ -9,6 +11,7 @@ use semver::Version;
use serde::Deserialize;
use thiserror::Error;
use tokio::sync::RwLock;
use url::Url;

/// Shared target location
pub static TARGET: RwLock<Option<LookupData>> = RwLock::const_new(None);
Expand All @@ -29,22 +32,18 @@ struct ServerDetails {
/// version obtained from the server
#[derive(Debug, Clone)]
pub struct LookupData {
/// The scheme used to connect to the server (e.g http or https)
pub scheme: String,
/// The host address of the server
pub host: String,
/// Server url
pub url: Url,
/// The server version
pub version: Version,
/// The server port
pub port: u16,
}

/// Errors that can occur while looking up a server
#[derive(Debug, Error)]
pub enum LookupError {
/// The server url was missing the host portion
#[error("Unable to find host portion of provided Connection URL")]
InvalidHostTarget,
#[error("Invalid Connection URL: {0}")]
InvalidHostTarget(#[from] url::ParseError),
/// The server connection failed
#[error("Failed to connect to server: {0}")]
ConnectionFailed(reqwest::Error),
Expand All @@ -63,24 +62,31 @@ pub enum LookupError {
}

pub async fn try_lookup_host(client: Client, host: &str) -> Result<LookupData, LookupError> {
let mut url = String::new();

// Fill in missing host portion
if !host.starts_with("http://") && !host.starts_with("https://") {
url.push_str("http://");
url.push_str(host)
} else {
url.push_str(host);
}
let url = {
let mut url = String::new();

// Fill in missing scheme portion
if !host.starts_with("http://") && !host.starts_with("https://") {
url.push_str("http://");
url.push_str(host)
} else {
url.push_str(host);
}

if !host.ends_with('/') {
url.push('/')
}
// Ensure theres a trailing slash (URL path will be interpeted incorrectly without)
if !host.ends_with('/') {
url.push('/');
}

url.push_str("api/server");
url
};

let url = Url::from_str(&url)?;

let info_url = url.join("api/server").expect("Failed to server info URL");

let response = client
.get(url)
.get(info_url)
.header(ACCEPT, "application/json")
.send()
.await
Expand All @@ -107,15 +113,6 @@ pub async fn try_lookup_host(client: Client, host: &str) -> Result<LookupData, L
}
};

let url = response.url();
let scheme = url.scheme().to_string();

let port = url.port_or_known_default().unwrap_or(80);
let host = match url.host() {
Some(value) => value.to_string(),
None => return Err(LookupError::InvalidHostTarget),
};

let details = response
.json::<ServerDetails>()
.await
Expand All @@ -134,9 +131,7 @@ pub async fn try_lookup_host(client: Client, host: &str) -> Result<LookupData, L
}

Ok(LookupData {
scheme,
host,
port,
url,
version: details.version,
})
}
Expand Down
20 changes: 14 additions & 6 deletions src/servers/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use hyper::body::Body;
use hyper::service::service_fn;
use hyper::{server::conn::Http, Request};
use hyper::{Response, StatusCode};
use log::error;
use log::{debug, error};
use reqwest::Client;
use std::convert::Infallible;
use std::{net::Ipv4Addr, process::exit};
Expand Down Expand Up @@ -66,10 +66,18 @@ async fn proxy_http(req: Request<Body>, http_client: Client) -> Result<Response<
}
};

format!(
"{}://{}:{}{}",
target.scheme, target.host, target.port, path
)
// Remove the leading / to make the path relative
let path = path.strip_prefix('/').unwrap_or(path);

match target.url.join(path) {
Ok(value) => value,
Err(_) => {
// Failed to create a path
let mut error_response = Response::default();
*error_response.status_mut() = StatusCode::SERVICE_UNAVAILABLE;
return Ok(error_response);
}
}
};

let response = match proxy_request(http_client, target_url).await {
Expand All @@ -90,7 +98,7 @@ async fn proxy_http(req: Request<Body>, http_client: Client) -> Result<Response<
/// a response on success or providing an error.
async fn proxy_request(
http_client: Client,
target_url: String,
target_url: url::Url,
) -> Result<Response<Body>, reqwest::Error> {
let response = http_client.get(target_url).send().await?;

Expand Down
11 changes: 6 additions & 5 deletions src/servers/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,16 @@ const LEGACY_HEADER_HOST: &str = "x-pocket-relay-host";
const HEADER_LOCAL_HTTP: &str = "x-pocket-relay-local-http";

/// Endpoint for upgrading the server connection
const UPGRADE_ENDPOINT: &str = "/api/server/upgrade";
const UPGRADE_ENDPOINT: &str = "api/server/upgrade";

async fn handle_blaze(mut client: TcpStream, http_client: Client) {
let url = match &*TARGET.read().await {
// Create the upgrade URL
Some(target) => format!(
"{}://{}:{}{}",
target.scheme, target.host, target.port, UPGRADE_ENDPOINT
),
Some(target) => target
.url
.join(UPGRADE_ENDPOINT)
.expect("Failed to create update endpoint URL"),

None => return,
};

Expand Down
13 changes: 6 additions & 7 deletions src/servers/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tokio::{
};

/// Server API endpoint to send telemetry data to
const TELEMETRY_ENDPOINT: &str = "/api/server/telemetry";
const TELEMETRY_ENDPOINT: &str = "api/server/telemetry";

pub async fn start_server(http_client: Client) {
// Initializing the underlying TCP listener
Expand Down Expand Up @@ -36,16 +36,15 @@ pub async fn start_server(http_client: Client) {
None => return,
};

// Create the telemetry URL
let url = format!(
"{}://{}:{}{}",
target.scheme, target.host, target.port, TELEMETRY_ENDPOINT
);
let url = target
.url
.join(TELEMETRY_ENDPOINT)
.expect("Failed to create telemetry endpoint");

let mut stream = stream;
while let Ok(message) = read_message(&mut stream).await {
// TODO: Batch these telemetry messages and send them to the server
let _ = http_client.post(&url).json(&message).send().await;
let _ = http_client.post(url.clone()).json(&message).send().await;
}
});
}
Expand Down
4 changes: 3 additions & 1 deletion src/ui/iced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ impl Application for App {
LookupState::Loading => text("Connecting...").style(YELLOW_TEXT),
LookupState::Success(lookup_data) => text(format!(
"Connected: {} {} version v{}",
lookup_data.scheme, lookup_data.host, lookup_data.version
lookup_data.url.scheme(),
lookup_data.url.authority(),
lookup_data.version
))
.style(Palette::DARK.success),
LookupState::Error => text("Failed to connect").style(Palette::DARK.danger),
Expand Down
4 changes: 3 additions & 1 deletion src/ui/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ impl App {
Ok(result) => {
let text = format!(
"Connected: {} {} version v{}",
result.scheme, result.host, result.version
result.url.scheme(),
result.url.authority(),
result.version
);
self.connection_label.set_text(&text)
}
Expand Down