Merge pull request #3 from Trivernis/feature/managing-cookies-and-headers

Feature/managing cookies and headers
pull/4/head
Julius Riegel 3 years ago committed by GitHub
commit 0e409754e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,6 @@
[package]
name = "hydrus-api"
version = "0.3.1"
version = "0.3.2"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
license = "Apache-2.0"

@ -11,7 +11,11 @@ use crate::api_core::adding_urls::{
AddUrl, AddUrlRequest, AddUrlResponse, AssociateUrl, AssociateUrlRequest, GetUrlFiles,
GetUrlFilesResponse, GetUrlInfo, GetUrlInfoResponse,
};
use crate::api_core::common::{FileIdentifier, FileMetadataInfo, FileRecord};
use crate::api_core::common::{FileIdentifier, FileMetadataInfo, FileRecord, OptionalStringNumber};
use crate::api_core::managing_cookies_and_http_headers::{
GetCookies, GetCookiesResponse, SetCookies, SetCookiesRequest, SetUserAgent,
SetUserAgentRequest,
};
use crate::api_core::managing_pages::{
FocusPage, FocusPageRequest, GetPageInfo, GetPageInfoResponse, GetPages, GetPagesResponse,
};
@ -331,4 +335,30 @@ impl Client {
Ok(())
}
/// Returns all cookies for the given domain
pub async fn get_cookies<S: AsRef<str>>(&self, domain: S) -> Result<GetCookiesResponse> {
self.get_and_parse::<GetCookies, [(&str, &str)]>(&[("domain", domain.as_ref())])
.await
}
/// Sets some cookies for some websites.
/// Each entry needs to be in the format `[<name>, <value>, <domain>, <path>, <expires>]`
/// with the types `[String, String, String, String, u64]`
pub async fn set_cookies(&self, cookies: Vec<[OptionalStringNumber; 5]>) -> Result<()> {
self.post::<SetCookies>(SetCookiesRequest { cookies })
.await?;
Ok(())
}
/// Sets the user agent that is being used for every request hydrus starts
pub async fn set_user_agent<S: ToString>(&self, user_agent: S) -> Result<()> {
self.post::<SetUserAgent>(SetUserAgentRequest {
user_agent: user_agent.to_string(),
})
.await?;
Ok(())
}
}

@ -60,3 +60,41 @@ pub struct PageInformation {
#[serde(default = "Vec::new")]
pub pages: Vec<PageInformation>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OptionalStringNumber {
String(String),
Number(u64),
None,
}
impl From<u64> for OptionalStringNumber {
fn from(value: u64) -> Self {
Self::Number(value)
}
}
impl From<String> for OptionalStringNumber {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl OptionalStringNumber {
pub fn string(&self) -> Option<&str> {
if let Self::String(s) = &self {
Some(s)
} else {
None
}
}
pub fn number(&self) -> Option<u64> {
if let Self::Number(n) = &self {
Some(*n)
} else {
None
}
}
}

@ -0,0 +1,102 @@
use crate::api_core::common::OptionalStringNumber;
use crate::api_core::Endpoint;
#[derive(Clone, Debug, Deserialize)]
pub struct GetCookiesResponse {
pub cookies: Vec<[OptionalStringNumber; 5]>,
}
pub struct GetCookies;
impl Endpoint for GetCookies {
type Request = ();
type Response = GetCookiesResponse;
fn path() -> String {
String::from("manage_cookies/get_cookies")
}
}
#[derive(Clone, Debug, Serialize)]
pub struct SetCookiesRequest {
pub cookies: Vec<[OptionalStringNumber; 5]>,
}
pub struct SetCookies;
impl Endpoint for SetCookies {
type Request = SetCookiesRequest;
type Response = ();
fn path() -> String {
String::from("manage_cookies/set_cookies")
}
}
pub struct CookieBuilder {
name: OptionalStringNumber,
value: OptionalStringNumber,
domain: OptionalStringNumber,
path: OptionalStringNumber,
expires: OptionalStringNumber,
}
impl Default for CookieBuilder {
fn default() -> Self {
Self {
name: String::new().into(),
value: String::new().into(),
domain: String::new().into(),
path: String::new().into(),
expires: OptionalStringNumber::None,
}
}
}
impl CookieBuilder {
pub fn name<S: ToString>(mut self, name: S) -> Self {
self.name = name.to_string().into();
self
}
pub fn value<S: ToString>(mut self, value: S) -> Self {
self.value = value.to_string().into();
self
}
pub fn domain<S: ToString>(mut self, domain: S) -> Self {
self.domain = domain.to_string().into();
self
}
pub fn path<S: ToString>(mut self, path: S) -> Self {
self.path = path.to_string().into();
self
}
pub fn expires(mut self, expires: u64) -> Self {
self.expires = expires.into();
self
}
pub fn build(self) -> [OptionalStringNumber; 5] {
[self.name, self.value, self.domain, self.path, self.expires]
}
}
#[derive(Clone, Debug, Serialize)]
pub struct SetUserAgentRequest {
#[serde(rename = "user-agent")]
pub user_agent: String,
}
pub struct SetUserAgent;
impl Endpoint for SetUserAgent {
type Request = SetUserAgentRequest;
type Response = ();
fn path() -> String {
String::from("manage_headers/set_user_agent")
}
}

@ -7,6 +7,7 @@ pub mod adding_tags;
pub mod adding_urls;
pub mod client;
pub mod common;
pub mod managing_cookies_and_http_headers;
pub mod managing_pages;
pub mod searching_and_fetching_files;

@ -0,0 +1,99 @@
use crate::api_core::common::OptionalStringNumber;
use crate::api_core::managing_cookies_and_http_headers::CookieBuilder;
use crate::error::Result;
use crate::Client;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub struct Address {
client: Client,
domain: String,
path: String,
}
impl Address {
pub(crate) fn from_str(client: Client, domain: &str) -> Self {
let (domain, path) = domain.split_once("/").unwrap_or((domain, "/"));
Self {
client,
domain: domain.to_string(),
path: path.to_string(),
}
}
/// Returns the path after the domain name
pub fn path(&self) -> &str {
&self.path
}
/// Sets the path of the domain that can be used for setting cookies
pub fn set_path<S: ToString>(&mut self, path: S) {
self.path = path.to_string();
}
/// Sets cookies for the domain
pub async fn set_cookies(&self, cookies: Vec<DomainCookie>) -> Result<()> {
let cookies = cookies
.into_iter()
.map(|cookie| {
let mut builder = CookieBuilder::default()
.domain(&self.domain)
.path(&self.path)
.name(cookie.name)
.value(cookie.value);
if let Some(expires) = cookie.expires {
builder =
builder.expires(expires.duration_since(UNIX_EPOCH).unwrap().as_secs());
}
builder.build()
})
.collect();
self.client.set_cookies(cookies).await
}
/// Returns all cookies stored for this domain
pub async fn get_cookies(&self) -> Result<Vec<DomainCookie>> {
let response = self.client.get_cookies(&self.domain).await?;
let cookies = response
.cookies
.into_iter()
.map(DomainCookie::from)
.collect();
Ok(cookies)
}
}
#[derive(Clone, Debug)]
pub struct DomainCookie {
pub name: String,
pub value: String,
pub expires: Option<SystemTime>,
}
impl DomainCookie {
/// Creates a new cookie that will be expire after the given instant or only last for the session
pub fn new<S1: ToString, S2: ToString>(
name: S1,
value: S2,
expires: Option<SystemTime>,
) -> Self {
Self {
name: name.to_string(),
value: value.to_string(),
expires,
}
}
}
impl From<[OptionalStringNumber; 5]> for DomainCookie {
fn from(cookie_entry: [OptionalStringNumber; 5]) -> Self {
let name = cookie_entry[0].string().unwrap_or("");
let value = cookie_entry[1].string().unwrap_or("");
let expires = cookie_entry[4]
.number()
.map(|n| UNIX_EPOCH + Duration::from_secs(n));
Self::new(name, value, expires)
}
}

@ -2,6 +2,7 @@ use crate::api_core::common::FileIdentifier;
use crate::api_core::searching_and_fetching_files::FileSearchLocation;
use crate::error::Result;
use crate::utils::tag_list_to_string_list;
use crate::wrapper::address::Address;
use crate::wrapper::builders::import_builder::ImportBuilder;
use crate::wrapper::builders::tagging_builder::TaggingBuilder;
use crate::wrapper::hydrus_file::HydrusFile;
@ -47,6 +48,11 @@ impl Hydrus {
}
}
/// Returns the address as an object that can be used to get and set cookies
pub fn address<S: AsRef<str>>(&self, address: S) -> Address {
Address::from_str(self.client.clone(), address.as_ref())
}
/// Returns information about a given url in an object that allows
/// further operations with that url
pub async fn url<S: AsRef<str>>(&self, url: S) -> Result<Url> {
@ -115,4 +121,9 @@ impl Hydrus {
pages_response.pages,
))
}
/// Sets the user agent hydrus uses for http requests
pub async fn set_user_agent<S: ToString>(&self, user_agent: S) -> Result<()> {
self.client.set_user_agent(user_agent).await
}
}

@ -1,3 +1,4 @@
pub mod address;
pub mod builders;
pub mod hydrus;
pub mod hydrus_file;

@ -2,6 +2,7 @@ use crate::api_core::adding_urls::{
URL_TYPE_FILE, URL_TYPE_GALLERY, URL_TYPE_POST, URL_TYPE_WATCHABLE,
};
use crate::error::Result;
use crate::wrapper::address::Address;
use crate::wrapper::builders::import_builder::UrlImportBuilder;
use crate::wrapper::hydrus_file::HydrusFile;
use crate::Client;
@ -59,6 +60,16 @@ impl Url {
UrlImportBuilder::new(self.client.clone(), &self.url)
}
/// Returns the address to manipulate cookies for this url
pub fn address(&self) -> Address {
let url = self
.normalised_url
.trim_start_matches("http://")
.trim_start_matches("https://");
Address::from_str(self.client.clone(), url)
}
/// Associates the url with a list of file hashes
pub async fn associate(&mut self, hashes: Vec<String>) -> Result<()> {
self.client

@ -2,5 +2,6 @@ mod test_access_management;
mod test_adding_files;
mod test_adding_tags;
mod test_adding_urls;
mod test_managing_cookies_and_http_headers;
mod test_managing_pages;
mod test_searching_and_fetching_files;

@ -0,0 +1,29 @@
use super::super::common;
use hydrus_api::api_core::managing_cookies_and_http_headers::CookieBuilder;
#[tokio::test]
async fn it_returns_cookies_for_a_domain() {
let client = common::get_client();
client.get_cookies("trivernis.net").await.unwrap();
}
#[tokio::test]
async fn it_sets_cookies_for_a_domain() {
let client = common::get_client();
let cookie = CookieBuilder::default()
.name("my_cookie")
.value("my_value")
.domain("trivernis.net")
.path("/")
.build();
client.set_cookies(vec![cookie]).await.unwrap();
}
#[tokio::test]
async fn it_sets_the_user_agent() {
let client = common::get_client();
client
.set_user_agent("Mozilla/5.0 (compatible; Hydrus Client)")
.await
.unwrap();
}

@ -3,3 +3,4 @@ mod test_hydrus;
mod test_import;
mod test_url;
mod test_page;
mod test_address;

@ -0,0 +1,31 @@
use super::super::common;
use hydrus_api::wrapper::address::{Address, DomainCookie};
use std::time::{Duration, SystemTime};
fn get_address() -> Address {
let hydrus = common::get_hydrus();
hydrus.address("trivernis.net/some/path")
}
#[tokio::test]
async fn it_sets_cookies() {
let address = get_address();
address
.set_cookies(vec![
DomainCookie::new("name", "value", None),
DomainCookie::new(
"name2",
"value2",
Some(SystemTime::now() + Duration::from_secs(30)),
),
])
.await
.unwrap();
}
#[tokio::test]
async fn it_retrieves_cookies() {
let address = get_address();
address.get_cookies().await.unwrap();
}

@ -60,3 +60,12 @@ async fn it_adds_tags() {
.await
.unwrap();
}
#[tokio::test]
async fn it_sets_the_user_agent() {
let hydrus = common::get_hydrus();
hydrus
.set_user_agent("Mozilla/5.0 (compatible; Hydrus Client)")
.await
.unwrap();
}

Loading…
Cancel
Save