summaryrefslogtreecommitdiff
path: root/crates/typst-cli/src
diff options
context:
space:
mode:
authorZicklag <zicklag@katharostech.com>2023-09-11 10:03:47 +0000
committerGitHub <noreply@github.com>2023-09-11 12:03:47 +0200
commit6483d3035bab4df2d644acb738974413977aaa37 (patch)
tree44b329b5ac2f0b22b75172abb5f55180f4862f75 /crates/typst-cli/src
parentd056280165b981ef7a11f5bfb02417ef96760352 (diff)
Support proxy and custom certificate configuration. (#2006)
Diffstat (limited to 'crates/typst-cli/src')
-rw-r--r--crates/typst-cli/src/args.rs4
-rw-r--r--crates/typst-cli/src/download.rs50
-rw-r--r--crates/typst-cli/src/fonts.rs2
-rw-r--r--crates/typst-cli/src/main.rs15
-rw-r--r--crates/typst-cli/src/query.rs6
-rw-r--r--crates/typst-cli/src/update.rs6
6 files changed, 65 insertions, 18 deletions
diff --git a/crates/typst-cli/src/args.rs b/crates/typst-cli/src/args.rs
index 24a843fb..2113e03c 100644
--- a/crates/typst-cli/src/args.rs
+++ b/crates/typst-cli/src/args.rs
@@ -17,6 +17,10 @@ pub struct CliArguments {
/// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace
#[clap(short, long, action = ArgAction::Count)]
pub verbosity: u8,
+
+ /// Path to a custom CA certificate to use when making network requests.
+ #[clap(long = "cert", env = "TYPST_CERT")]
+ pub cert: Option<PathBuf>,
}
/// What to do.
diff --git a/crates/typst-cli/src/download.rs b/crates/typst-cli/src/download.rs
index 416f8c68..9db73fe8 100644
--- a/crates/typst-cli/src/download.rs
+++ b/crates/typst-cli/src/download.rs
@@ -1,20 +1,60 @@
+// Acknowledgement:
+// Closely modelled after rustup's [`DownloadTracker`].
+// https://github.com/rust-lang/rustup/blob/master/src/cli/download_tracker.rs
+
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Read, Stderr, Write};
+use std::sync::Arc;
use std::time::{Duration, Instant};
+use once_cell::sync::Lazy;
use ureq::Response;
-// Acknowledgement:
-// Closely modelled after rustup's [`DownloadTracker`].
-// https://github.com/rust-lang/rustup/blob/master/src/cli/download_tracker.rs
-
/// Keep track of this many download speed samples.
const SPEED_SAMPLES: usize = 5;
+/// Lazily loads a custom CA certificate if present, but if there's an error
+/// loading certificate, it just uses the default configuration.
+static TLS_CONFIG: Lazy<Option<Arc<rustls::ClientConfig>>> = Lazy::new(|| {
+ crate::ARGS
+ .cert
+ .as_ref()
+ .map(|path| {
+ let file = std::fs::OpenOptions::new().read(true).open(path)?;
+ let mut buffer = std::io::BufReader::new(file);
+ let certs = rustls_pemfile::certs(&mut buffer)?;
+ let mut store = rustls::RootCertStore::empty();
+ store.add_parsable_certificates(&certs);
+ let config = rustls::ClientConfig::builder()
+ .with_safe_defaults()
+ .with_root_certificates(store)
+ .with_no_client_auth();
+ Ok::<_, std::io::Error>(Arc::new(config))
+ })
+ .and_then(|x| x.ok())
+});
+
/// Download binary data and display its progress.
#[allow(clippy::result_large_err)]
pub fn download_with_progress(url: &str) -> Result<Vec<u8>, ureq::Error> {
- let response = ureq::get(url).call()?;
+ let mut builder = ureq::AgentBuilder::new()
+ .user_agent(concat!("typst/{}", env!("CARGO_PKG_VERSION")));
+
+ // Get the network proxy config from the environment.
+ if let Some(proxy) = env_proxy::for_url_str(url)
+ .to_url()
+ .and_then(|url| ureq::Proxy::new(url).ok())
+ {
+ builder = builder.proxy(proxy);
+ }
+
+ // Apply a custom CA certificate if present.
+ if let Some(config) = &*TLS_CONFIG {
+ builder = builder.tls_config(config.clone());
+ }
+
+ let agent = builder.build();
+ let response = agent.get(url).call()?;
Ok(RemoteReader::from_response(response).download()?)
}
diff --git a/crates/typst-cli/src/fonts.rs b/crates/typst-cli/src/fonts.rs
index 3e89e0d6..6b4fc2fc 100644
--- a/crates/typst-cli/src/fonts.rs
+++ b/crates/typst-cli/src/fonts.rs
@@ -12,7 +12,7 @@ use walkdir::WalkDir;
use crate::args::FontsCommand;
/// Execute a font listing command.
-pub fn fonts(command: FontsCommand) -> StrResult<()> {
+pub fn fonts(command: &FontsCommand) -> StrResult<()> {
let mut searcher = FontSearcher::new();
searcher.search(&command.font_paths);
diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs
index fe99e029..a6e60e5b 100644
--- a/crates/typst-cli/src/main.rs
+++ b/crates/typst-cli/src/main.rs
@@ -17,6 +17,7 @@ use std::process::ExitCode;
use clap::Parser;
use codespan_reporting::term::{self, termcolor};
+use once_cell::sync::Lazy;
use termcolor::{ColorChoice, WriteColor};
use crate::args::{CliArguments, Command};
@@ -26,10 +27,12 @@ thread_local! {
static EXIT: Cell<ExitCode> = Cell::new(ExitCode::SUCCESS);
}
+/// The parsed commandline arguments.
+static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse);
+
/// Entry point.
fn main() -> ExitCode {
- let arguments = CliArguments::parse();
- let _guard = match crate::tracing::setup_tracing(&arguments) {
+ let _guard = match crate::tracing::setup_tracing(&ARGS) {
Ok(guard) => guard,
Err(err) => {
eprintln!("failed to initialize tracing {}", err);
@@ -37,9 +40,9 @@ fn main() -> ExitCode {
}
};
- let res = match arguments.command {
- Command::Compile(command) => crate::compile::compile(command),
- Command::Watch(command) => crate::watch::watch(command),
+ let res = match &ARGS.command {
+ Command::Compile(command) => crate::compile::compile(command.clone()),
+ Command::Watch(command) => crate::watch::watch(command.clone()),
Command::Query(command) => crate::query::query(command),
Command::Fonts(command) => crate::fonts::fonts(command),
Command::Update(command) => crate::update::update(command),
@@ -89,7 +92,7 @@ mod update {
use crate::args::UpdateCommand;
use typst::diag::{bail, StrResult};
- pub fn update(_: UpdateCommand) -> StrResult<()> {
+ pub fn update(_: &UpdateCommand) -> StrResult<()> {
bail!(
"self-updating is not enabled for this executable, \
please update with the package manager or mechanism \
diff --git a/crates/typst-cli/src/query.rs b/crates/typst-cli/src/query.rs
index bf02f49d..68cf3319 100644
--- a/crates/typst-cli/src/query.rs
+++ b/crates/typst-cli/src/query.rs
@@ -12,7 +12,7 @@ use crate::set_failed;
use crate::world::SystemWorld;
/// Execute a query command.
-pub fn query(command: QueryCommand) -> StrResult<()> {
+pub fn query(command: &QueryCommand) -> StrResult<()> {
let mut world = SystemWorld::new(&command.common)?;
tracing::info!("Starting querying");
@@ -27,8 +27,8 @@ pub fn query(command: QueryCommand) -> StrResult<()> {
match result {
// Retrieve and print query results.
Ok(document) => {
- let data = retrieve(&world, &command, &document)?;
- let serialized = format(data, &command)?;
+ let data = retrieve(&world, command, &document)?;
+ let serialized = format(data, command)?;
println!("{serialized}");
print_diagnostics(&world, &[], &warnings, command.common.diagnostic_format)
.map_err(|_| "failed to print diagnostics")?;
diff --git a/crates/typst-cli/src/update.rs b/crates/typst-cli/src/update.rs
index b22eb7c5..562d7d2d 100644
--- a/crates/typst-cli/src/update.rs
+++ b/crates/typst-cli/src/update.rs
@@ -21,7 +21,7 @@ const TYPST_REPO: &str = "typst";
/// Fetches a target release or the latest release (if no version was specified)
/// from GitHub, unpacks it and self replaces the current binary with the
/// pre-compiled asset from the downloaded release.
-pub fn update(command: UpdateCommand) -> StrResult<()> {
+pub fn update(command: &UpdateCommand) -> StrResult<()> {
if let Some(ref version) = command.version {
let current_tag = env!("CARGO_PKG_VERSION").parse().unwrap();
@@ -61,7 +61,7 @@ pub fn update(command: UpdateCommand) -> StrResult<()> {
fs::copy(current_exe, &backup_path)
.map_err(|err| eco_format!("failed to create backup: {err}"))?;
- let release = Release::from_tag(command.version)?;
+ let release = Release::from_tag(command.version.as_ref())?;
if !update_needed(&release)? && !command.force {
eprintln!("Already up-to-date.");
return Ok(());
@@ -99,7 +99,7 @@ struct Release {
impl Release {
/// Download the target release, or latest if version is `None`, from the
/// Typst repository.
- pub fn from_tag(tag: Option<Version>) -> StrResult<Release> {
+ pub fn from_tag(tag: Option<&Version>) -> StrResult<Release> {
let url = match tag {
Some(tag) => format!(
"https://api.github.com/repos/{}/{}/releases/tags/v{}",