Implement multipart uploads

This commit is contained in:
Alex Auvolat
2020-04-26 20:39:32 +00:00
parent 1999c0ae51
commit 81ecc4999e
9 changed files with 296 additions and 59 deletions
+53 -30
View File
@@ -3,7 +3,6 @@ use std::net::SocketAddr;
use std::sync::Arc;
use futures::future::Future;
use hyper::body::{Bytes, HttpBody};
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server};
@@ -15,11 +14,9 @@ use garage_core::garage::Garage;
use crate::http_util::*;
use crate::signature::check_signature;
use crate::s3_get::{handle_get, handle_head};
use crate::s3_list::handle_list;
use crate::s3_put::{handle_delete, handle_put};
pub type BodyType = Box<dyn HttpBody<Data = Bytes, Error = Error> + Send + Unpin>;
use crate::s3_get::*;
use crate::s3_list::*;
use crate::s3_put::*;
pub async fn run_api_server(
garage: Arc<Garage>,
@@ -100,33 +97,62 @@ async fn handler_inner(
)));
}
let mut params = HashMap::new();
if let Some(query) = req.uri().query() {
let query_pairs = url::form_urlencoded::parse(query.as_bytes());
for (key, val) in query_pairs {
params.insert(key.to_lowercase(), val.to_string());
}
}
if let Some(key) = key {
match req.method() {
&Method::HEAD => Ok(handle_head(garage, &bucket, &key).await?),
&Method::GET => Ok(handle_get(garage, &bucket, &key).await?),
&Method::HEAD => {
// HeadObject query
Ok(handle_head(garage, &bucket, &key).await?)
}
&Method::GET => {
// GetObject query
Ok(handle_get(garage, &bucket, &key).await?)
}
&Method::PUT => {
let mime_type = req
.headers()
.get(hyper::header::CONTENT_TYPE)
.map(|x| x.to_str())
.unwrap_or(Ok("blob"))?
.to_string();
let version_uuid =
handle_put(garage, &mime_type, &bucket, &key, req.into_body()).await?;
let response = format!("{}\n", hex::encode(version_uuid,));
Ok(Response::new(Box::new(BytesBody::from(response))))
if ["partnumber", "uploadid"]
.iter()
.all(|x| params.contains_key(&x.to_string()))
{
let part_number = params.get("partnumber").unwrap();
let upload_id = params.get("uploadid").unwrap();
Ok(handle_put_part(garage, req, &bucket, &key, part_number, upload_id).await?)
} else {
// PutObject query
Ok(handle_put(garage, req, &bucket, &key).await?)
}
}
&Method::DELETE => {
// DeleteObject query
let version_uuid = handle_delete(garage, &bucket, &key).await?;
let response = format!("{}\n", hex::encode(version_uuid,));
let response = format!("{}\n", hex::encode(version_uuid));
Ok(Response::new(Box::new(BytesBody::from(response))))
}
&Method::POST => {
if params.contains_key(&"uploads".to_string()) {
// CreateMultipartUpload call
Ok(handle_create_multipart_upload(garage, &req, &bucket, &key).await?)
} else if params.contains_key(&"uploadid".to_string()) {
let upload_id = params.get("uploadid").unwrap();
Ok(handle_complete_multipart_upload(garage, req, &bucket, &key, upload_id).await?)
} else {
Err(Error::BadRequest(format!(
"Not a CreateMultipartUpload call, what is it?"
)))
}
}
_ => Err(Error::BadRequest(format!("Invalid method"))),
}
} else {
match req.method() {
&Method::PUT | &Method::HEAD => {
// If PUT: corresponds to a bucket creation call
// If PUT: CreateBucket, if HEAD: HeadBucket
// If we're here, the bucket already exists, so just answer ok
let empty_body: BodyType = Box::new(BytesBody::from(vec![]));
let response = Response::builder()
@@ -135,21 +161,18 @@ async fn handler_inner(
.unwrap();
Ok(response)
}
&Method::DELETE => Err(Error::Forbidden(
"Cannot delete buckets using S3 api, please talk to Garage directly".into(),
)),
&Method::DELETE => {
// DeleteBucket query
Err(Error::Forbidden(
"Cannot delete buckets using S3 api, please talk to Garage directly".into(),
))
}
&Method::GET => {
let mut params = HashMap::new();
if let Some(query) = req.uri().query() {
let query_pairs = url::form_urlencoded::parse(query.as_bytes());
for (key, val) in query_pairs {
params.insert(key.to_lowercase(), val.to_string());
}
}
if ["delimiter", "prefix"]
.iter()
.all(|x| params.contains_key(&x.to_string()))
{
// ListObjects query
let delimiter = params.get("delimiter").unwrap();
let max_keys = params
.get("max-keys")