Merge pull request #5 from redlib-org/improve_spoofing
Improve spoofing
This commit is contained in:
commit
d4c4d61ce8
1
build.rs
1
build.rs
@ -7,6 +7,7 @@ use std::os::unix::process::ExitStatusExt;
|
|||||||
use std::os::windows::process::ExitStatusExt;
|
use std::os::windows::process::ExitStatusExt;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
println!("cargo:rerun-if-changed=src/");
|
||||||
let output = String::from_utf8(
|
let output = String::from_utf8(
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["rev-parse", "HEAD"])
|
.args(["rev-parse", "HEAD"])
|
||||||
|
104
scripts/update_oauth_resources.sh
Executable file
104
scripts/update_oauth_resources.sh
Executable file
@ -0,0 +1,104 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Requirements
|
||||||
|
# - curl
|
||||||
|
# - rg
|
||||||
|
# - jq
|
||||||
|
|
||||||
|
# Fetch iOS app versions
|
||||||
|
ios_version_list=$(curl -s "https://ipaarchive.com/app/usa/1064216828" | rg "(20\d{2}\.\d+.\d+) / (\d+)" --only-matching -r "Version \$1/Build \$2" | sort | uniq)
|
||||||
|
|
||||||
|
# Count the number of lines in the version list
|
||||||
|
ios_app_count=$(echo "$ios_version_list" | wc -l)
|
||||||
|
|
||||||
|
echo -e "Fetching \e[34m$ios_app_count iOS app versions...\e[0m"
|
||||||
|
|
||||||
|
|
||||||
|
# Specify the filename as a variable
|
||||||
|
filename="src/oauth_resources.rs"
|
||||||
|
|
||||||
|
# Add comment that it is user generated
|
||||||
|
echo "// This file was generated by scripts/update_oauth_resources.sh" >> "$filename"
|
||||||
|
echo "// Rerun scripts/update_oauth_resources.sh to update this file" >> "$filename"
|
||||||
|
echo "// Please do not edit manually" >> "$filename"
|
||||||
|
echo "// Filled in with real app versions" >> "$filename"
|
||||||
|
|
||||||
|
# Open the array in the source file
|
||||||
|
echo "pub static IOS_APP_VERSION_LIST: &[&str; $ios_app_count] = &[" >> "$filename"
|
||||||
|
|
||||||
|
|
||||||
|
# Append the version list to the source file
|
||||||
|
echo "$ios_version_list" | while IFS= read -r line; do
|
||||||
|
echo " \"$line\"," >> "$filename"
|
||||||
|
echo -e "Fetched \e[34m$line\e[0m."
|
||||||
|
done
|
||||||
|
|
||||||
|
# Close the array in the source file
|
||||||
|
echo "];" >> "$filename"
|
||||||
|
|
||||||
|
# Fetch Android app versions
|
||||||
|
page_1=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions/" | rg "<a class=\"ver-item\" href=\"(/reddit/com\.reddit\.frontpage/download/phone-20\d{2}\.\d+\.\d+-apk)\" rel=\"nofollow\">" -r "https://apkcombo.com\$1" | sort | uniq)
|
||||||
|
# Append with pages
|
||||||
|
page_2=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=2" | rg "<a class=\"ver-item\" href=\"(/reddit/com\.reddit\.frontpage/download/phone-20\d{2}\.\d+\.\d+-apk)\" rel=\"nofollow\">" -r "https://apkcombo.com\$1" | sort | uniq)
|
||||||
|
page_3=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=3" | rg "<a class=\"ver-item\" href=\"(/reddit/com\.reddit\.frontpage/download/phone-20\d{2}\.\d+\.\d+-apk)\" rel=\"nofollow\">" -r "https://apkcombo.com\$1" | sort | uniq)
|
||||||
|
page_4=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=4" | rg "<a class=\"ver-item\" href=\"(/reddit/com\.reddit\.frontpage/download/phone-20\d{2}\.\d+\.\d+-apk)\" rel=\"nofollow\">" -r "https://apkcombo.com\$1" | sort | uniq)
|
||||||
|
page_5=$(curl -s "https://apkcombo.com/reddit/com.reddit.frontpage/old-versions?page=5" | rg "<a class=\"ver-item\" href=\"(/reddit/com\.reddit\.frontpage/download/phone-20\d{2}\.\d+\.\d+-apk)\" rel=\"nofollow\">" -r "https://apkcombo.com\$1" | sort | uniq)
|
||||||
|
|
||||||
|
# Concatenate all pages
|
||||||
|
versions="${page_1}"
|
||||||
|
versions+=$'\n'
|
||||||
|
versions+="${page_2}"
|
||||||
|
versions+=$'\n'
|
||||||
|
versions+="${page_3}"
|
||||||
|
versions+=$'\n'
|
||||||
|
versions+="${page_4}"
|
||||||
|
versions+=$'\n'
|
||||||
|
versions+="${page_5}"
|
||||||
|
|
||||||
|
# Count the number of lines in the version list
|
||||||
|
android_count=$(echo "$versions" | wc -l)
|
||||||
|
|
||||||
|
echo -e "Fetching \e[32m$android_count Android app versions...\e[0m"
|
||||||
|
|
||||||
|
# Append to the source file
|
||||||
|
echo "pub static ANDROID_APP_VERSION_LIST: &[&str; $android_count] = &[" >> "$filename"
|
||||||
|
|
||||||
|
# For each in versions, curl the page and extract the build number
|
||||||
|
echo "$versions" | while IFS= read -r line; do
|
||||||
|
fetch_page=$(curl -s "$line")
|
||||||
|
build=$(echo "$fetch_page" | rg "<span class=\"vercode\">\((\d+)\)</span>" --only-matching -r "\$1" | head -n1)
|
||||||
|
version=$(echo "$fetch_page" | rg "<span class=\"vername\">Reddit (20\d{2}\.\d+\.\d+)</span>" --only-matching -r "\$1" | head -n1)
|
||||||
|
echo " \"Version $version/Build $build\"," >> "$filename"
|
||||||
|
echo -e "Fetched \e[32mVersion $version/Build $build\e[0m"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Close the array in the source file
|
||||||
|
echo "];" >> "$filename"
|
||||||
|
|
||||||
|
# Retrieve iOS versions
|
||||||
|
table=$(curl -s "https://en.wikipedia.org/w/api.php?action=parse&page=IOS_17&prop=wikitext§ion=31&format=json" | jq ".parse.wikitext.\"*\"" | rg "(17\.[\d\.]*)\\\n\|(\w*)\\\n\|" --only-matching -r "Version \$1 (Build \$2)")
|
||||||
|
|
||||||
|
# Count the number of lines in the version list
|
||||||
|
ios_count=$(echo "$table" | wc -l)
|
||||||
|
|
||||||
|
echo -e "Fetching \e[34m$ios_count iOS versions...\e[0m"
|
||||||
|
|
||||||
|
# Append to the source file
|
||||||
|
echo "pub static IOS_OS_VERSION_LIST: &[&str; $ios_count] = &[" >> "$filename"
|
||||||
|
|
||||||
|
# For each in versions, curl the page and extract the build number
|
||||||
|
echo "$table" | while IFS= read -r line; do
|
||||||
|
echo " \"$line\"," >> "$filename"
|
||||||
|
echo -e "Fetched $line\e[0m"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Close the array in the source file
|
||||||
|
echo "];" >> "$filename"
|
||||||
|
|
||||||
|
echo -e "\e[34mRetrieved $ios_app_count iOS app versions.\e[0m"
|
||||||
|
echo -e "\e[32mRetrieved $android_count Android app versions.\e[0m"
|
||||||
|
echo -e "\e[34mRetrieved $ios_count iOS versions.\e[0m"
|
||||||
|
|
||||||
|
echo -e "\e[34mTotal: $((ios_app_count + android_count + ios_count))\e[0m"
|
||||||
|
|
||||||
|
echo -e "\e[32mSuccess!\e[0m"
|
@ -20,12 +20,12 @@ use crate::server::RequestExt;
|
|||||||
|
|
||||||
const REDDIT_URL_BASE: &str = "https://oauth.reddit.com";
|
const REDDIT_URL_BASE: &str = "https://oauth.reddit.com";
|
||||||
|
|
||||||
pub(crate) static CLIENT: Lazy<Client<HttpsConnector<HttpConnector>>> = Lazy::new(|| {
|
pub static CLIENT: Lazy<Client<HttpsConnector<HttpConnector>>> = Lazy::new(|| {
|
||||||
let https = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots().https_only().enable_http1().build();
|
let https = hyper_rustls::HttpsConnectorBuilder::new().with_native_roots().https_only().enable_http1().build();
|
||||||
client::Client::builder().build(https)
|
client::Client::builder().build(https)
|
||||||
});
|
});
|
||||||
|
|
||||||
pub(crate) static OAUTH_CLIENT: Lazy<RwLock<Oauth>> = Lazy::new(|| {
|
pub static OAUTH_CLIENT: Lazy<RwLock<Oauth>> = Lazy::new(|| {
|
||||||
let client = block_on(Oauth::new());
|
let client = block_on(Oauth::new());
|
||||||
tokio::spawn(token_daemon());
|
tokio::spawn(token_daemon());
|
||||||
RwLock::new(client)
|
RwLock::new(client)
|
||||||
@ -332,7 +332,7 @@ pub async fn json(path: String, quarantine: bool) -> Result<Value, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn test_localization_popular() {
|
async fn test_localization_popular() {
|
||||||
let val = json("/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL".to_string(), false).await.unwrap();
|
let val = json("/r/popular/hot.json?&raw_json=1&geo_filter=GLOBAL".to_string(), false).await.unwrap();
|
||||||
assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap());
|
assert_eq!("GLOBAL", val["data"]["geo_filter"].as_str().unwrap());
|
||||||
|
@ -7,11 +7,11 @@ use std::{env::var, fs::read_to_string};
|
|||||||
//
|
//
|
||||||
// This is the local static that is initialized at runtime (technically at
|
// This is the local static that is initialized at runtime (technically at
|
||||||
// first request) and contains the instance settings.
|
// first request) and contains the instance settings.
|
||||||
pub(crate) static CONFIG: Lazy<Config> = Lazy::new(Config::load);
|
pub static CONFIG: Lazy<Config> = Lazy::new(Config::load);
|
||||||
|
|
||||||
// This serves as the frontend for the Pushshift API - on removed comments, this URL will
|
// This serves as the frontend for the Pushshift API - on removed comments, this URL will
|
||||||
// be the base of a link, to display removed content (on another site).
|
// be the base of a link, to display removed content (on another site).
|
||||||
pub(crate) const DEFAULT_PUSHSHIFT_FRONTEND: &str = "www.unddit.com";
|
pub const DEFAULT_PUSHSHIFT_FRONTEND: &str = "www.unddit.com";
|
||||||
|
|
||||||
/// Stores the configuration parsed from the environment variables and the
|
/// Stores the configuration parsed from the environment variables and the
|
||||||
/// config file. `Config::Default()` contains None for each setting.
|
/// config file. `Config::Default()` contains None for each setting.
|
||||||
@ -104,7 +104,7 @@ impl Config {
|
|||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
let load_config = |name: &str| {
|
let load_config = |name: &str| {
|
||||||
let new_file = read_to_string(name);
|
let new_file = read_to_string(name);
|
||||||
new_file.ok().and_then(|new_file| toml::from_str::<Config>(&new_file).ok())
|
new_file.ok().and_then(|new_file| toml::from_str::<Self>(&new_file).ok())
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = load_config("redlib.toml").or(load_config("libreddit.toml")).unwrap_or_default();
|
let config = load_config("redlib.toml").or(load_config("libreddit.toml")).unwrap_or_default();
|
||||||
@ -168,7 +168,7 @@ fn get_setting_from_config(name: &str, config: &Config) -> Option<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves setting from environment variable or config file.
|
/// Retrieves setting from environment variable or config file.
|
||||||
pub(crate) fn get_setting(name: &str) -> Option<String> {
|
pub fn get_setting(name: &str) -> Option<String> {
|
||||||
get_setting_from_config(name, &CONFIG)
|
get_setting_from_config(name, &CONFIG)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use time::OffsetDateTime;
|
|||||||
// This is the local static that is intialized at runtime (technically at
|
// This is the local static that is intialized at runtime (technically at
|
||||||
// the first request to the info endpoint) and contains the data
|
// the first request to the info endpoint) and contains the data
|
||||||
// retrieved from the info endpoint.
|
// retrieved from the info endpoint.
|
||||||
pub(crate) static INSTANCE_INFO: Lazy<InstanceInfo> = Lazy::new(InstanceInfo::new);
|
pub static INSTANCE_INFO: Lazy<InstanceInfo> = Lazy::new(InstanceInfo::new);
|
||||||
|
|
||||||
/// Handles instance info endpoint
|
/// Handles instance info endpoint
|
||||||
pub async fn instance_info(req: Request<Body>) -> Result<Response<Body>, String> {
|
pub async fn instance_info(req: Request<Body>) -> Result<Response<Body>, String> {
|
||||||
@ -84,7 +84,7 @@ fn info_html(req: Request<Body>) -> Result<Response<Body>, Error> {
|
|||||||
Response::builder().status(200).header("content-type", "text/html; charset=utf8").body(Body::from(message))
|
Response::builder().status(200).header("content-type", "text/html; charset=utf8").body(Body::from(message))
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub(crate) struct InstanceInfo {
|
pub struct InstanceInfo {
|
||||||
package_name: String,
|
package_name: String,
|
||||||
crate_version: String,
|
crate_version: String,
|
||||||
git_commit: String,
|
git_commit: String,
|
||||||
|
@ -7,6 +7,7 @@ mod config;
|
|||||||
mod duplicates;
|
mod duplicates;
|
||||||
mod instance_info;
|
mod instance_info;
|
||||||
mod oauth;
|
mod oauth;
|
||||||
|
mod oauth_resources;
|
||||||
mod post;
|
mod post;
|
||||||
mod search;
|
mod search;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
132
src/oauth.rs
132
src/oauth.rs
@ -1,6 +1,9 @@
|
|||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
use crate::client::{CLIENT, OAUTH_CLIENT};
|
use crate::{
|
||||||
|
client::{CLIENT, OAUTH_CLIENT},
|
||||||
|
oauth_resources::{ANDROID_APP_VERSION_LIST, IOS_APP_VERSION_LIST, IOS_OS_VERSION_LIST},
|
||||||
|
};
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use hyper::{client, Body, Method, Request};
|
use hyper::{client, Body, Method, Request};
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -12,26 +15,10 @@ static REDDIT_IOS_OAUTH_CLIENT_ID: &str = "LNDo9k1o8UAEUw";
|
|||||||
|
|
||||||
static AUTH_ENDPOINT: &str = "https://accounts.reddit.com";
|
static AUTH_ENDPOINT: &str = "https://accounts.reddit.com";
|
||||||
|
|
||||||
// Various Android user agents - build numbers from valid APK variants
|
// Spoofed client for Android and iOS devices
|
||||||
pub(crate) static ANDROID_USER_AGENT: [&str; 3] = [
|
|
||||||
"Reddit/Version 2023.21.0/Build 956283/Android 13",
|
|
||||||
"Reddit/Version 2023.21.0/Build 968223/Android 10",
|
|
||||||
"Reddit/Version 2023.21.0/Build 946732/Android 12",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Various iOS user agents - iOS versions.
|
|
||||||
pub(crate) static IOS_USER_AGENT: [&str; 3] = [
|
|
||||||
"Reddit/Version 2023.22.0/Build 613580/iOS Version 17.0 (Build 21A5248V)",
|
|
||||||
"Reddit/Version 2023.22.0/Build 613580/iOS Version 16.0 (Build 20A5328h)",
|
|
||||||
"Reddit/Version 2023.22.0/Build 613580/iOS Version 16.5",
|
|
||||||
];
|
|
||||||
// Various iOS device codes. iPhone 11 displays as `iPhone12,1`
|
|
||||||
// I just changed the number a few times for some plausible values
|
|
||||||
pub(crate) static IOS_DEVICES: [&str; 5] = ["iPhone8,1", "iPhone11,1", "iPhone12,1", "iPhone13,1", "iPhone14,1"];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct Oauth {
|
pub struct Oauth {
|
||||||
// Currently unused, may be necessary if we decide to support GQL in the future
|
pub(crate) initial_headers: HashMap<String, String>,
|
||||||
pub(crate) headers_map: HashMap<String, String>,
|
pub(crate) headers_map: HashMap<String, String>,
|
||||||
pub(crate) token: String,
|
pub(crate) token: String,
|
||||||
expires_in: u64,
|
expires_in: u64,
|
||||||
@ -40,17 +27,19 @@ pub(crate) struct Oauth {
|
|||||||
|
|
||||||
impl Oauth {
|
impl Oauth {
|
||||||
pub(crate) async fn new() -> Self {
|
pub(crate) async fn new() -> Self {
|
||||||
let mut oauth = Oauth::default();
|
let mut oauth = Self::default();
|
||||||
oauth.login().await;
|
oauth.login().await;
|
||||||
oauth
|
oauth
|
||||||
}
|
}
|
||||||
pub(crate) fn default() -> Self {
|
pub(crate) fn default() -> Self {
|
||||||
// Generate a random device to spoof
|
// Generate a random device to spoof
|
||||||
let device = Device::random();
|
let device = Device::random();
|
||||||
let headers = device.headers.clone();
|
let headers_map = device.headers.clone();
|
||||||
|
let initial_headers = device.initial_headers.clone();
|
||||||
// For now, just insert headers - no token request
|
// For now, just insert headers - no token request
|
||||||
Oauth {
|
Self {
|
||||||
headers_map: headers,
|
headers_map,
|
||||||
|
initial_headers,
|
||||||
token: String::new(),
|
token: String::new(),
|
||||||
expires_in: 0,
|
expires_in: 0,
|
||||||
device,
|
device,
|
||||||
@ -62,16 +51,8 @@ impl Oauth {
|
|||||||
let mut builder = Request::builder().method(Method::POST).uri(&url);
|
let mut builder = Request::builder().method(Method::POST).uri(&url);
|
||||||
|
|
||||||
// Add headers from spoofed client
|
// Add headers from spoofed client
|
||||||
for (key, value) in self.headers_map.iter() {
|
for (key, value) in self.initial_headers.iter() {
|
||||||
// Skip Authorization header - won't be present in `Device` struct
|
builder = builder.header(key, value);
|
||||||
// and will only be there in subsequent token refreshes.
|
|
||||||
// Sending a bearer auth token when requesting one is a bad idea
|
|
||||||
// Normally, you'd want to send it along to authenticate a refreshed token,
|
|
||||||
// but neither Android nor iOS does this - it just requests a new token.
|
|
||||||
// We try to match behavior as closely as possible.
|
|
||||||
if key != "Authorization" {
|
|
||||||
builder = builder.header(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Set up HTTP Basic Auth - basically just the const OAuth ID's with no password,
|
// Set up HTTP Basic Auth - basically just the const OAuth ID's with no password,
|
||||||
// Base64-encoded. https://en.wikipedia.org/wiki/Basic_access_authentication
|
// Base64-encoded. https://en.wikipedia.org/wiki/Basic_access_authentication
|
||||||
@ -82,7 +63,7 @@ impl Oauth {
|
|||||||
|
|
||||||
// Set JSON body. I couldn't tell you what this means. But that's what the client sends
|
// Set JSON body. I couldn't tell you what this means. But that's what the client sends
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"scopes": ["*","email","pii"]
|
"scopes": ["*","email"]
|
||||||
});
|
});
|
||||||
let body = Body::from(json.to_string());
|
let body = Body::from(json.to_string());
|
||||||
|
|
||||||
@ -100,6 +81,11 @@ impl Oauth {
|
|||||||
self.headers_map.insert("x-reddit-loid".to_owned(), header.to_str().ok()?.to_string());
|
self.headers_map.insert("x-reddit-loid".to_owned(), header.to_str().ok()?.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same with x-reddit-session
|
||||||
|
if let Some(header) = resp.headers().get("x-reddit-session") {
|
||||||
|
self.headers_map.insert("x-reddit-session".to_owned(), header.to_str().ok()?.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// Serialize response
|
// Serialize response
|
||||||
let body_bytes = hyper::body::to_bytes(resp.into_body()).await.ok()?;
|
let body_bytes = hyper::body::to_bytes(resp.into_body()).await.ok()?;
|
||||||
let json: serde_json::Value = serde_json::from_slice(&body_bytes).ok()?;
|
let json: serde_json::Value = serde_json::from_slice(&body_bytes).ok()?;
|
||||||
@ -109,7 +95,7 @@ impl Oauth {
|
|||||||
self.expires_in = json.get("expires_in")?.as_u64()?;
|
self.expires_in = json.get("expires_in")?.as_u64()?;
|
||||||
self.headers_map.insert("Authorization".to_owned(), format!("Bearer {}", self.token));
|
self.headers_map.insert("Authorization".to_owned(), format!("Bearer {}", self.token));
|
||||||
|
|
||||||
info!("✅ Success - Retrieved token \"{}...\", expires in {}", &self.token[..32], self.expires_in);
|
info!("[✅] Success - Retrieved token \"{}...\", expires in {}", &self.token[..32], self.expires_in);
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
@ -123,7 +109,7 @@ impl Oauth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn token_daemon() {
|
pub async fn token_daemon() {
|
||||||
// Monitor for refreshing token
|
// Monitor for refreshing token
|
||||||
loop {
|
loop {
|
||||||
// Get expiry time - be sure to not hold the read lock
|
// Get expiry time - be sure to not hold the read lock
|
||||||
@ -132,22 +118,22 @@ pub(crate) async fn token_daemon() {
|
|||||||
// sleep for the expiry time minus 2 minutes
|
// sleep for the expiry time minus 2 minutes
|
||||||
let duration = Duration::from_secs(expires_in - 120);
|
let duration = Duration::from_secs(expires_in - 120);
|
||||||
|
|
||||||
info!("Waiting for {duration:?} seconds before refreshing OAuth token...");
|
info!("[⏳] Waiting for {duration:?} seconds before refreshing OAuth token...");
|
||||||
|
|
||||||
tokio::time::sleep(duration).await;
|
tokio::time::sleep(duration).await;
|
||||||
|
|
||||||
info!("[{duration:?} ELAPSED] Refreshing OAuth token...");
|
info!("[⌛] {duration:?} Elapsed! Refreshing OAuth token...");
|
||||||
|
|
||||||
// Refresh token - in its own scope
|
// Refresh token - in its own scope
|
||||||
{
|
{
|
||||||
let mut client = OAUTH_CLIENT.write().await;
|
OAUTH_CLIENT.write().await.refresh().await;
|
||||||
client.refresh().await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct Device {
|
struct Device {
|
||||||
oauth_id: String,
|
oauth_id: String,
|
||||||
|
initial_headers: HashMap<String, String>,
|
||||||
headers: HashMap<String, String>,
|
headers: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,8 +142,11 @@ impl Device {
|
|||||||
// Generate uuid
|
// Generate uuid
|
||||||
let uuid = uuid::Uuid::new_v4().to_string();
|
let uuid = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
// Select random user agent from ANDROID_USER_AGENT
|
// Generate random user-agent
|
||||||
let android_user_agent = choose(&ANDROID_USER_AGENT).to_string();
|
let android_app_version = choose(ANDROID_APP_VERSION_LIST).to_string();
|
||||||
|
let android_version = fastrand::u8(9..=14);
|
||||||
|
|
||||||
|
let android_user_agent = format!("Reddit/{android_app_version}/Android {android_version}");
|
||||||
|
|
||||||
// Android device headers
|
// Android device headers
|
||||||
let headers = HashMap::from([
|
let headers = HashMap::from([
|
||||||
@ -166,61 +155,84 @@ impl Device {
|
|||||||
("User-Agent".into(), android_user_agent),
|
("User-Agent".into(), android_user_agent),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
info!("Spoofing Android client with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_ANDROID_OAUTH_CLIENT_ID}\"");
|
info!("[🔄] Spoofing Android client with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_ANDROID_OAUTH_CLIENT_ID}\"");
|
||||||
|
|
||||||
Device {
|
Self {
|
||||||
oauth_id: REDDIT_ANDROID_OAUTH_CLIENT_ID.to_string(),
|
oauth_id: REDDIT_ANDROID_OAUTH_CLIENT_ID.to_string(),
|
||||||
headers,
|
headers: headers.clone(),
|
||||||
|
initial_headers: headers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn ios() -> Self {
|
fn ios() -> Self {
|
||||||
// Generate uuid
|
// Generate uuid
|
||||||
let uuid = uuid::Uuid::new_v4().to_string();
|
let uuid = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
// Select random user agent from IOS_USER_AGENT
|
// Generate random user-agent
|
||||||
let ios_user_agent = choose(&IOS_USER_AGENT).to_string();
|
let ios_app_version = choose(IOS_APP_VERSION_LIST).to_string();
|
||||||
|
let ios_os_version = choose(IOS_OS_VERSION_LIST).to_string();
|
||||||
|
let ios_user_agent = format!("Reddit/{ios_app_version}/iOS {ios_os_version}");
|
||||||
|
|
||||||
// Select random iOS device from IOS_DEVICES
|
// Generate random device
|
||||||
let ios_device = choose(&IOS_DEVICES).to_string();
|
let ios_device_num = fastrand::u8(8..=15).to_string();
|
||||||
|
let ios_device = format!("iPhone{ios_device_num},1").to_string();
|
||||||
|
|
||||||
// iOS device headers
|
let initial_headers = HashMap::from([
|
||||||
|
("X-Reddit-DPR".into(), "2".into()),
|
||||||
|
("User-Agent".into(), ios_user_agent.clone()),
|
||||||
|
("Device-Name".into(), ios_device.clone()),
|
||||||
|
]);
|
||||||
let headers = HashMap::from([
|
let headers = HashMap::from([
|
||||||
("X-Reddit-DPR".into(), "2".into()),
|
("X-Reddit-DPR".into(), "2".into()),
|
||||||
("Device-Name".into(), ios_device.clone()),
|
("Device-Name".into(), ios_device.clone()),
|
||||||
("X-Reddit-Device-Id".into(), uuid.clone()),
|
|
||||||
("User-Agent".into(), ios_user_agent),
|
("User-Agent".into(), ios_user_agent),
|
||||||
("Client-Vendor-Id".into(), uuid.clone()),
|
("Client-Vendor-Id".into(), uuid.clone()),
|
||||||
|
("x-dev-ad-id".into(), "00000000-0000-0000-0000-000000000000".into()),
|
||||||
|
("Reddit-User_Id".into(), "anonymous_browsing_mode".into()),
|
||||||
|
("x-reddit-device-id".into(), uuid.clone()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
info!("Spoofing iOS client {ios_device} with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_IOS_OAUTH_CLIENT_ID}\"");
|
info!("[🔄] Spoofing iOS client {ios_device} with headers: {headers:?}, uuid: \"{uuid}\", and OAuth ID \"{REDDIT_IOS_OAUTH_CLIENT_ID}\"");
|
||||||
|
|
||||||
Device {
|
Self {
|
||||||
oauth_id: REDDIT_IOS_OAUTH_CLIENT_ID.to_string(),
|
oauth_id: REDDIT_IOS_OAUTH_CLIENT_ID.to_string(),
|
||||||
|
initial_headers,
|
||||||
headers,
|
headers,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Randomly choose a device
|
// Randomly choose a device
|
||||||
fn random() -> Self {
|
fn random() -> Self {
|
||||||
if fastrand::bool() {
|
if fastrand::bool() {
|
||||||
Device::android()
|
Self::android()
|
||||||
} else {
|
} else {
|
||||||
Device::ios()
|
Self::ios()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waiting on fastrand 2.0.0 for the `choose` function
|
|
||||||
// https://github.com/smol-rs/fastrand/pull/59/
|
|
||||||
fn choose<T: Copy>(list: &[T]) -> T {
|
fn choose<T: Copy>(list: &[T]) -> T {
|
||||||
*fastrand::choose_multiple(list.iter(), 1)[0]
|
*fastrand::choose_multiple(list.iter(), 1)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn test_oauth_client() {
|
async fn test_oauth_client() {
|
||||||
assert!(!OAUTH_CLIENT.read().await.token.is_empty());
|
assert!(!OAUTH_CLIENT.read().await.token.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn test_oauth_client_refresh() {
|
async fn test_oauth_client_refresh() {
|
||||||
OAUTH_CLIENT.write().await.refresh().await.unwrap();
|
OAUTH_CLIENT.write().await.refresh().await.unwrap();
|
||||||
}
|
}
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
|
async fn test_oauth_token_exists() {
|
||||||
|
assert!(!OAUTH_CLIENT.read().await.token.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
|
async fn test_oauth_headers_len() {
|
||||||
|
assert!(OAUTH_CLIENT.read().await.headers_map.len() >= 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_creating_device() {
|
||||||
|
Device::random();
|
||||||
|
}
|
||||||
|
231
src/oauth_resources.rs
Normal file
231
src/oauth_resources.rs
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
pub static IOS_APP_VERSION_LIST: &[&str; 67] = &[
|
||||||
|
"Version 2020.0.0/Build 306960",
|
||||||
|
"Version 2020.10.0/Build 307041",
|
||||||
|
"Version 2020.10.1/Build 307047",
|
||||||
|
"Version 2020.1.0/Build 306966",
|
||||||
|
"Version 2020.11.0/Build 307049",
|
||||||
|
"Version 2020.11.1/Build 307063",
|
||||||
|
"Version 2020.12.0/Build 307070",
|
||||||
|
"Version 2020.13.0/Build 307072",
|
||||||
|
"Version 2020.13.1/Build 307075",
|
||||||
|
"Version 2020.14.0/Build 307077",
|
||||||
|
"Version 2020.14.1/Build 307080",
|
||||||
|
"Version 2020.15.0/Build 307084",
|
||||||
|
"Version 2020.16.0/Build 307090",
|
||||||
|
"Version 2020.17.0/Build 307093",
|
||||||
|
"Version 2020.19.0/Build 307137",
|
||||||
|
"Version 2020.20.0/Build 307156",
|
||||||
|
"Version 2020.20.1/Build 307159",
|
||||||
|
"Version 2020.2.0/Build 306969",
|
||||||
|
"Version 2020.21.0/Build 307162",
|
||||||
|
"Version 2020.21.1/Build 307165",
|
||||||
|
"Version 2020.22.0/Build 307177",
|
||||||
|
"Version 2020.22.1/Build 307181",
|
||||||
|
"Version 2020.23.0/Build 307183",
|
||||||
|
"Version 2020.24.0/Build 307189",
|
||||||
|
"Version 2020.25.0/Build 307198",
|
||||||
|
"Version 2020.26.0/Build 307205",
|
||||||
|
"Version 2020.26.1/Build 307213",
|
||||||
|
"Version 2020.27.0/Build 307229",
|
||||||
|
"Version 2020.28.0/Build 307233",
|
||||||
|
"Version 2020.29.0/Build 307235",
|
||||||
|
"Version 2020.30.0/Build 307238",
|
||||||
|
"Version 2020.3.0/Build 306971",
|
||||||
|
"Version 2020.31.0/Build 307240",
|
||||||
|
"Version 2020.31.1/Build 307246",
|
||||||
|
"Version 2020.32.0/Build 307250",
|
||||||
|
"Version 2020.33.0/Build 307252",
|
||||||
|
"Version 2020.34.0/Build 307260",
|
||||||
|
"Version 2020.35.0/Build 307262",
|
||||||
|
"Version 2020.36.0/Build 307265",
|
||||||
|
"Version 2020.37.0/Build 307272",
|
||||||
|
"Version 2020.38.0/Build 307286",
|
||||||
|
"Version 2020.39.0/Build 307306",
|
||||||
|
"Version 2020.4.0/Build 306978",
|
||||||
|
"Version 2020.5.0/Build 306993",
|
||||||
|
"Version 2020.5.1/Build 307005",
|
||||||
|
"Version 2020.6.0/Build 307007",
|
||||||
|
"Version 2020.7.0/Build 307012",
|
||||||
|
"Version 2020.8.0/Build 307014",
|
||||||
|
"Version 2020.8.1/Build 307017",
|
||||||
|
"Version 2020.9.0/Build 307035",
|
||||||
|
"Version 2020.9.1/Build 307039",
|
||||||
|
"Version 2023.18.0/Build 310494",
|
||||||
|
"Version 2023.19.0/Build 310507",
|
||||||
|
"Version 2023.20.0/Build 310535",
|
||||||
|
"Version 2023.21.0/Build 310560",
|
||||||
|
"Version 2023.22.0/Build 613580",
|
||||||
|
"Version 2023.23.0/Build 310613",
|
||||||
|
"Version 2023.23.1/Build 613639",
|
||||||
|
"Version 2023.24.0/Build 613663",
|
||||||
|
"Version 2023.25.0/Build 613739",
|
||||||
|
"Version 2023.26.0/Build 613749",
|
||||||
|
"Version 2023.27.0/Build 613771",
|
||||||
|
"Version 2023.28.0/Build 613803",
|
||||||
|
"Version 2023.28.1/Build 613809",
|
||||||
|
"Version 2023.29.0/Build 613825",
|
||||||
|
"Version 2023.30.0/Build 613849",
|
||||||
|
"Version 2023.31.0/Build 613864",
|
||||||
|
];
|
||||||
|
pub static ANDROID_APP_VERSION_LIST: &[&str; 150] = &[
|
||||||
|
"Version 2023.25.1/Build 1018737",
|
||||||
|
"Version 2023.26.0/Build 1019073",
|
||||||
|
"Version 2023.27.0/Build 1031923",
|
||||||
|
"Version 2023.28.0/Build 1046887",
|
||||||
|
"Version 2023.29.0/Build 1059855",
|
||||||
|
"Version 2023.30.0/Build 1078734",
|
||||||
|
"Version 2023.31.0/Build 1091027",
|
||||||
|
"Version 2023.32.0/Build 1109919",
|
||||||
|
"Version 2023.32.1/Build 1114141",
|
||||||
|
"Version 2023.33.1/Build 1129741",
|
||||||
|
"Version 2023.34.0/Build 1144243",
|
||||||
|
"Version 2023.35.0/Build 1157967",
|
||||||
|
"Version 2023.36.0/Build 1168982",
|
||||||
|
"Version 2023.37.0/Build 1182743",
|
||||||
|
"Version 2023.38.0/Build 1198522",
|
||||||
|
"Version 2023.39.0/Build 1211607",
|
||||||
|
"Version 2023.39.1/Build 1221505",
|
||||||
|
"Version 2023.40.0/Build 1221521",
|
||||||
|
"Version 2023.41.0/Build 1233125",
|
||||||
|
"Version 2023.41.1/Build 1239615",
|
||||||
|
"Version 2023.42.0/Build 1245088",
|
||||||
|
"Version 2023.43.0/Build 1257426",
|
||||||
|
"Version 2023.44.0/Build 1268622",
|
||||||
|
"Version 2023.45.0/Build 1281371",
|
||||||
|
"Version 2023.47.0/Build 1303604",
|
||||||
|
"Version 2023.48.0/Build 1319123",
|
||||||
|
"Version 2023.49.0/Build 1321715",
|
||||||
|
"Version 2023.49.1/Build 1322281",
|
||||||
|
"Version 2023.50.0/Build 1332338",
|
||||||
|
"Version 2023.50.1/Build 1345844",
|
||||||
|
"Version 2023.02.0/Build 717912",
|
||||||
|
"Version 2023.03.0/Build 729220",
|
||||||
|
"Version 2023.04.0/Build 744681",
|
||||||
|
"Version 2023.05.0/Build 755453",
|
||||||
|
"Version 2023.06.0/Build 775017",
|
||||||
|
"Version 2023.07.0/Build 788827",
|
||||||
|
"Version 2023.07.1/Build 790267",
|
||||||
|
"Version 2023.08.0/Build 798718",
|
||||||
|
"Version 2023.09.0/Build 812015",
|
||||||
|
"Version 2023.09.1/Build 816833",
|
||||||
|
"Version 2023.10.0/Build 821148",
|
||||||
|
"Version 2023.11.0/Build 830610",
|
||||||
|
"Version 2023.12.0/Build 841150",
|
||||||
|
"Version 2023.13.0/Build 852246",
|
||||||
|
"Version 2023.14.0/Build 861593",
|
||||||
|
"Version 2023.14.1/Build 864826",
|
||||||
|
"Version 2023.15.0/Build 870628",
|
||||||
|
"Version 2023.16.0/Build 883294",
|
||||||
|
"Version 2023.16.1/Build 886269",
|
||||||
|
"Version 2023.17.0/Build 896030",
|
||||||
|
"Version 2023.17.1/Build 900542",
|
||||||
|
"Version 2023.18.0/Build 911877",
|
||||||
|
"Version 2023.19.0/Build 927681",
|
||||||
|
"Version 2023.20.0/Build 943980",
|
||||||
|
"Version 2023.20.1/Build 946732",
|
||||||
|
"Version 2023.21.0/Build 956283",
|
||||||
|
"Version 2023.22.0/Build 968223",
|
||||||
|
"Version 2023.23.0/Build 983896",
|
||||||
|
"Version 2023.24.0/Build 998541",
|
||||||
|
"Version 2023.25.0/Build 1014750",
|
||||||
|
"Version 2022.24.0/Build 510950",
|
||||||
|
"Version 2022.24.1/Build 513462",
|
||||||
|
"Version 2022.25.0/Build 515072",
|
||||||
|
"Version 2022.25.1/Build 516394",
|
||||||
|
"Version 2022.25.2/Build 519915",
|
||||||
|
"Version 2022.26.0/Build 521193",
|
||||||
|
"Version 2022.27.0/Build 527406",
|
||||||
|
"Version 2022.27.1/Build 529687",
|
||||||
|
"Version 2022.28.0/Build 533235",
|
||||||
|
"Version 2022.30.0/Build 548620",
|
||||||
|
"Version 2022.31.0/Build 556666",
|
||||||
|
"Version 2022.31.1/Build 562612",
|
||||||
|
"Version 2022.32.0/Build 567875",
|
||||||
|
"Version 2022.33.0/Build 572600",
|
||||||
|
"Version 2022.34.0/Build 579352",
|
||||||
|
"Version 2022.35.0/Build 588016",
|
||||||
|
"Version 2022.35.1/Build 589034",
|
||||||
|
"Version 2022.36.0/Build 593102",
|
||||||
|
"Version 2022.37.0/Build 601691",
|
||||||
|
"Version 2022.38.0/Build 607460",
|
||||||
|
"Version 2022.39.0/Build 615385",
|
||||||
|
"Version 2022.39.1/Build 619019",
|
||||||
|
"Version 2022.40.0/Build 624782",
|
||||||
|
"Version 2022.41.0/Build 630468",
|
||||||
|
"Version 2022.41.1/Build 634168",
|
||||||
|
"Version 2022.42.0/Build 638508",
|
||||||
|
"Version 2022.43.0/Build 648277",
|
||||||
|
"Version 2022.44.0/Build 664348",
|
||||||
|
"Version 2022.45.0/Build 677985",
|
||||||
|
"Version 2023.01.0/Build 709875",
|
||||||
|
"Version 2021.45.0/Build 387663",
|
||||||
|
"Version 2021.46.0/Build 392043",
|
||||||
|
"Version 2021.47.0/Build 394342",
|
||||||
|
"Version 2022.10.0/Build 429896",
|
||||||
|
"Version 2022.1.0/Build 402829",
|
||||||
|
"Version 2022.11.0/Build 433004",
|
||||||
|
"Version 2022.12.0/Build 436848",
|
||||||
|
"Version 2022.13.0/Build 442084",
|
||||||
|
"Version 2022.13.1/Build 444621",
|
||||||
|
"Version 2022.14.1/Build 452742",
|
||||||
|
"Version 2022.15.0/Build 455453",
|
||||||
|
"Version 2022.16.0/Build 462377",
|
||||||
|
"Version 2022.17.0/Build 468480",
|
||||||
|
"Version 2022.18.0/Build 473740",
|
||||||
|
"Version 2022.19.1/Build 482464",
|
||||||
|
"Version 2022.20.0/Build 487703",
|
||||||
|
"Version 2022.2.0/Build 405543",
|
||||||
|
"Version 2022.21.0/Build 492436",
|
||||||
|
"Version 2022.22.0/Build 498700",
|
||||||
|
"Version 2022.23.0/Build 502374",
|
||||||
|
"Version 2022.23.1/Build 506606",
|
||||||
|
"Version 2022.3.0/Build 408637",
|
||||||
|
"Version 2022.4.0/Build 411368",
|
||||||
|
"Version 2022.5.0/Build 414731",
|
||||||
|
"Version 2022.6.0/Build 418391",
|
||||||
|
"Version 2022.6.1/Build 419585",
|
||||||
|
"Version 2022.6.2/Build 420562",
|
||||||
|
"Version 2022.7.0/Build 420849",
|
||||||
|
"Version 2022.8.0/Build 423906",
|
||||||
|
"Version 2022.9.0/Build 426592",
|
||||||
|
"Version 2021.17.0/Build 323213",
|
||||||
|
"Version 2021.18.0/Build 324849",
|
||||||
|
"Version 2021.19.0/Build 325762",
|
||||||
|
"Version 2021.20.0/Build 326964",
|
||||||
|
"Version 2021.21.0/Build 327703",
|
||||||
|
"Version 2021.21.1/Build 328461",
|
||||||
|
"Version 2021.22.0/Build 329696",
|
||||||
|
"Version 2021.23.0/Build 331631",
|
||||||
|
"Version 2021.24.0/Build 333951",
|
||||||
|
"Version 2021.25.0/Build 335451",
|
||||||
|
"Version 2021.26.0/Build 336739",
|
||||||
|
"Version 2021.27.0/Build 338857",
|
||||||
|
"Version 2021.28.0/Build 340747",
|
||||||
|
"Version 2021.29.0/Build 342342",
|
||||||
|
"Version 2021.30.0/Build 343820",
|
||||||
|
"Version 2021.31.0/Build 346485",
|
||||||
|
"Version 2021.32.0/Build 349507",
|
||||||
|
"Version 2021.33.0/Build 351843",
|
||||||
|
"Version 2021.34.0/Build 353911",
|
||||||
|
"Version 2021.35.0/Build 355878",
|
||||||
|
"Version 2021.36.0/Build 359254",
|
||||||
|
"Version 2021.36.1/Build 360572",
|
||||||
|
"Version 2021.37.0/Build 361905",
|
||||||
|
"Version 2021.38.0/Build 365032",
|
||||||
|
"Version 2021.39.0/Build 369068",
|
||||||
|
"Version 2021.39.1/Build 372418",
|
||||||
|
"Version 2021.41.0/Build 376052",
|
||||||
|
"Version 2021.42.0/Build 378193",
|
||||||
|
"Version 2021.43.0/Build 382019",
|
||||||
|
"Version 2021.44.0/Build 385129",
|
||||||
|
];
|
||||||
|
pub static IOS_OS_VERSION_LIST: &[&str; 8] = &[
|
||||||
|
"Version 17.0.1 (Build 21A340)",
|
||||||
|
"Version 17.0.2 (Build 21A350)",
|
||||||
|
"Version 17.0.3 (Build 21A360)",
|
||||||
|
"Version 17.1 (Build 21B74)",
|
||||||
|
"Version 17.1.1 (Build 21B91)",
|
||||||
|
"Version 17.1.2 (Build 21B101)",
|
||||||
|
"Version 17.2 (Build 21C62)",
|
||||||
|
"Version 17.2.1 (Build 21C66)",
|
||||||
|
];
|
@ -47,11 +47,11 @@ impl CompressionType {
|
|||||||
/// Returns a `CompressionType` given a content coding
|
/// Returns a `CompressionType` given a content coding
|
||||||
/// in [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.4)
|
/// in [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.4)
|
||||||
/// format.
|
/// format.
|
||||||
fn parse(s: &str) -> Option<CompressionType> {
|
fn parse(s: &str) -> Option<Self> {
|
||||||
let c = match s {
|
let c = match s {
|
||||||
// Compressors we support.
|
// Compressors we support.
|
||||||
"gzip" => CompressionType::Gzip,
|
"gzip" => Self::Gzip,
|
||||||
"br" => CompressionType::Brotli,
|
"br" => Self::Brotli,
|
||||||
|
|
||||||
// The wildcard means that we can choose whatever
|
// The wildcard means that we can choose whatever
|
||||||
// compression we prefer. In this case, use the
|
// compression we prefer. In this case, use the
|
||||||
@ -69,8 +69,8 @@ impl CompressionType {
|
|||||||
impl ToString for CompressionType {
|
impl ToString for CompressionType {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
CompressionType::Gzip => "gzip".to_string(),
|
Self::Gzip => "gzip".to_string(),
|
||||||
CompressionType::Brotli => "br".to_string(),
|
Self::Brotli => "br".to_string(),
|
||||||
_ => String::new(),
|
_ => String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,7 +195,7 @@ impl Route<'_> {
|
|||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Server {
|
Self {
|
||||||
default_headers: HeaderMap::new(),
|
default_headers: HeaderMap::new(),
|
||||||
router: Router::new(),
|
router: Router::new(),
|
||||||
}
|
}
|
||||||
|
@ -443,7 +443,7 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result<Subreddit, String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn test_fetching_subreddit() {
|
async fn test_fetching_subreddit() {
|
||||||
let subreddit = subreddit("rust", false).await;
|
let subreddit = subreddit("rust", false).await;
|
||||||
assert!(subreddit.is_ok());
|
assert!(subreddit.is_ok());
|
||||||
|
@ -130,7 +130,7 @@ async fn user(name: &str) -> Result<User, String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn test_fetching_user() {
|
async fn test_fetching_user() {
|
||||||
let user = user("spez").await;
|
let user = user("spez").await;
|
||||||
assert!(user.is_ok());
|
assert!(user.is_ok());
|
||||||
|
Loading…
Reference in New Issue
Block a user