Support proxy and custom certificate configuration. (#2006)

This commit is contained in:
Zicklag 2023-09-11 10:03:47 +00:00 committed by GitHub
parent d056280165
commit 6483d3035b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 92 additions and 20 deletions

22
Cargo.lock generated
View File

@ -660,6 +660,16 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "env_proxy"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a5019be18538406a43b5419a5501461f0c8b49ea7dfda0cfc32f4e51fc44be1"
dependencies = [
"log",
"url",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -2172,6 +2182,15 @@ dependencies = [
"sct", "sct",
] ]
[[package]]
name = "rustls-pemfile"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [
"base64",
]
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.100.2" version = "0.100.2"
@ -2906,6 +2925,7 @@ dependencies = [
"codespan-reporting", "codespan-reporting",
"comemo", "comemo",
"dirs", "dirs",
"env_proxy",
"flate2", "flate2",
"inferno", "inferno",
"memmap2", "memmap2",
@ -2913,6 +2933,8 @@ dependencies = [
"once_cell", "once_cell",
"open", "open",
"pathdiff 0.1.0", "pathdiff 0.1.0",
"rustls",
"rustls-pemfile",
"same-file", "same-file",
"self-replace", "self-replace",
"semver", "semver",

View File

@ -49,6 +49,9 @@ tracing-error = "0.2"
tracing-flame = "0.2.0" tracing-flame = "0.2.0"
tracing-subscriber = "0.3.17" tracing-subscriber = "0.3.17"
ureq = "2" ureq = "2"
rustls = "0.21"
rustls-pemfile = "1"
env_proxy = "0.4"
walkdir = "2" walkdir = "2"
xz2 = { version = "0.1", optional = true } xz2 = { version = "0.1", optional = true }
zip = { version = "0.6", optional = true } zip = { version = "0.6", optional = true }

View File

@ -17,6 +17,10 @@ pub struct CliArguments {
/// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace /// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace
#[clap(short, long, action = ArgAction::Count)] #[clap(short, long, action = ArgAction::Count)]
pub verbosity: u8, 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. /// What to do.

View File

@ -1,20 +1,60 @@
use std::collections::VecDeque;
use std::io::{self, ErrorKind, Read, Stderr, Write};
use std::time::{Duration, Instant};
use ureq::Response;
// Acknowledgement: // Acknowledgement:
// Closely modelled after rustup's [`DownloadTracker`]. // Closely modelled after rustup's [`DownloadTracker`].
// https://github.com/rust-lang/rustup/blob/master/src/cli/download_tracker.rs // 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;
/// Keep track of this many download speed samples. /// Keep track of this many download speed samples.
const SPEED_SAMPLES: usize = 5; 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. /// Download binary data and display its progress.
#[allow(clippy::result_large_err)] #[allow(clippy::result_large_err)]
pub fn download_with_progress(url: &str) -> Result<Vec<u8>, ureq::Error> { 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()?) Ok(RemoteReader::from_response(response).download()?)
} }

View File

@ -12,7 +12,7 @@ use walkdir::WalkDir;
use crate::args::FontsCommand; use crate::args::FontsCommand;
/// Execute a font listing command. /// Execute a font listing command.
pub fn fonts(command: FontsCommand) -> StrResult<()> { pub fn fonts(command: &FontsCommand) -> StrResult<()> {
let mut searcher = FontSearcher::new(); let mut searcher = FontSearcher::new();
searcher.search(&command.font_paths); searcher.search(&command.font_paths);

View File

@ -17,6 +17,7 @@ use std::process::ExitCode;
use clap::Parser; use clap::Parser;
use codespan_reporting::term::{self, termcolor}; use codespan_reporting::term::{self, termcolor};
use once_cell::sync::Lazy;
use termcolor::{ColorChoice, WriteColor}; use termcolor::{ColorChoice, WriteColor};
use crate::args::{CliArguments, Command}; use crate::args::{CliArguments, Command};
@ -26,10 +27,12 @@ thread_local! {
static EXIT: Cell<ExitCode> = Cell::new(ExitCode::SUCCESS); static EXIT: Cell<ExitCode> = Cell::new(ExitCode::SUCCESS);
} }
/// The parsed commandline arguments.
static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse);
/// Entry point. /// Entry point.
fn main() -> ExitCode { fn main() -> ExitCode {
let arguments = CliArguments::parse(); let _guard = match crate::tracing::setup_tracing(&ARGS) {
let _guard = match crate::tracing::setup_tracing(&arguments) {
Ok(guard) => guard, Ok(guard) => guard,
Err(err) => { Err(err) => {
eprintln!("failed to initialize tracing {}", err); eprintln!("failed to initialize tracing {}", err);
@ -37,9 +40,9 @@ fn main() -> ExitCode {
} }
}; };
let res = match arguments.command { let res = match &ARGS.command {
Command::Compile(command) => crate::compile::compile(command), Command::Compile(command) => crate::compile::compile(command.clone()),
Command::Watch(command) => crate::watch::watch(command), Command::Watch(command) => crate::watch::watch(command.clone()),
Command::Query(command) => crate::query::query(command), Command::Query(command) => crate::query::query(command),
Command::Fonts(command) => crate::fonts::fonts(command), Command::Fonts(command) => crate::fonts::fonts(command),
Command::Update(command) => crate::update::update(command), Command::Update(command) => crate::update::update(command),
@ -89,7 +92,7 @@ mod update {
use crate::args::UpdateCommand; use crate::args::UpdateCommand;
use typst::diag::{bail, StrResult}; use typst::diag::{bail, StrResult};
pub fn update(_: UpdateCommand) -> StrResult<()> { pub fn update(_: &UpdateCommand) -> StrResult<()> {
bail!( bail!(
"self-updating is not enabled for this executable, \ "self-updating is not enabled for this executable, \
please update with the package manager or mechanism \ please update with the package manager or mechanism \

View File

@ -12,7 +12,7 @@ use crate::set_failed;
use crate::world::SystemWorld; use crate::world::SystemWorld;
/// Execute a query command. /// Execute a query command.
pub fn query(command: QueryCommand) -> StrResult<()> { pub fn query(command: &QueryCommand) -> StrResult<()> {
let mut world = SystemWorld::new(&command.common)?; let mut world = SystemWorld::new(&command.common)?;
tracing::info!("Starting querying"); tracing::info!("Starting querying");
@ -27,8 +27,8 @@ pub fn query(command: QueryCommand) -> StrResult<()> {
match result { match result {
// Retrieve and print query results. // Retrieve and print query results.
Ok(document) => { Ok(document) => {
let data = retrieve(&world, &command, &document)?; let data = retrieve(&world, command, &document)?;
let serialized = format(data, &command)?; let serialized = format(data, command)?;
println!("{serialized}"); println!("{serialized}");
print_diagnostics(&world, &[], &warnings, command.common.diagnostic_format) print_diagnostics(&world, &[], &warnings, command.common.diagnostic_format)
.map_err(|_| "failed to print diagnostics")?; .map_err(|_| "failed to print diagnostics")?;

View File

@ -21,7 +21,7 @@ const TYPST_REPO: &str = "typst";
/// Fetches a target release or the latest release (if no version was specified) /// 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 /// from GitHub, unpacks it and self replaces the current binary with the
/// pre-compiled asset from the downloaded release. /// 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 { if let Some(ref version) = command.version {
let current_tag = env!("CARGO_PKG_VERSION").parse().unwrap(); 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) fs::copy(current_exe, &backup_path)
.map_err(|err| eco_format!("failed to create backup: {err}"))?; .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 { if !update_needed(&release)? && !command.force {
eprintln!("Already up-to-date."); eprintln!("Already up-to-date.");
return Ok(()); return Ok(());
@ -99,7 +99,7 @@ struct Release {
impl Release { impl Release {
/// Download the target release, or latest if version is `None`, from the /// Download the target release, or latest if version is `None`, from the
/// Typst repository. /// Typst repository.
pub fn from_tag(tag: Option<Version>) -> StrResult<Release> { pub fn from_tag(tag: Option<&Version>) -> StrResult<Release> {
let url = match tag { let url = match tag {
Some(tag) => format!( Some(tag) => format!(
"https://api.github.com/repos/{}/{}/releases/tags/v{}", "https://api.github.com/repos/{}/{}/releases/tags/v{}",