Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
refactor!: Switch to Rocket master, lamedh, and http 0.2
  • Loading branch information
dbanty committed Feb 10, 2021
1 parent 6b28c56 commit 2d4fe51
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 108 deletions.
9 changes: 5 additions & 4 deletions Cargo.toml
Expand Up @@ -14,8 +14,9 @@ categories = ["web-programming::http-server"]
travis-ci = { repository = "GREsau/rocket-lamb" }

[dependencies]
rocket = { version = "0.4.0", default-features = false }
lambda_runtime = "0.2.1"
lambda_http = "0.1.1"
http = "0.1"
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master" }
lamedh_runtime = "0.3.0"
lamedh_http = "0.3.0"
http = "0.2.3"
failure = "0.1.5"
aws_lambda_events = "0.4.0"
14 changes: 8 additions & 6 deletions src/builder.rs
@@ -1,6 +1,7 @@
use crate::config::*;
use crate::handler::{LazyClient, RocketHandler};
use lambda_http::lambda;
use lamedh_http::handler;
use lamedh_runtime::run;
use rocket::Rocket;

/// A builder to create and configure a [RocketHandler](RocketHandler).
Expand Down Expand Up @@ -34,10 +35,11 @@ impl RocketHandlerBuilder {
///
/// ```rust,no_run
/// use rocket_lamb::RocketExt;
/// use lambda_http::lambda;
/// use lamedh_runtime::run;
/// use lamedh_http::handler;
///
/// let handler = rocket::ignite().lambda().into_handler();
/// lambda!(handler);
/// let rocket_handler = rocket::ignite().lambda().into_handler();
/// run(handler(rocket_handler));
/// ```
pub fn into_handler(self) -> RocketHandler {
RocketHandler {
Expand All @@ -58,12 +60,12 @@ impl RocketHandlerBuilder {
///
/// ```rust,no_run
/// use rocket_lamb::RocketExt;
/// use lambda_http::lambda;
/// use lambda_http::lambda::lambda;
///
/// rocket::ignite().lambda().launch();
/// ```
pub fn launch(self) -> ! {
lambda!(self.into_handler());
run(handler(self.into_handler()));
unreachable!("lambda! should loop forever (or panic)")
}

Expand Down
153 changes: 82 additions & 71 deletions src/handler.rs
@@ -1,12 +1,15 @@
use crate::config::*;
use crate::error::RocketLambError;
use crate::request_ext::RequestExt as _;
use lambda_http::{Body, Handler, Request, RequestExt, Response};
use lambda_runtime::{error::HandlerError, Context};
use aws_lambda_events::encodings::Body;
use lamedh_http::{Handler, Request, RequestExt, Response};
use lamedh_runtime::Context;
use rocket::http::{uri::Uri, Header};
use rocket::local::{Client, LocalRequest, LocalResponse};
use rocket::local::blocking::{Client, LocalRequest, LocalResponse};
use rocket::{Rocket, Route};
use std::future::Future;
use std::mem;
use std::pin::Pin;

/// A Lambda handler for API Gateway events that processes requests using a [Rocket](rocket::Rocket) instance.
pub struct RocketHandler {
Expand All @@ -20,12 +23,20 @@ pub(super) enum LazyClient {
Ready(Client),
}

impl Handler<Response<Body>> for RocketHandler {
fn run(&mut self, req: Request, _ctx: Context) -> Result<Response<Body>, HandlerError> {
impl Handler for RocketHandler {
type Error = failure::Error;
type Response = Response<Body>;
type Fut = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + 'static>>;

fn call(&mut self, req: Request, _ctx: Context) -> Self::Fut {
self.ensure_client_ready(&req);
self.process_request(req)
.map_err(failure::Error::from)
.map_err(failure::Error::into)
let fut = async {
process_request(self, req)
.await
.map_err(failure::Error::from)
.map_err(failure::Error::into)
};
Box::pin(fut)
}
}

Expand Down Expand Up @@ -60,69 +71,6 @@ impl RocketHandler {
}
}

fn process_request(&self, req: Request) -> Result<Response<Body>, RocketLambError> {
let local_req = self.create_rocket_request(req)?;
let local_res = local_req.dispatch();
self.create_lambda_response(local_res)
}

fn create_rocket_request(&self, req: Request) -> Result<LocalRequest, RocketLambError> {
let method = to_rocket_method(req.method())?;
let uri = self.get_path_and_query(&req);
let mut local_req = self.client().req(method, uri);
for (name, value) in req.headers() {
match value.to_str() {
Ok(v) => local_req.add_header(Header::new(name.to_string(), v.to_string())),
Err(_) => return Err(invalid_request!("invalid value for header '{}'", name)),
}
}
local_req.set_body(req.into_body());
Ok(local_req)
}

fn create_lambda_response(
&self,
mut local_res: LocalResponse,
) -> Result<Response<Body>, RocketLambError> {
let mut builder = Response::builder();
builder.status(local_res.status().code);
for h in local_res.headers().iter() {
builder.header(&h.name.to_string(), &h.value.to_string());
}

let response_type = local_res
.headers()
.get_one("content-type")
.unwrap_or_default()
.split(';')
.next()
.and_then(|ct| self.config.response_types.get(&ct.to_lowercase()))
.copied()
.unwrap_or(self.config.default_response_type);
let body = match (local_res.body(), response_type) {
(Some(b), ResponseType::Auto) => {
let bytes = b
.into_bytes()
.ok_or_else(|| invalid_response!("failed to read response body"))?;
match String::from_utf8(bytes) {
Ok(s) => Body::Text(s),
Err(e) => Body::Binary(e.into_bytes()),
}
}
(Some(b), ResponseType::Text) => Body::Text(
b.into_string()
.ok_or_else(|| invalid_response!("failed to read response body as UTF-8"))?,
),
(Some(b), ResponseType::Binary) => Body::Binary(
b.into_bytes()
.ok_or_else(|| invalid_response!("failed to read response body"))?,
),
(None, _) => Body::Empty,
};

builder.body(body).map_err(|e| invalid_response!("{}", e))
}

fn get_path_and_query(&self, req: &Request) -> String {
let mut uri = match self.config.base_path_behaviour {
BasePathBehaviour::Include | BasePathBehaviour::RemountAndInclude => req.full_path(),
Expand All @@ -146,6 +94,69 @@ impl RocketHandler {
}
}

async fn process_request(
handler: &RocketHandler,
req: Request,
) -> Result<Response<Body>, RocketLambError> {
let local_req = create_rocket_request(handler, req)?;
let local_res = local_req.dispatch();
create_lambda_response(handler, local_res).await
}

fn create_rocket_request(
handler: &RocketHandler,
req: Request,
) -> Result<LocalRequest, RocketLambError> {
let method = to_rocket_method(req.method())?;
let uri = handler.get_path_and_query(&req);
let mut local_req = handler.client().req(method, uri);
for (name, value) in req.headers() {
match value.to_str() {
Ok(v) => local_req.add_header(Header::new(name.to_string(), v.to_string())),
Err(_) => return Err(invalid_request!("invalid value for header '{}'", name)),
}
}
local_req.set_body(req.into_body());
Ok(local_req)
}

async fn create_lambda_response(
handler: &RocketHandler,
local_res: LocalResponse<'_>,
) -> Result<Response<Body>, RocketLambError> {
let mut builder = Response::builder();
builder = builder.status(local_res.status().code);
for h in local_res.headers().iter() {
builder = builder.header(&h.name.to_string(), &h.value.to_string());
}

let response_type = local_res
.headers()
.get_one("content-type")
.unwrap_or_default()
.split(';')
.next()
.and_then(|ct| handler.config.response_types.get(&ct.to_lowercase()))
.copied()
.unwrap_or(handler.config.default_response_type);
let body = match local_res.into_bytes() {
Some(b) => match response_type {
ResponseType::Auto => match String::from_utf8(b) {
Ok(s) => Body::Text(s),
Err(e) => Body::Binary(e.into_bytes()),
},
ResponseType::Text => Body::Text(
String::from_utf8(b)
.map_err(|_| invalid_response!("failed to read response body as UTF-8"))?,
),
ResponseType::Binary => Body::Binary(b),
},
None => Body::Empty,
};

builder.body(body).map_err(|e| invalid_response!("{}", e))
}

fn to_rocket_method(method: &http::Method) -> Result<rocket::http::Method, RocketLambError> {
use http::Method as H;
use rocket::http::Method::*;
Expand Down
56 changes: 29 additions & 27 deletions src/request_ext.rs
@@ -1,6 +1,6 @@
use http::header::HOST;
use lambda_http::request::RequestContext;
use lambda_http::{Request, RequestExt as _};
use lamedh_http::request::RequestContext;
use lamedh_http::{Request, RequestExt as _};

pub(crate) trait RequestExt {
fn full_path(&self) -> String;
Expand All @@ -12,7 +12,7 @@ pub(crate) trait RequestExt {

impl RequestExt for Request {
fn full_path(&self) -> String {
if self.request_context().is_alb() || !is_default_api_gateway_url(self) {
if matches!(self.request_context(), RequestContext::Alb(_)) || !is_default_api_gateway_url(self) {
self.uri().path().to_owned()
} else {
let mut path = self.base_path();
Expand All @@ -22,33 +22,35 @@ impl RequestExt for Request {
}

fn base_path(&self) -> String {
match self.request_context() {
RequestContext::ApiGateway {
stage,
resource_path,
..
} => {
if is_default_api_gateway_url(self) {
format!("/{}", stage)
} else {
let resource_path = populate_resource_path(self, resource_path);
let full_path = self.uri().path();
let resource_path_index =
full_path.rfind(&resource_path).unwrap_or_else(|| {
panic!(
"Could not find segment '{}' in path '{}'.",
resource_path, full_path
)
});
full_path[..resource_path_index].to_owned()
}
}
RequestContext::Alb { .. } => String::new(),
}
// TODO: Find out what this is for and is supposed to return >.>
String::new()
// match self.request_context() {
// RequestContext::ApiGateway {
// stage,
// resource_path,
// ..
// } => {
// if is_default_api_gateway_url(self) {
// format!("/{}", stage)
// } else {
// let resource_path = populate_resource_path(self, resource_path);
// let full_path = self.uri().path();
// let resource_path_index =
// full_path.rfind(&resource_path).unwrap_or_else(|| {
// panic!(
// "Could not find segment '{}' in path '{}'.",
// resource_path, full_path
// )
// });
// full_path[..resource_path_index].to_owned()
// }
// }
// RequestContext::Alb { .. } => String::new(),
// }
}

fn api_path(&self) -> &str {
if self.request_context().is_alb() || is_default_api_gateway_url(self) {
if matches!(self.request_context(), RequestContext::Alb(_)) || is_default_api_gateway_url(self) {
self.uri().path()
} else {
&self.uri().path()[self.base_path().len()..]
Expand Down

0 comments on commit 2d4fe51

Please sign in to comment.