Skip to content

Commit

Permalink
[blob] Support uploading base64-encoded blobs
Browse files Browse the repository at this point in the history
Summary:
Address [[ https://linear.app/comm/issue/ENG-9413/update-upload-blob-endpoint-to-accept-base64-strings-as-well | ENG-9413 ]].

As an alternative to `blob_data` multipart form field, added a `base64_data` field which accepts base64-strings.

I could take advantage of multipart "field types" (MIME types for fields) and check type of existing `blob_data` but there's a risk that React Native fetch would not support it correctly, so went with a simple solution.

Test Plan: Tested on staging with D13495

Reviewers: tomek, kamil, varun, will

Reviewed By: will

Subscribers: ashoat

Differential Revision: https://phab.comm.dev/D13503
  • Loading branch information
barthap committed Sep 28, 2024
1 parent 43df5d0 commit b11c172
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions services/blob/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async-stream = { workspace = true }
aws-config = { workspace = true }
aws-sdk-dynamodb = { workspace = true }
aws-sdk-s3 = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
comm-lib = { path = "../../shared/comm-lib", features = [
Expand Down
30 changes: 27 additions & 3 deletions services/blob/src/http/handlers/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ use crate::service::BlobService;
use crate::validate_identifier;

use actix_web::error::{ErrorBadRequest, ErrorRangeNotSatisfiable};
use actix_web::web::Bytes;
use actix_web::{
http::header::{ByteRangeSpec, Range},
web, HttpResponse,
};
use async_stream::try_stream;
use base64::Engine;
use comm_lib::http::multipart;
use serde::{Deserialize, Serialize};
use tokio_stream::StreamExt;
use tracing::{info, instrument, trace, warn};
use tracing::{debug, info, instrument, trace, warn};
use tracing_futures::Instrument;

/// Returns a tuple of first and last byte number (inclusive) represented by given range header.
Expand Down Expand Up @@ -162,21 +164,43 @@ pub async fn upload_blob_handler(
return Err(ErrorBadRequest("Bad request"));
}
validate_identifier!(blob_hash);

tracing::Span::current().record("blob_hash", &blob_hash);

trace!("Receiving blob data");
let stream = try_stream! {
while let Some(mut field) = payload.try_next().await? {
let field_name = field.name();

if field_name == "base64_data" {
trace!("Got base64_data");

let mut buf = Vec::new();
while let Some(chunk) = field.try_next().await? {
buf.extend_from_slice(&chunk);
}

let base64_string = String::from_utf8(buf)
.map_err(|err| actix_web::error::ParseError::Utf8(err.utf8_error()))?;

let data = base64::engine::general_purpose::STANDARD
.decode(&base64_string)
.map_err(|err| {
debug!("Invalid base64 payload: {err:?}");
ErrorBadRequest("Invalid base64")
})?;
yield Bytes::from(data);
return;
}

if field_name != "blob_data" {
warn!(
field_name,
"Malfolmed request: 'blob_data' multipart field expected."
"Malformed request: 'blob_data' or 'base64_data' multipart field expected."
);
Err(ErrorBadRequest("Bad request"))?;
}

trace!("Got blob_data. Streaming...");
while let Some(chunk) = field.try_next().await? {
yield chunk;
}
Expand Down

0 comments on commit b11c172

Please sign in to comment.