Page MenuHomePhabricator

D13479.diff
No OneTemporary

D13479.diff

diff --git a/services/blob/src/http/handlers/holders.rs b/services/blob/src/http/handlers/holders.rs
new file mode 100644
--- /dev/null
+++ b/services/blob/src/http/handlers/holders.rs
@@ -0,0 +1,82 @@
+use actix_web::error::ErrorBadRequest;
+use actix_web::{web, HttpResponse};
+use serde::{Deserialize, Serialize};
+use tracing::{info, instrument, trace, warn};
+
+use crate::service::BlobService;
+
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct BlobHashAndHolder {
+ blob_hash: String,
+ holder: String,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoveHoldersPayload {
+ requests: Vec<BlobHashAndHolder>,
+ #[serde(default)]
+ instant_delete: bool,
+}
+
+#[derive(Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoveHoldersResponse {
+ failed_requests: Vec<BlobHashAndHolder>,
+}
+
+#[instrument(name = "remove_multiple_holders", skip(service))]
+pub async fn remove_holders_handler(
+ service: web::Data<BlobService>,
+ payload: web::Json<RemoveHoldersPayload>,
+) -> actix_web::Result<HttpResponse> {
+ let RemoveHoldersPayload {
+ requests,
+ instant_delete,
+ } = payload.into_inner();
+ info!(
+ instant_delete,
+ "Remove request for {} holders.",
+ requests.len()
+ );
+ validate_request(&requests)?;
+
+ let mut failed_requests = Vec::new();
+ // This has to be done sequentially because `service.revoke_holder()`
+ // performs a DDB transaction and these transactions could conflict
+ // with each other, e.g. if two holders were removed for the same blob hash.
+ for item in requests {
+ trace!("Removing item: {:?}", &item);
+
+ let BlobHashAndHolder { holder, blob_hash } = &item;
+ if let Err(err) = service
+ .revoke_holder(blob_hash, holder, instant_delete)
+ .await
+ {
+ warn!("Holder removal failed: {:?}", err);
+ failed_requests.push(item);
+ }
+ }
+ let response = RemoveHoldersResponse { failed_requests };
+ Ok(HttpResponse::Ok().json(web::Json(response)))
+}
+
+/**
+ * Returns `HTTP 400 Bad Request` if one or more blob hashes or holders
+ * have invalid format. See [`comm_lib::tools::is_valid_identifier`] for
+ * valid format conditions
+ */
+fn validate_request(items: &[BlobHashAndHolder]) -> actix_web::Result<()> {
+ use comm_lib::tools::is_valid_identifier;
+ let all_valid =
+ items.iter().all(|BlobHashAndHolder { holder, blob_hash }| {
+ is_valid_identifier(holder) && is_valid_identifier(blob_hash)
+ });
+
+ if !all_valid {
+ return Err(ErrorBadRequest("One or more requests have invalid format"));
+ }
+
+ Ok(())
+}
diff --git a/services/blob/src/http/mod.rs b/services/blob/src/http/mod.rs
--- a/services/blob/src/http/mod.rs
+++ b/services/blob/src/http/mod.rs
@@ -12,6 +12,7 @@
mod handlers {
pub(super) mod blob;
+ pub(super) mod holders;
}
pub async fn run_http_server(
@@ -39,11 +40,16 @@
)
.service(
web::resource("/blob")
- .wrap(auth_middleware)
+ .wrap(auth_middleware.clone())
.route(web::put().to(handlers::blob::upload_blob_handler))
.route(web::post().to(handlers::blob::assign_holder_handler))
.route(web::delete().to(handlers::blob::remove_holder_handler)),
)
+ .service(
+ web::resource("/holders")
+ .wrap(auth_middleware)
+ .route(web::delete().to(handlers::holders::remove_holders_handler)),
+ )
})
.bind(("0.0.0.0", CONFIG.http_port))?
.run()

File Metadata

Mime Type
text/plain
Expires
Sun, Nov 17, 2:03 PM (20 h, 26 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2526553
Default Alt Text
D13479.diff (3 KB)

Event Timeline