correct location, response xml and behavior on missing filename

This commit is contained in:
trinity-1686a 2022-02-12 00:09:19 +01:00
parent 63948190e4
commit 93637d40ec
4 changed files with 58 additions and 12 deletions

1
Cargo.lock generated
View file

@ -699,6 +699,7 @@ dependencies = [
"chrono", "chrono",
"crypto-mac 0.10.1", "crypto-mac 0.10.1",
"err-derive 0.3.0", "err-derive 0.3.0",
"form_urlencoded",
"futures", "futures",
"futures-util", "futures-util",
"garage_model 0.6.0", "garage_model 0.6.0",

View file

@ -36,6 +36,7 @@ futures-util = "0.3"
pin-project = "1.0" pin-project = "1.0"
tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] } tokio = { version = "1.0", default-features = false, features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros", "sync", "signal", "fs"] }
form_urlencoded = "1.0.0"
http = "0.2" http = "0.2"
httpdate = "0.3" httpdate = "0.3"
http-range = "0.1" http-range = "0.1"

View file

@ -17,6 +17,7 @@ use garage_model::garage::Garage;
use crate::api_server::resolve_bucket; use crate::api_server::resolve_bucket;
use crate::error::*; use crate::error::*;
use crate::s3_put::{get_headers, save_stream}; use crate::s3_put::{get_headers, save_stream};
use crate::s3_xml;
use crate::signature::payload::{parse_date, verify_v4}; use crate::signature::payload::{parse_date, verify_v4};
pub async fn handle_post_object( pub async fn handle_post_object(
@ -39,7 +40,7 @@ pub async fn handle_post_object(
.for_field("file", 5 * 1024 * 1024 * 1024), .for_field("file", 5 * 1024 * 1024 * 1024),
); );
let (_head, body) = req.into_parts(); let (head, body) = req.into_parts();
let mut multipart = Multipart::with_constraints(body, boundary, constraints); let mut multipart = Multipart::with_constraints(body, boundary, constraints);
let mut params = HeaderMap::new(); let mut params = HeaderMap::new();
@ -107,9 +108,11 @@ pub async fn handle_post_object(
.to_str()?; .to_str()?;
let key = if key.contains("${filename}") { let key = if key.contains("${filename}") {
let filename = field.file_name(); if let Some(filename) = field.file_name() {
// TODO is this correct? Maybe we should error instead of default? key.replace("${filename}", filename)
key.replace("${filename}", filename.unwrap_or_default()) } else {
key.to_owned()
}
} else { } else {
key.to_owned() key.to_owned()
}; };
@ -229,10 +232,6 @@ pub async fn handle_post_object(
.await?; .await?;
let etag = format!("\"{}\"", md5); let etag = format!("\"{}\"", md5);
// TODO get uri
// get Host
// append www-form-urlencoded key
let location = "todo";
let resp = if let Some(mut target) = params let resp = if let Some(mut target) = params
.get("success_action_redirect") .get("success_action_redirect")
@ -252,18 +251,49 @@ pub async fn handle_post_object(
.header(header::ETAG, etag) .header(header::ETAG, etag)
.body(target.into())? .body(target.into())?
} else { } else {
let path = head
.uri
.into_parts()
.path_and_query
.map(|paq| paq.path().to_string())
.unwrap_or_else(|| "/".to_string());
let authority = head
.headers
.get(header::HOST)
.and_then(|h| h.to_str().ok())
.unwrap_or_default();
let proto = if !authority.is_empty() {
"https://"
} else {
""
};
let url_key: String = form_urlencoded::byte_serialize(key.as_bytes())
.flat_map(str::chars)
.collect();
let location = format!("{}{}{}{}", proto, authority, path, url_key);
let action = params let action = params
.get("success_action_status") .get("success_action_status")
.and_then(|h| h.to_str().ok()) .and_then(|h| h.to_str().ok())
.unwrap_or("204"); .unwrap_or("204");
let builder = Response::builder() let builder = Response::builder()
.header(header::LOCATION, location) .header(header::LOCATION, location.clone())
.header(header::ETAG, etag); .header(header::ETAG, etag.clone());
match action { match action {
"200" => builder.status(StatusCode::OK).body(Body::empty())?, "200" => builder.status(StatusCode::OK).body(Body::empty())?,
"201" => { "201" => {
// TODO body should be an XML document, not sure which yet let xml = s3_xml::PostObject {
builder.status(StatusCode::CREATED).body(Body::from(""))? xmlns: (),
location: s3_xml::Value(location),
bucket: s3_xml::Value(bucket),
key: s3_xml::Value(key),
etag: s3_xml::Value(etag),
};
let body = s3_xml::to_xml_with_header(&xml)?;
builder
.status(StatusCode::CREATED)
.body(Body::from(body.into_bytes()))?
} }
_ => builder.status(StatusCode::NO_CONTENT).body(Body::empty())?, _ => builder.status(StatusCode::NO_CONTENT).body(Body::empty())?,
} }

View file

@ -289,6 +289,20 @@ pub struct VersioningConfiguration {
pub status: Option<Value>, pub status: Option<Value>,
} }
#[derive(Debug, Serialize, PartialEq)]
pub struct PostObject {
#[serde(serialize_with = "xmlns_tag")]
pub xmlns: (),
#[serde(rename = "Location")]
pub location: Value,
#[serde(rename = "Bucket")]
pub bucket: Value,
#[serde(rename = "Key")]
pub key: Value,
#[serde(rename = "ETag")]
pub etag: Value,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;