Fix bundle lookup: normalize fingerprints, handle 404 gracefully
Server: normalize fingerprints by stripping colons and lowercasing before storing/looking up in sled. Adds tracing for register/lookup. Client: check HTTP status before parsing JSON response body. Shows clear error when user is not registered instead of parse error. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ impl ServerClient {
|
||||
|
||||
/// Fetch a user's pre-key bundle from the server.
|
||||
pub async fn fetch_bundle(&self, fingerprint: &str) -> Result<PreKeyBundle> {
|
||||
let resp: BundleResponse = self
|
||||
let response = self
|
||||
.client
|
||||
.get(format!(
|
||||
"{}/v1/keys/{}",
|
||||
@@ -67,7 +67,17 @@ impl ServerClient {
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.context("failed to fetch bundle")?
|
||||
.context("failed to fetch bundle")?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
anyhow::bail!(
|
||||
"server returned {} — user {} may not be registered",
|
||||
response.status(),
|
||||
fingerprint
|
||||
);
|
||||
}
|
||||
|
||||
let resp: BundleResponse = response
|
||||
.json()
|
||||
.await
|
||||
.context("failed to parse bundle response")?;
|
||||
|
||||
@@ -13,10 +13,18 @@ pub fn routes() -> Router<AppState> {
|
||||
.route("/keys/{fingerprint}", get(get_bundle))
|
||||
}
|
||||
|
||||
/// Normalize fingerprint: strip colons, lowercase.
|
||||
fn normalize_fp(fp: &str) -> String {
|
||||
fp.chars()
|
||||
.filter(|c| c.is_ascii_hexdigit())
|
||||
.collect::<String>()
|
||||
.to_lowercase()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RegisterRequest {
|
||||
fingerprint: String,
|
||||
bundle: Vec<u8>, // bincode-serialized PreKeyBundle
|
||||
bundle: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -28,7 +36,9 @@ async fn register_keys(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<RegisterRequest>,
|
||||
) -> Json<RegisterResponse> {
|
||||
let _ = state.db.keys.insert(req.fingerprint.as_bytes(), req.bundle);
|
||||
let key = normalize_fp(&req.fingerprint);
|
||||
tracing::info!("Registering bundle for {}", key);
|
||||
let _ = state.db.keys.insert(key.as_bytes(), req.bundle);
|
||||
Json(RegisterResponse { ok: true })
|
||||
}
|
||||
|
||||
@@ -36,11 +46,16 @@ async fn get_bundle(
|
||||
State(state): State<AppState>,
|
||||
Path(fingerprint): Path<String>,
|
||||
) -> Result<Json<serde_json::Value>, axum::http::StatusCode> {
|
||||
match state.db.keys.get(fingerprint.as_bytes()) {
|
||||
let key = normalize_fp(&fingerprint);
|
||||
tracing::info!("Looking up bundle for {}", key);
|
||||
match state.db.keys.get(key.as_bytes()) {
|
||||
Ok(Some(data)) => Ok(Json(serde_json::json!({
|
||||
"fingerprint": fingerprint,
|
||||
"bundle": base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &data),
|
||||
}))),
|
||||
_ => Err(axum::http::StatusCode::NOT_FOUND),
|
||||
_ => {
|
||||
tracing::warn!("Bundle not found for {}", key);
|
||||
Err(axum::http::StatusCode::NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user