From 2c3e70765c685202cfa44c2a005e04972fa95c92 Mon Sep 17 00:00:00 2001 From: Boni Garcia Date: Mon, 27 Feb 2023 12:11:06 +0100 Subject: [PATCH] [rust] Selenium Manager parse mirror to get latest IEDriverServer version (part of #11672) --- rust/src/chrome.rs | 4 +- rust/src/downloads.rs | 6 ++- rust/src/edge.rs | 4 +- rust/src/files.rs | 3 ++ rust/src/iexplorer.rs | 89 +++++++++++++++++++++++++++-------- rust/src/lib.rs | 1 + rust/src/mirror.rs | 41 ++++++++++++++++ rust/tests/iexplorer_tests.rs | 36 ++++++++++++++ 8 files changed, 159 insertions(+), 25 deletions(-) create mode 100644 rust/src/mirror.rs create mode 100644 rust/tests/iexplorer_tests.rs diff --git a/rust/src/chrome.rs b/rust/src/chrome.rs index ef94be6878b1c..d3b42798c8440 100644 --- a/rust/src/chrome.rs +++ b/rust/src/chrome.rs @@ -23,7 +23,7 @@ use std::path::PathBuf; use crate::config::ARCH::ARM64; use crate::config::OS::{LINUX, MACOS, WINDOWS}; -use crate::downloads::read_content_from_link; +use crate::downloads::read_version_from_link; use crate::files::{compose_driver_path_in_cache, BrowserPath, PARSE_ERROR}; use crate::logger::Logger; use crate::metadata::{ @@ -176,7 +176,7 @@ impl SeleniumManager for ChromeManager { "Reading {} version from {}", &self.driver_name, driver_url )); - match read_content_from_link(self.get_http_client(), driver_url) { + match read_version_from_link(self.get_http_client(), driver_url) { Ok(version) => { driver_version = version; break; diff --git a/rust/src/downloads.rs b/rust/src/downloads.rs index bfface458c1ca..f56c6bf62e4a6 100644 --- a/rust/src/downloads.rs +++ b/rust/src/downloads.rs @@ -65,12 +65,16 @@ pub async fn download_driver_to_tmp_folder( Ok((tmp_dir, target_path)) } +pub fn read_version_from_link(http_client: &Client, url: String) -> Result> { + parse_version(read_content_from_link(http_client, url)?) +} + #[tokio::main] pub async fn read_content_from_link( http_client: &Client, url: String, ) -> Result> { - parse_version(http_client.get(url).send().await?.text().await?) + Ok(http_client.get(url).send().await?.text().await?) } #[tokio::main] diff --git a/rust/src/edge.rs b/rust/src/edge.rs index ddc95e5354950..546e559880637 100644 --- a/rust/src/edge.rs +++ b/rust/src/edge.rs @@ -23,7 +23,7 @@ use std::path::PathBuf; use crate::config::ARCH::{ARM64, X32}; use crate::config::OS::{LINUX, MACOS, WINDOWS}; -use crate::downloads::read_content_from_link; +use crate::downloads::read_version_from_link; use crate::files::{compose_driver_path_in_cache, BrowserPath}; use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, @@ -176,7 +176,7 @@ impl SeleniumManager for EdgeManager { "Reading {} version from {}", &self.driver_name, driver_url )); - let driver_version = read_content_from_link(self.get_http_client(), driver_url)?; + let driver_version = read_version_from_link(self.get_http_client(), driver_url)?; if !browser_version.is_empty() { metadata.drivers.push(create_driver_metadata( diff --git a/rust/src/files.rs b/rust/src/files.rs index 9ed447f202fe1..315720501fcb9 100644 --- a/rust/src/files.rs +++ b/rust/src/files.rs @@ -192,5 +192,8 @@ pub fn parse_version(version_text: String) -> Result> { break; } } + if parsed_version.ends_with('.') { + parsed_version = parsed_version[0..parsed_version.len() - 1].to_string(); + } Ok(parsed_version) } diff --git a/rust/src/iexplorer.rs b/rust/src/iexplorer.rs index c2aa3ee2cd32f..e94dcbc7c83a7 100644 --- a/rust/src/iexplorer.rs +++ b/rust/src/iexplorer.rs @@ -17,18 +17,19 @@ use crate::config::ManagerConfig; use reqwest::Client; +use std::cell::RefCell; use std::collections::HashMap; use std::error::Error; use std::path::PathBuf; -use crate::downloads::read_redirect_from_link; use crate::files::{compose_driver_path_in_cache, BrowserPath}; -use crate::{create_default_http_client, Logger, SeleniumManager}; +use crate::{create_default_http_client, parse_version, Logger, SeleniumManager}; use crate::metadata::{ create_driver_metadata, get_driver_version_from_metadata, get_metadata, write_metadata, }; +use crate::mirror::{get_mirror_response, Assets, SeleniumRelease}; pub const IE_NAMES: &[&str] = &[ "iexplorer", @@ -39,7 +40,9 @@ pub const IE_NAMES: &[&str] = &[ ]; pub const IEDRIVER_NAME: &str = "IEDriverServer"; const DRIVER_URL: &str = "https://github.com/SeleniumHQ/selenium/releases/"; -const LATEST_RELEASE: &str = "latest"; +const IEDRIVER_RELEASE: &str = "IEDriverServer_Win32_"; + +thread_local!(static RELEASE_URL: RefCell = RefCell::new("".to_string())); pub struct IExplorerManager { pub browser_name: &'static str, @@ -100,29 +103,75 @@ impl SeleniumManager for IExplorerManager { Ok(driver_version) } _ => { - let latest_url = format!("{}{}", DRIVER_URL, LATEST_RELEASE); - let driver_version = read_redirect_from_link(self.get_http_client(), latest_url)?; - - if !browser_version.is_empty() { - metadata.drivers.push(create_driver_metadata( - browser_version, - self.driver_name, - &driver_version, - )); - write_metadata(&metadata, self.get_logger()); + let selenium_releases = get_mirror_response(self.get_http_client())?; + + let filtered_releases: Vec = selenium_releases + .into_iter() + .filter(|r| { + r.assets + .iter() + .any(|url| url.browser_download_url.contains(IEDRIVER_RELEASE)) + }) + .collect(); + + if !filtered_releases.is_empty() { + let assets = &filtered_releases.get(0).unwrap().assets; + let driver_releases: Vec<&Assets> = assets + .iter() + .filter(|url| url.browser_download_url.contains(IEDRIVER_RELEASE)) + .collect(); + let driver_url = &driver_releases.last().unwrap().browser_download_url; + RELEASE_URL.with(|url| { + *url.borrow_mut() = driver_url.to_string(); + }); + + let index_release = + driver_url.rfind(IEDRIVER_RELEASE).unwrap() + IEDRIVER_RELEASE.len(); + let driver_version = + parse_version(driver_url.as_str()[index_release..].to_string())?; + + if !browser_version.is_empty() { + metadata.drivers.push(create_driver_metadata( + browser_version, + self.driver_name, + &driver_version, + )); + write_metadata(&metadata, self.get_logger()); + } + + Ok(driver_version) + } else { + Err(format!("{} release not available", self.get_driver_name()).into()) } - - Ok(driver_version) } } } fn get_driver_url(&self) -> Result> { - let driver_version = self.get_driver_version(); - Ok(format!( - "{}download/selenium-{}/IEDriverServer_Win32_{}.zip", - DRIVER_URL, driver_version, driver_version - )) + let mut driver_url = "".to_string(); + RELEASE_URL.with(|url| { + driver_url = url.borrow().to_string(); + }); + if driver_url.is_empty() { + let driver_version = self.get_driver_version(); + let mut release_version = driver_version.to_string(); + if !driver_version.ends_with('0') { + // E.g.: version 4.8.1 is shipped within release 4.8.0 + let error_message = format!( + "Wrong {} version: '{}'", + self.get_driver_name(), + driver_version + ); + let index = release_version.rfind('.').ok_or(error_message)? + 1; + release_version = release_version[..index].to_string(); + release_version.push('0'); + } + driver_url = format!( + "{}download/selenium-{}/{}{}.zip", + DRIVER_URL, release_version, IEDRIVER_RELEASE, driver_version + ); + } + Ok(driver_url) } fn get_driver_path_in_cache(&self) -> PathBuf { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c676dbc0b42b8..dad7d8ba8f223 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -49,6 +49,7 @@ pub mod firefox; pub mod iexplorer; pub mod logger; pub mod metadata; +pub mod mirror; pub mod safari; pub mod safaritp; diff --git a/rust/src/mirror.rs b/rust/src/mirror.rs new file mode 100644 index 0000000000000..34e24bbd9ca5d --- /dev/null +++ b/rust/src/mirror.rs @@ -0,0 +1,41 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use crate::downloads::read_content_from_link; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::error::Error; + +pub const MIRROR_URL: &str = + "https://raw.githubusercontent.com/SeleniumHQ/selenium/trunk/common/mirror/selenium"; + +#[derive(Serialize, Deserialize)] +pub struct Assets { + pub browser_download_url: String, +} + +#[derive(Serialize, Deserialize)] +pub struct SeleniumRelease { + pub tag_name: String, + pub assets: Vec, +} + +pub fn get_mirror_response(http_client: &Client) -> Result, Box> { + let content = read_content_from_link(http_client, MIRROR_URL.to_string())?; + let mirror_response: Vec = serde_json::from_str(&content)?; + Ok(mirror_response) +} diff --git a/rust/tests/iexplorer_tests.rs b/rust/tests/iexplorer_tests.rs new file mode 100644 index 0000000000000..66ca80a6cbd3c --- /dev/null +++ b/rust/tests/iexplorer_tests.rs @@ -0,0 +1,36 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use assert_cmd::Command; + +use rstest::rstest; + +#[rstest] +#[case("4.8.0")] +#[case("4.8.1")] +fn iexplorer_test(#[case] driver_version: String) { + let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap(); + let cmd_assert = cmd + .args([ + "--browser", + "iexplorer", + "--driver-version", + &driver_version, + ]) + .assert(); + cmd_assert.success(); +}