mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-25 04:44:12 +02:00
Apply send cookies as a delta; judge NAT64/6to4 addresses by the IPv4 they carry
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
//! Carrying a send's cookie changes back into a jar.
|
||||
//!
|
||||
//! A send starts from a snapshot of the jar and hands back the jar as the
|
||||
//! transaction left it. Writing that whole result over the jar would also
|
||||
//! write over anything the user changed *while* the send was in flight — a
|
||||
//! cookie edited or deleted in the jar view, or set by another send. So the
|
||||
//! send's contribution is taken as a difference (what it added, changed, or
|
||||
//! removed relative to its snapshot) and applied to whatever the jar holds now.
|
||||
|
||||
use crate::models::{Cookie, CookieDomain};
|
||||
|
||||
/// The identity of a cookie in a jar: two cookies with the same name, domain
|
||||
/// and path are the same cookie, whatever their value or attributes.
|
||||
type CookieKey = (String, CookieDomain, String);
|
||||
|
||||
fn key(c: &Cookie) -> CookieKey {
|
||||
(c.name.clone(), c.domain.clone(), c.path.clone())
|
||||
}
|
||||
|
||||
/// Apply the changes between `before` (the snapshot a send started from) and
|
||||
/// `after` (the jar as the send left it) to `current` (the jar as it is now).
|
||||
///
|
||||
/// Cookies the send removed are removed; cookies it added or changed replace
|
||||
/// their counterpart in `current`, or are appended. Cookies the send did not
|
||||
/// touch are left exactly as `current` has them.
|
||||
pub fn apply_cookie_changes(
|
||||
current: Vec<Cookie>,
|
||||
before: &[Cookie],
|
||||
after: &[Cookie],
|
||||
) -> Vec<Cookie> {
|
||||
let removed: Vec<CookieKey> =
|
||||
before.iter().filter(|b| !after.iter().any(|a| key(a) == key(b))).map(key).collect();
|
||||
let changed: Vec<&Cookie> = after.iter().filter(|a| !before.iter().any(|b| b == *a)).collect();
|
||||
|
||||
let mut result: Vec<Cookie> =
|
||||
current.into_iter().filter(|c| !removed.contains(&key(c))).collect();
|
||||
for cookie in changed {
|
||||
match result.iter_mut().find(|c| key(c) == key(cookie)) {
|
||||
Some(existing) => *existing = cookie.clone(),
|
||||
None => result.push(cookie.clone()),
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::models::CookieExpires;
|
||||
|
||||
fn cookie(name: &str, value: &str) -> Cookie {
|
||||
Cookie {
|
||||
name: name.to_string(),
|
||||
value: value.to_string(),
|
||||
domain: CookieDomain::HostOnly("example.com".to_string()),
|
||||
expires: CookieExpires::SessionEnd,
|
||||
path: "/".to_string(),
|
||||
secure: false,
|
||||
http_only: false,
|
||||
same_site: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_send_that_changed_nothing_leaves_the_jar_alone() {
|
||||
let before = vec![cookie("a", "1")];
|
||||
let current = vec![cookie("a", "edited"), cookie("b", "2")];
|
||||
assert_eq!(apply_cookie_changes(current.clone(), &before, &before), current);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn additions_and_changes_land_without_touching_concurrent_edits() {
|
||||
let before = vec![cookie("a", "1"), cookie("b", "2")];
|
||||
let after = vec![cookie("a", "1"), cookie("b", "3"), cookie("c", "4")];
|
||||
// Meanwhile the user edited `a` and added `d`.
|
||||
let current = vec![cookie("a", "edited"), cookie("b", "2"), cookie("d", "5")];
|
||||
assert_eq!(
|
||||
apply_cookie_changes(current, &before, &after),
|
||||
vec![
|
||||
cookie("a", "edited"),
|
||||
cookie("b", "3"),
|
||||
cookie("d", "5"),
|
||||
cookie("c", "4")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_cookie_the_send_removed_is_removed() {
|
||||
let before = vec![cookie("a", "1"), cookie("b", "2")];
|
||||
let after = vec![cookie("b", "2")];
|
||||
let current = vec![cookie("a", "1"), cookie("b", "2"), cookie("c", "3")];
|
||||
assert_eq!(
|
||||
apply_cookie_changes(current, &before, &after),
|
||||
vec![cookie("b", "2"), cookie("c", "3")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_cookie_the_user_deleted_mid_send_stays_deleted_unless_the_send_set_it() {
|
||||
let before = vec![cookie("a", "1")];
|
||||
let after = vec![cookie("a", "1")]; // untouched by the send
|
||||
assert_eq!(apply_cookie_changes(vec![], &before, &after), vec![]);
|
||||
let after = vec![cookie("a", "fresh")]; // the send set it again
|
||||
assert_eq!(apply_cookie_changes(vec![], &before, &after), vec![cookie("a", "fresh")]);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use yaak_database::SqlitePool;
|
||||
|
||||
pub mod blob_manager;
|
||||
pub mod client_db;
|
||||
pub mod cookies;
|
||||
mod connection_or_tx;
|
||||
pub mod error;
|
||||
pub mod migrate;
|
||||
|
||||
@@ -694,22 +694,22 @@ export function __wbg_versions_215a3ab1c9d5745a(arg0) {
|
||||
return ret;
|
||||
}
|
||||
export function __wbindgen_cast_0000000000000001(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1116, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 1117, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__ha1c2fa93df0107f3);
|
||||
return ret;
|
||||
}
|
||||
export function __wbindgen_cast_0000000000000002(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 211, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("Event")], shim_idx: 212, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__ha7903b6e296dd8f4);
|
||||
return ret;
|
||||
}
|
||||
export function __wbindgen_cast_0000000000000003(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx: 164, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [NamedExternref("IDBVersionChangeEvent")], shim_idx: 83, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__hdf19cb46f9aecb24);
|
||||
return ret;
|
||||
}
|
||||
export function __wbindgen_cast_0000000000000004(arg0, arg1) {
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 209, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
// Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [], shim_idx: 210, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
||||
const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__ha1b480b83daa641f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -29,8 +29,10 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use yaak_models::blob_manager::{BlobManager, BodyChunk};
|
||||
use yaak_models::cookies::apply_cookie_changes;
|
||||
use yaak_models::models::{
|
||||
AnyModel, CookieJar, HttpRequest, HttpResponseEvent, HttpResponseEventData, HttpSendSettings,
|
||||
AnyModel, Cookie, CookieJar, HttpRequest, HttpResponseEvent, HttpResponseEventData,
|
||||
HttpSendSettings,
|
||||
};
|
||||
use yaak_models::models_ops;
|
||||
use yaak_models::query_manager::QueryManager;
|
||||
@@ -214,6 +216,14 @@ struct ResponseIdReq {
|
||||
response_id: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PersistSendCookiesReq {
|
||||
cookie_jar_id: String,
|
||||
before: Vec<Cookie>,
|
||||
after: Vec<Cookie>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct InsertResponseEventsReq {
|
||||
@@ -341,6 +351,20 @@ fn dispatch(
|
||||
)
|
||||
}
|
||||
|
||||
// The cookies a send set or cleared, applied to the jar as it is *now* rather than
|
||||
// written over it, so an edit made while the send was in flight survives.
|
||||
"web_persist_send_cookies" => {
|
||||
let req: PersistSendCookiesReq = from_js(payload)?;
|
||||
if req.before == req.after {
|
||||
return to_json(());
|
||||
}
|
||||
let db = host.queries.connect();
|
||||
let jar = db.get_cookie_jar(&req.cookie_jar_id).map_err(js_error)?;
|
||||
let cookies = apply_cookie_changes(jar.cookies.clone(), &req.before, &req.after);
|
||||
db.upsert_cookie_jar(&CookieJar { cookies, ..jar }, source).map_err(js_error)?;
|
||||
to_json(())
|
||||
}
|
||||
|
||||
// The tab's half of the send timeline: the events the proxy streamed back, recorded
|
||||
// under the response they belong to. Same rows the desktop's send task writes, and the
|
||||
// writes fan out to every tab as `model_writes` like any other.
|
||||
|
||||
Reference in New Issue
Block a user