We are able to serve a file

This commit is contained in:
Quentin 2020-11-21 15:15:25 +01:00
parent d4c7f4e374
commit 0f33231ee6
3 changed files with 109 additions and 5 deletions

1
Cargo.lock generated
View File

@ -660,6 +660,7 @@ dependencies = [
"garage_util 0.1.0", "garage_util 0.1.0",
"hex", "hex",
"http", "http",
"httpdate",
"hyper", "hyper",
"idna", "idna",
"log", "log",

View File

@ -42,3 +42,5 @@ webpki = "0.21"
roxmltree = "0.11" roxmltree = "0.11"
idna = "0.2" idna = "0.2"
httpdate = "0.3"

View File

@ -2,17 +2,23 @@ use std::borrow::Cow;
use std::convert::Infallible; use std::convert::Infallible;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, UNIX_EPOCH};
use futures::future::Future; use futures::future::Future;
use futures::stream::*;
use hyper::header::HOST; use hyper::{
use hyper::server::conn::AddrStream; header::HOST,
use hyper::service::{make_service_fn, service_fn}; body::Bytes,
use hyper::{Body, Request, Response, Server}; server::conn::AddrStream,
service::{make_service_fn, service_fn},
Body, Request, Response, Server, StatusCode};
use idna::domain_to_unicode; use idna::domain_to_unicode;
use garage_model::garage::Garage; use garage_model::garage::Garage;
use garage_model::object_table::*;
use garage_table::EmptyKey;
use garage_util::error::Error as GarageError; use garage_util::error::Error as GarageError;
use crate::error::*; use crate::error::*;
@ -90,7 +96,102 @@ async fn serve_file(garage: Arc<Garage>, req: Request<Body>) -> Result<Response<
.await? .await?
.ok_or(Error::NotFound)?; .ok_or(Error::NotFound)?;
Ok(Response::new(Body::from("hello world\n"))) // Get last complete version descriptor
let last_v = object
.versions()
.iter()
.rev()
.filter(|v| v.is_complete())
.next()
.ok_or(Error::NotFound)?;
// Unwrap version
let last_v_data = match &last_v.state {
ObjectVersionState::Complete(x) => x,
_ => unreachable!(),
};
// Get metadata from version
let last_v_meta = match last_v_data {
ObjectVersionData::DeleteMarker => return Err(Error::NotFound),
ObjectVersionData::Inline(meta, _) => meta,
ObjectVersionData::FirstBlock(meta, _) => meta,
};
// @FIXME Support range
// Set headers
let resp_builder = object_headers(&last_v, last_v_meta).status(StatusCode::OK);
// Stream body
match &last_v_data {
ObjectVersionData::DeleteMarker => unreachable!(),
ObjectVersionData::Inline(_, bytes) => {
let body: Body = Body::from(bytes.to_vec());
Ok(resp_builder.body(body)?)
}
ObjectVersionData::FirstBlock(_, first_block_hash) => {
let read_first_block = garage.block_manager.rpc_get_block(&first_block_hash);
let get_next_blocks = garage.version_table.get(&last_v.uuid, &EmptyKey);
let (first_block, version) = futures::try_join!(read_first_block, get_next_blocks)?;
let version = version.ok_or(Error::NotFound)?;
let mut blocks = version
.blocks()
.iter()
.map(|vb| (vb.hash, None))
.collect::<Vec<_>>();
blocks[0].1 = Some(first_block);
let body_stream = futures::stream::iter(blocks)
.map(move |(hash, data_opt)| {
let garage = garage.clone();
async move {
if let Some(data) = data_opt {
Ok(Bytes::from(data))
} else {
garage
.block_manager
.rpc_get_block(&hash)
.await
.map(Bytes::from)
}
}
})
.buffered(2);
//let body: Body = Box::new(StreamBody::new(Box::pin(body_stream)));
let body = hyper::body::Body::wrap_stream(body_stream);
Ok(resp_builder.body(body)?)
}
}
}
// Copied from api/s3_get.rs
fn object_headers(
version: &ObjectVersion,
version_meta: &ObjectVersionMeta,
) -> http::response::Builder {
let date = UNIX_EPOCH + Duration::from_millis(version.timestamp);
let date_str = httpdate::fmt_http_date(date);
let mut resp = Response::builder()
.header(
"Content-Type",
version_meta.headers.content_type.to_string(),
)
.header("Content-Length", format!("{}", version_meta.size))
.header("ETag", version_meta.etag.to_string())
.header("Last-Modified", date_str)
.header("Accept-Ranges", format!("bytes"));
for (k, v) in version_meta.headers.other.iter() {
resp = resp.header(k, v.to_string());
}
resp
} }
/// Extract host from the authority section given by the HTTP host header /// Extract host from the authority section given by the HTTP host header