separate host/port columns, increase host len to 1024 bytes

This commit is contained in:
yggverse 2025-07-23 08:15:48 +03:00
parent a7230fd329
commit 77ee4aa78c
3 changed files with 51 additions and 39 deletions

View File

@ -4,7 +4,10 @@ mod database;
use anyhow::Result; use anyhow::Result;
use certificate::Certificate; use certificate::Certificate;
use database::Database; use database::Database;
use gtk::{gio::TlsCertificate, glib::Uri}; use gtk::{
gio::TlsCertificate,
glib::{GString, Uri},
};
use r2d2::Pool; use r2d2::Pool;
use r2d2_sqlite::SqliteConnectionManager; use r2d2_sqlite::SqliteConnectionManager;
use sqlite::Transaction; use sqlite::Transaction;
@ -15,7 +18,7 @@ use std::{cell::RefCell, collections::HashMap};
/// https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation /// https://geminiprotocol.net/docs/protocol-specification.gmi#tls-server-certificate-validation
pub struct Tofu { pub struct Tofu {
database: Database, database: Database,
memory: RefCell<HashMap<String, Certificate>>, memory: RefCell<HashMap<(GString, i32), Certificate>>,
} }
impl Tofu { impl Tofu {
@ -31,8 +34,11 @@ impl Tofu {
// build in-memory index... // build in-memory index...
let mut m = memory.borrow_mut(); let mut m = memory.borrow_mut();
for r in records { for r in records {
if m.insert(r.address, Certificate::from_db(Some(r.id), &r.pem, r.time)?) if m.insert(
.is_some() (r.host.into(), r.port),
Certificate::from_db(Some(r.id), &r.pem, r.time)?,
)
.is_some()
{ {
panic!() // expect unique address panic!() // expect unique address
} }
@ -50,31 +56,34 @@ impl Tofu {
default_port: i32, default_port: i32,
tls_certificate: TlsCertificate, tls_certificate: TlsCertificate,
) -> Result<bool> { ) -> Result<bool> {
match address(uri, default_port) { match uri.host() {
Some(k) => Ok(self Some(host) => Ok(self
.memory .memory
.borrow_mut() .borrow_mut()
.insert(k, Certificate::from_tls_certificate(tls_certificate)?) .insert(
(host, port(uri.port(), default_port)),
Certificate::from_tls_certificate(tls_certificate)?,
)
.is_none()), .is_none()),
None => Ok(false), None => Ok(false),
} }
} }
pub fn server_certificate(&self, uri: &Uri, default_port: i32) -> Option<TlsCertificate> { pub fn server_certificate(&self, uri: &Uri, default_port: i32) -> Option<TlsCertificate> {
address(uri, default_port).and_then(|k| { uri.host().and_then(|host| {
self.memory self.memory
.borrow() .borrow()
.get(&k) .get(&(host, port(uri.port(), default_port)))
.map(|c| c.tls_certificate().clone()) .map(|c| c.tls_certificate().clone())
}) })
} }
/// Save in-memory index to the permanent database (on app close) /// Save in-memory index to the permanent database (on app close)
pub fn save(&self) -> Result<()> { pub fn save(&self) -> Result<()> {
for (address, certificate) in self.memory.borrow_mut().drain() { for ((host, port), certificate) in self.memory.borrow_mut().drain() {
if certificate.id().is_none() { if certificate.id().is_none() {
self.database self.database
.add(address, certificate.time(), &certificate.pem())?; .add(host.into(), port, certificate.time(), &certificate.pem())?;
} }
} }
Ok(()) Ok(())
@ -94,17 +103,10 @@ pub fn migrate(tx: &Transaction) -> Result<()> {
Ok(()) Ok(())
} }
fn address(uri: &Uri, default_port: i32) -> Option<String> { fn port(port: i32, default_port: i32) -> i32 {
uri.host().map(|host| { if port.is_positive() {
let port = uri.port(); port
format!( } else {
"{}:{}", default_port
host, }
if port.is_positive() {
port
} else {
default_port
}
)
})
} }

View File

@ -34,10 +34,10 @@ impl Database {
/// Create new record in database /// Create new record in database
/// * return last insert ID on success /// * return last insert ID on success
pub fn add(&self, address: String, time: &DateTime, pem: &str) -> Result<i64> { pub fn add(&self, host: String, port: i32, time: &DateTime, pem: &str) -> Result<i64> {
let mut connection = self.pool.get()?; let mut connection = self.pool.get()?;
let tx = connection.transaction()?; let tx = connection.transaction()?;
let id = insert(&tx, self.profile_id, address, time, pem)?; let id = insert(&tx, self.profile_id, host, port, time, pem)?;
tx.commit()?; tx.commit()?;
Ok(id) Ok(id)
} }
@ -52,11 +52,12 @@ pub fn init(tx: &Transaction) -> Result<usize> {
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`profile_id` INTEGER NOT NULL, `profile_id` INTEGER NOT NULL,
`time` INTEGER NOT NULL, `time` INTEGER NOT NULL,
`address` VARCHAR(255) NOT NULL, `port` INTEGER NOT NULL,
`host` VARCHAR(1024) NOT NULL,
`pem` TEXT NOT NULL, `pem` TEXT NOT NULL,
FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`), FOREIGN KEY (`profile_id`) REFERENCES `profile` (`id`),
UNIQUE (`address`) UNIQUE (`host`, `port`)
)", )",
[], [],
)?) )?)
@ -65,7 +66,8 @@ pub fn init(tx: &Transaction) -> Result<usize> {
pub fn insert( pub fn insert(
tx: &Transaction, tx: &Transaction,
profile_id: i64, profile_id: i64,
address: String, host: String,
port: i32,
time: &DateTime, time: &DateTime,
pem: &str, pem: &str,
) -> Result<i64> { ) -> Result<i64> {
@ -73,28 +75,35 @@ pub fn insert(
"INSERT INTO `profile_tofu` ( "INSERT INTO `profile_tofu` (
`profile_id`, `profile_id`,
`time`, `time`,
`address`, `host`,
`port`,
`pem` `pem`
) VALUES (?, ?, ?, ?) ON CONFLICT (`address`) ) VALUES (?, ?, ?, ?, ?) ON CONFLICT (`host`, `port`)
DO UPDATE SET `time` = `excluded`.`time`, DO UPDATE SET `time` = `excluded`.`time`,
`pem` = `excluded`.`pem`", `pem` = `excluded`.`pem`",
(profile_id, time.to_unix(), address, pem), (profile_id, time.to_unix(), host, port, pem),
)?; )?;
Ok(tx.last_insert_rowid()) Ok(tx.last_insert_rowid())
} }
pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>> { pub fn select(tx: &Transaction, profile_id: i64) -> Result<Vec<Row>> {
let mut stmt = tx.prepare( let mut stmt = tx.prepare(
"SELECT `id`, `profile_id`, `address`, `time`, `pem` FROM `profile_tofu` WHERE `profile_id` = ?", "SELECT `id`,
`profile_id`,
`host`,
`port`,
`time`,
`pem` FROM `profile_tofu` WHERE `profile_id` = ?",
)?; )?;
let result = stmt.query_map([profile_id], |row| { let result = stmt.query_map([profile_id], |row| {
Ok(Row { Ok(Row {
id: row.get(0)?, id: row.get(0)?,
//profile_id: row.get(1)?, //profile_id: row.get(1)?,
address: row.get(2)?, host: row.get(2)?,
time: DateTime::from_unix_local(row.get(3)?).unwrap(), port: row.get(3)?,
pem: row.get(4)?, time: DateTime::from_unix_local(row.get(4)?).unwrap(),
pem: row.get(5)?,
}) })
})?; })?;

View File

@ -1,6 +1,7 @@
pub struct Row { pub struct Row {
pub address: String, pub host: String,
pub id: i64, pub id: i64,
pub pem: String, pub pem: String,
pub port: i32,
pub time: gtk::glib::DateTime, pub time: gtk::glib::DateTime,
} }