Skip to content

Commit

Permalink
add get_object API closes #4 (#10)
Browse files Browse the repository at this point in the history
* add get_object API closes #4

* cargo fix

* update doc
  • Loading branch information
oneslash authored May 22, 2023
1 parent efec677 commit e724d4f
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 12 deletions.
1 change: 1 addition & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ jobs:
aws --endpoint-url http://localhost:9090 s3 mb s3://sardo
aws --endpoint-url http://localhost:9090 s3 ls
aws --endpoint-url http://localhost:9090 s3 cp ./Cargo.toml s3://sardo
aws --endpoint-url http://localhost:9090 s3 cp s3://sardo/Cargo.toml /tmp/Cargo.toml
20 changes: 20 additions & 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ tracing-subscriber = "0.3"
serde = { version = "1.0", features = ["derive"] }
chrono = "0.4"
quick-xml = { version = "0.28", features = ["serialize"] }
mime_guess = "2.0"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ $ ./s3-chelak --server-url "my_custom_url" --server-port "8080" --working-folder
| GetBucketTagging | :x: |
| GetBucketVersioning | :x: |
| GetBucketWebsite | :x: |
| GetObject | :x: |
| GetObject | :white_check_mark: |
| GetObjectAcl | :x: |
| GetObjectLegalHold | :x: |
| GetObjectLockConfiguration | :x: |
Expand All @@ -95,7 +95,7 @@ $ ./s3-chelak --server-url "my_custom_url" --server-port "8080" --working-folder
| GetObjectTorrent | :x: |
| GetPublicAccessBlock | :x: |
| HeadBucket | :x: |
| HeadObject | :x: |
| HeadObject | :white_check_mark: |
| ListBucketAnalyticsConfigurations | :x: |
| ListBucketIntelligentTieringConfigurations | :x: |
| ListBucketInventoryConfigurations | :x: |
Expand Down
2 changes: 1 addition & 1 deletion src/apis/create_bucket.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use actix_web::{error, put, web, Error, HttpResponse};
use actix_web::{error, web, Error, HttpResponse};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug)]
Expand Down
34 changes: 34 additions & 0 deletions src/apis/get_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::apis::get_object::get_object;
use crate::apis::list_buckets;
use actix_router::{Path, ResourceDef, Url};
use actix_web::{get, web, Error, HttpRequest, HttpResponse};

#[get("/{tail}*")]
pub async fn handle_get(
_: web::Path<String>,
req: HttpRequest,
data: web::Data<crate::AppState>,
) -> Result<HttpResponse, Error> {
let mut path = Path::new(Url::new(req.uri().clone()));

let routes = [
("/{bucket}/{object_name}", "bucket and object"),
("/", "get bucket"),
];

for (pattern, description) in &routes {
if ResourceDef::new(*pattern).capture_match_info(&mut path) {
return match (path.get("bucket"), path.get("object_name")) {
(Some(bucket), Some(object_name)) if *description == "bucket and object" => {
get_object(bucket.to_string(), object_name.to_string(), data).await
}
(None, None) if *description == "get bucket" => {
list_buckets::list_buckets(data).await
}
_ => return Ok(HttpResponse::InternalServerError().finish()),
};
}
}

Err(actix_web::error::ErrorNotFound("Not Found"))
}
49 changes: 49 additions & 0 deletions src/apis/get_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use actix_web::{error, head, web, Error, HttpRequest, HttpResponse};
use std::fs::File;
use std::io::Read;

pub async fn get_object(
bucket_name: String,
object_name: String,
data: web::Data<crate::AppState>,
) -> Result<HttpResponse, Error> {
let working_folder = &data.working_folder;
let filepath = format!("{}/{}/{}", working_folder, bucket_name, object_name);

let mut file = File::open(filepath)?;
if file.metadata()?.is_file() {
let mut content = Vec::new();
file.read_to_end(&mut content)?;
return Ok(HttpResponse::Ok().body(content));
}

Err(error::ErrorNotFound("Not Found"))
}

#[head("/{bucket_name}/{object_name}")]
pub async fn get_object_head(
req: HttpRequest,
data: web::Data<crate::AppState>,
) -> Result<HttpResponse, Error> {
let working_folder = &data.working_folder;
let bucket_name = req.match_info().get("bucket_name").unwrap();
let object_name = req.match_info().get("object_name").unwrap();

let filepath = format!("{}/{}/{}", working_folder, bucket_name, object_name);
let file = File::open(&filepath)?;

if file.metadata()?.is_file() {
let guess = mime_guess::from_path(&filepath).first_or_octet_stream();
let mut resp = HttpResponse::Ok();
resp.append_header(("Content-Length", file.metadata()?.len().to_string()));
resp.append_header(("Content-Type", guess.to_string()));
resp.append_header((
"Last-Modified",
crate::utils::get_timestamp(file.metadata()?.modified()?),
));

return Ok(resp.finish());
}

Ok(HttpResponse::Ok().finish())
}
3 changes: 1 addition & 2 deletions src/apis/list_buckets.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::utils::get_timestamp;
use actix_web::http::header::ContentType;
use actix_web::{get, web, Error, HttpResponse};
use actix_web::{web, Error, HttpResponse};
use quick_xml::se::to_string;
use serde::{Deserialize, Serialize};
use tracing::error;
Expand All @@ -27,7 +27,6 @@ pub struct Bucket {
}

/// List Buckets (GET Bucket) (https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html)
#[get("/")]
pub async fn list_buckets(data: web::Data<crate::AppState>) -> Result<HttpResponse, Error> {
let working_folder = &data.working_folder;
let entities = std::fs::read_dir(working_folder)?;
Expand Down
7 changes: 5 additions & 2 deletions src/apis/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
pub mod create_bucket;
pub mod get_handler;
pub mod list_buckets;
pub mod put_object;
pub mod put_handler;

mod create_bucket;
pub mod get_object;
mod put_object;
4 changes: 2 additions & 2 deletions src/apis/put_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use actix_router::{Path, Resource, ResourceDef, Url};
use actix_router::{Path, ResourceDef, Url};

use crate::apis::create_bucket::create_bucket;
use crate::apis::put_object::put_object;
Expand All @@ -9,7 +9,7 @@ pub async fn handle_put(
_: web::Path<String>,
req: HttpRequest,
data: web::Data<crate::AppState>,
mut payload: Option<web::Payload>,
payload: Option<web::Payload>,
) -> Result<HttpResponse, Error> {
let mut path = Path::new(Url::new(req.uri().clone()));

Expand Down
2 changes: 1 addition & 1 deletion src/apis/put_object.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use actix_web::{put, web, Error, FromRequest, HttpRequest, HttpResponse};
use actix_web::{web, Error, HttpResponse};
use futures::StreamExt;
use std::fs::File;
use std::io::Write;
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ async fn main() -> std::io::Result<()> {
App::new()
.wrap(middleware::Logger::default())
.service(apis::put_handler::handle_put)
// .service(apis::create_bucket::create_bucket)
.service(apis::list_buckets::list_buckets)
.service(apis::get_handler::handle_get)
.service(apis::get_object::get_object_head)
.default_service(web::route().to(not_found))
.app_data(web::Data::new(AppState {
working_folder: config.working_folder.clone(),
Expand Down

0 comments on commit e724d4f

Please sign in to comment.