use s3s::dto::{GetObjectInput, GetObjectOutput};
use s3s::route::S3Route;
use s3s::service::S3ServiceBuilder;
use s3s::{Body, HttpError, S3, S3Request, S3Response, S3Result};
use axum::Router;
use axum::error_handling::HandleError;
use axum::http::{Extensions, HeaderMap, Method, Response, StatusCode, Uri};
use axum::routing::get;
use tokio::net::TcpListener;
#[derive(Debug, Clone)]
struct DummyS3;
#[async_trait::async_trait]
impl S3 for DummyS3 {
async fn get_object(&self, _req: S3Request<GetObjectInput>) -> S3Result<S3Response<GetObjectOutput>> {
tracing::info!("DummyS3 GetObject called");
Err(s3s::s3_error!(NotImplemented, "GetObject is not implemented"))
}
}
#[derive(Debug, Clone)]
struct AssumeRoleRoute;
#[async_trait::async_trait]
impl S3Route for AssumeRoleRoute {
fn is_match(&self, method: &Method, uri: &Uri, headers: &HeaderMap, _: &mut Extensions) -> bool {
if method == Method::POST
&& uri.path() == "/"
&& let Some(val) = headers.get(hyper::header::CONTENT_TYPE)
&& val.as_bytes() == b"application/x-www-form-urlencoded"
{
return true;
}
false
}
async fn check_access(&self, _req: &mut S3Request<Body>) -> S3Result<()> {
Ok(())
}
async fn call(&self, _req: S3Request<Body>) -> S3Result<S3Response<Body>> {
tracing::info!("AssumeRole route called");
Err(s3s::s3_error!(NotImplemented, "STS operations are not supported in this example"))
}
}
async fn health_check() -> &'static str {
tracing::info!("Health check endpoint called");
"OK"
}
async fn handle_s3_error(err: HttpError) -> Response<Body> {
tracing::error!(?err, "S3 service error");
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Internal Server Error".to_string()))
.unwrap()
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env().add_directive(tracing::Level::INFO.into()))
.init();
let s3_service = {
let mut builder = S3ServiceBuilder::new(DummyS3);
builder.set_route(AssumeRoleRoute);
builder.build()
};
let s3_service = HandleError::new(s3_service, handle_s3_error);
let app = Router::new().route("/health", get(health_check)).fallback_service(s3_service);
let addr = "127.0.0.1:8014";
let listener = TcpListener::bind(addr).await.unwrap();
tracing::info!("Axum server listening on http://{}", addr);
tracing::info!("Health check: http://{}/health", addr);
tracing::info!("S3 endpoint: http://{}/", addr);
tracing::info!("Press Ctrl+C to stop");
axum::serve(listener, app).await.unwrap();
}