Follow URL redirects before downloading, this passes URL shorteners
This commit is contained in:
parent
e2ebbc91a9
commit
dd3a4c561f
6 changed files with 70 additions and 28 deletions
|
@ -19,13 +19,14 @@ use tempfile::{Builder as TempBuilder, NamedTempFile};
|
|||
use super::select_api_version;
|
||||
#[cfg(feature = "archive")]
|
||||
use crate::archive::archive::Archive;
|
||||
use crate::client::create_transfer_client;
|
||||
use crate::client::{create_client, create_transfer_client};
|
||||
use crate::cmd::matcher::{download::DownloadMatcher, main::MainMatcher, Matcher};
|
||||
#[cfg(feature = "history")]
|
||||
use crate::history_tool;
|
||||
use crate::progress::ProgressBar;
|
||||
use crate::util::{
|
||||
ensure_enough_space, ensure_password, prompt_yes, quit, quit_error, quit_error_msg, ErrorHints,
|
||||
ensure_enough_space, ensure_password, follow_url, print_error, prompt_yes, quit, quit_error,
|
||||
quit_error_msg, ErrorHints,
|
||||
};
|
||||
|
||||
/// A file download action.
|
||||
|
@ -46,12 +47,21 @@ impl<'a> Download<'a> {
|
|||
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
|
||||
let matcher_download = DownloadMatcher::with(self.cmd_matches).unwrap();
|
||||
|
||||
// Get the share URL, derive the host
|
||||
let url = matcher_download.url();
|
||||
let host = matcher_download.guess_host();
|
||||
// Create a regular client
|
||||
let client = create_client(&matcher_main);
|
||||
|
||||
// Create a reqwest client capable for downloading files
|
||||
let client = create_transfer_client(&matcher_main);
|
||||
// Get the share URL, attempt to follow it
|
||||
let url = matcher_download.url();
|
||||
let url = match follow_url(&client, &url) {
|
||||
Ok(url) => url,
|
||||
Err(err) => {
|
||||
print_error(err.context("failed to follow share URL, ignoring").compat());
|
||||
url
|
||||
}
|
||||
};
|
||||
|
||||
// Guess the host
|
||||
let host = matcher_download.guess_host(Some(url.clone()));
|
||||
|
||||
// Determine the API version to use
|
||||
let mut desired_version = matcher_main.api();
|
||||
|
@ -154,6 +164,9 @@ impl<'a> Download<'a> {
|
|||
let progress_bar = Arc::new(Mutex::new(ProgressBar::new_download()));
|
||||
let progress_reader: Arc<Mutex<ProgressReporter>> = progress_bar;
|
||||
|
||||
// Create a transfer client
|
||||
let transfer_client = create_transfer_client(&matcher_main);
|
||||
|
||||
// Execute an download action
|
||||
let progress = if !matcher_main.quiet() {
|
||||
Some(progress_reader)
|
||||
|
@ -161,7 +174,7 @@ impl<'a> Download<'a> {
|
|||
None
|
||||
};
|
||||
ApiDownload::new(api_version, &file, target, password, false, Some(metadata))
|
||||
.invoke(&client, progress)?;
|
||||
.invoke(&transfer_client, progress)?;
|
||||
|
||||
// Extract the downloaded file if working with an archive
|
||||
#[cfg(feature = "archive")]
|
||||
|
|
|
@ -321,10 +321,7 @@ impl<'a> Upload<'a> {
|
|||
{
|
||||
if matcher_upload.qrcode() {
|
||||
if let Err(err) = print_qr(url.as_str()) {
|
||||
print_error(
|
||||
err.context("failed to print QR code, ignoring")
|
||||
.compat(),
|
||||
);
|
||||
print_error(err.context("failed to print QR code, ignoring").compat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use chbs::{
|
||||
config::BasicConfig,
|
||||
prelude::*,
|
||||
word::WordList,
|
||||
};
|
||||
use chbs::{config::BasicConfig, prelude::*, word::WordList};
|
||||
use clap::Arg;
|
||||
|
||||
use super::{CmdArg, CmdArgFlag};
|
||||
|
|
|
@ -26,8 +26,8 @@ impl<'a: 'b, 'b> DownloadMatcher<'a> {
|
|||
/// Guess the file share host, based on the file share URL.
|
||||
///
|
||||
/// See `Self::url`.
|
||||
pub fn guess_host(&'a self) -> Url {
|
||||
let mut url = self.url();
|
||||
pub fn guess_host(&'a self, url: Option<Url>) -> Url {
|
||||
let mut url = url.unwrap_or(self.url());
|
||||
url.set_path("");
|
||||
url.set_query(None);
|
||||
url.set_fragment(None);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
use ffsend_api::{
|
||||
api::request::{ensure_success, ResponseError},
|
||||
url::{self, Url},
|
||||
reqwest::{self, Client},
|
||||
url::{self, Url},
|
||||
};
|
||||
use urlshortener::{
|
||||
providers::{Provider, self},
|
||||
providers::{self, Provider},
|
||||
request::{Method, Request},
|
||||
};
|
||||
|
||||
|
@ -21,9 +21,7 @@ pub fn shorten(client: &Client, url: &str) -> Result<String> {
|
|||
|
||||
/// Shorten the given URL.
|
||||
pub fn shorten_url(client: &Client, url: &Url) -> Result<Url> {
|
||||
Url::parse(
|
||||
&shorten(client, url.as_str())?,
|
||||
).map_err(|err| err.into())
|
||||
Url::parse(&shorten(client, url.as_str())?).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
/// Do the request as given, return the response.
|
||||
|
@ -59,8 +57,7 @@ fn request(client: &Client, req: Request) -> Result<String> {
|
|||
}
|
||||
|
||||
// Send the request, ensure success
|
||||
let mut response = builder.send()
|
||||
.map_err(Error::Request)?;
|
||||
let mut response = builder.send().map_err(Error::Request)?;
|
||||
ensure_success(&response)?;
|
||||
|
||||
// Respond with the body text
|
||||
|
@ -84,7 +81,7 @@ pub enum Error {
|
|||
|
||||
/// An error occurred while parsing the shortened URL.
|
||||
#[fail(display = "failed to shorten URL, could not parse URL")]
|
||||
Url(#[cause] url::ParseError)
|
||||
Url(#[cause] url::ParseError),
|
||||
}
|
||||
|
||||
impl From<url::ParseError> for Error {
|
||||
|
|
41
src/util.rs
41
src/util.rs
|
@ -29,7 +29,11 @@ use chrono::Duration;
|
|||
use failure::{err_msg, Fail};
|
||||
#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
|
||||
use failure::{Compat, Error};
|
||||
use ffsend_api::url::Url;
|
||||
use ffsend_api::{
|
||||
api::request::{ensure_success, ResponseError},
|
||||
reqwest::{self, Client},
|
||||
url::Url,
|
||||
};
|
||||
use rpassword::prompt_password_stderr;
|
||||
|
||||
use crate::cmd::matcher::MainMatcher;
|
||||
|
@ -857,3 +861,38 @@ pub fn api_version_list() -> Vec<&'static str> {
|
|||
|
||||
versions
|
||||
}
|
||||
|
||||
/// Follow redirects on the given URL, and return the final full URL.
|
||||
///
|
||||
/// This is used to obtain share URLs from shortened links.
|
||||
///
|
||||
// TODO: extract this into module
|
||||
pub fn follow_url(client: &Client, url: &Url) -> Result<Url, FollowError> {
|
||||
// Send the request, follow the URL, ensure success
|
||||
let response = client
|
||||
.get(url.as_str())
|
||||
.send()
|
||||
.map_err(FollowError::Request)?;
|
||||
ensure_success(&response)?;
|
||||
|
||||
// Obtain the final URL
|
||||
Ok(response.url().clone())
|
||||
}
|
||||
|
||||
/// URL following error.
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum FollowError {
|
||||
/// Failed to send the shortening request.
|
||||
#[fail(display = "failed to send URL follow request")]
|
||||
Request(#[cause] reqwest::Error),
|
||||
|
||||
/// The server responded with a bad response.
|
||||
#[fail(display = "failed to shorten URL, got bad response")]
|
||||
Response(#[cause] ResponseError),
|
||||
}
|
||||
|
||||
impl From<ResponseError> for FollowError {
|
||||
fn from(err: ResponseError) -> Self {
|
||||
FollowError::Response(err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue