correct location, response xml and behavior on missing filename
This commit is contained in:
parent
63948190e4
commit
93637d40ec
4 changed files with 58 additions and 12 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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())?,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
Loading…
Reference in a new issue