|
1 | 1 | use base::{ |
2 | 2 | runner::{ServerContext, ServerRunner}, |
3 | | - store::{FlowExecutionResult, FlowIdentifyResult}, |
4 | 3 | traits::Server as ServerTrait, |
5 | 4 | }; |
6 | 5 | use code0_flow::flow_service::ModuleDefinitionAppendix; |
7 | | -use http_body_util::{BodyExt, Full}; |
8 | 6 | use hyper::server::conn::http1; |
9 | | -use hyper::{Request, Response}; |
10 | | -use hyper::{ |
11 | | - StatusCode, |
12 | | - body::{Bytes, Incoming}, |
13 | | -}; |
14 | 7 | use hyper_util::rt::TokioIo; |
15 | | -use std::convert::Infallible; |
16 | 8 | use std::net::SocketAddr; |
17 | 9 | use std::sync::Arc; |
18 | 10 | use tokio::net::TcpListener; |
19 | 11 | use tonic::async_trait; |
20 | | -use tucana::shared::{ |
21 | | - Endpoint, ModuleDefinition, Struct, ValidationFlow, Value, helper::value::ToValue, value::Kind, |
22 | | -}; |
23 | | - |
24 | | -use crate::auth::{authenticate_header_name, validate_flow_auth}; |
25 | | -use crate::response::{error_to_http_response, value_to_http_response}; |
| 12 | +use tucana::shared::{Endpoint, ModuleDefinition}; |
26 | 13 |
|
27 | 14 | mod auth; |
28 | 15 | mod config; |
29 | 16 | mod content_type; |
| 17 | +mod request; |
30 | 18 | mod response; |
31 | 19 | mod route; |
32 | 20 |
|
@@ -69,129 +57,6 @@ struct HttpServer { |
69 | 57 | addr: Option<SocketAddr>, |
70 | 58 | } |
71 | 59 |
|
72 | | -async fn execute_flow_to_hyper_response( |
73 | | - flow: ValidationFlow, |
74 | | - body: Value, |
75 | | - store: Arc<base::store::AdapterStore>, |
76 | | -) -> Response<Full<Bytes>> { |
77 | | - match store.execute_flow_with_emitter(flow, Some(body)).await { |
78 | | - FlowExecutionResult::Ongoing(result) => { |
79 | | - log::debug!("Received first ongoing response from emitter"); |
80 | | - value_to_http_response(result) |
81 | | - } |
82 | | - FlowExecutionResult::Failed => { |
83 | | - log::error!("Flow execution failed event received from emitter"); |
84 | | - error_to_http_response(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error") |
85 | | - } |
86 | | - FlowExecutionResult::FinishedWithoutOngoing => Response::builder() |
87 | | - .status(StatusCode::NO_CONTENT) |
88 | | - .body(Full::new(Bytes::new())) |
89 | | - .unwrap(), |
90 | | - FlowExecutionResult::TransportError => { |
91 | | - log::error!("Flow execution transport error"); |
92 | | - error_to_http_response(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error") |
93 | | - } |
94 | | - } |
95 | | -} |
96 | | - |
97 | | -pub async fn handle_request( |
98 | | - req: Request<Incoming>, |
99 | | - store: Arc<base::store::AdapterStore>, |
100 | | -) -> Result<Response<Full<Bytes>>, Infallible> { |
101 | | - let method = req.method().clone(); |
102 | | - let path = req.uri().path().to_string(); |
103 | | - let headers = req.headers().clone(); |
104 | | - |
105 | | - // Read full body |
106 | | - let body_bytes = match BodyExt::collect(req.into_body()).await { |
107 | | - Ok(collected) => collected.to_bytes().to_vec(), |
108 | | - Err(err) => { |
109 | | - log::error!("Failed to read request body: {}", err); |
110 | | - return Ok(error_to_http_response( |
111 | | - StatusCode::BAD_REQUEST, |
112 | | - "Failed to read request body", |
113 | | - )); |
114 | | - } |
115 | | - }; |
116 | | - |
117 | | - // slug matching |
118 | | - let Some(slug) = route::extract_slug_from_path(&path) else { |
119 | | - return Ok(error_to_http_response( |
120 | | - StatusCode::BAD_REQUEST, |
121 | | - "Missing slug in path", |
122 | | - )); |
123 | | - }; |
124 | | - |
125 | | - let pattern = format!("REST.{}.*", slug); |
126 | | - let route = route::RequestRoute { |
127 | | - url: path.clone(), |
128 | | - method, |
129 | | - }; |
130 | | - |
131 | | - let resp = match store.get_possible_flow_match(pattern, route).await { |
132 | | - FlowIdentifyResult::Single(flow) => { |
133 | | - if let Err(err) = validate_flow_auth(&flow, &headers) { |
134 | | - let mut response = error_to_http_response(err.status_code(), err.message()); |
135 | | - response |
136 | | - .headers_mut() |
137 | | - .insert(authenticate_header_name(), err.challenge()); |
138 | | - return Ok(response); |
139 | | - } |
140 | | - |
141 | | - let request_body_value = |
142 | | - match content_type::parse_body_from_headers(&headers, &body_bytes) { |
143 | | - Ok(value) => value, |
144 | | - Err(err) => { |
145 | | - log::warn!("Failed to parse request body: {}", err); |
146 | | - let status_code = match err { |
147 | | - content_type::BodyParseError::UnsupportedContentType { .. } => { |
148 | | - StatusCode::UNSUPPORTED_MEDIA_TYPE |
149 | | - } |
150 | | - _ => StatusCode::BAD_REQUEST, |
151 | | - }; |
152 | | - |
153 | | - return Ok(error_to_http_response(status_code, &err.to_string())); |
154 | | - } |
155 | | - }; |
156 | | - |
157 | | - let mut header_fields = std::collections::HashMap::new(); |
158 | | - let mut fields = std::collections::HashMap::new(); |
159 | | - |
160 | | - for (name, value) in headers.iter() { |
161 | | - let key = name.as_str().to_owned(); |
162 | | - let value_str = value |
163 | | - .to_str() |
164 | | - .map(str::to_owned) |
165 | | - .unwrap_or_else(|_| String::from_utf8_lossy(value.as_bytes()).into_owned()); |
166 | | - |
167 | | - header_fields.insert(key, value_str.to_value()); |
168 | | - } |
169 | | - |
170 | | - if let Some(v) = request_body_value { |
171 | | - fields.insert(String::from("payload"), v); |
172 | | - }; |
173 | | - |
174 | | - fields.insert( |
175 | | - String::from("headers"), |
176 | | - Value { |
177 | | - kind: Some(Kind::StructValue(Struct { |
178 | | - fields: header_fields, |
179 | | - })), |
180 | | - }, |
181 | | - ); |
182 | | - |
183 | | - let input = Value { |
184 | | - kind: Some(Kind::StructValue(Struct { fields })), |
185 | | - }; |
186 | | - |
187 | | - execute_flow_to_hyper_response(flow, input, store).await |
188 | | - } |
189 | | - _ => error_to_http_response(StatusCode::NOT_FOUND, "No flow found for path"), |
190 | | - }; |
191 | | - |
192 | | - Ok(resp) |
193 | | -} |
194 | | - |
195 | 60 | #[async_trait] |
196 | 61 | impl ServerTrait<config::HttpServerConfig> for HttpServer { |
197 | 62 | async fn init(&mut self, ctx: &ServerContext<config::HttpServerConfig>) -> anyhow::Result<()> { |
@@ -247,7 +112,7 @@ impl ServerTrait<config::HttpServerConfig> for HttpServer { |
247 | 112 | tokio::spawn(async move { |
248 | 113 | let svc = hyper::service::service_fn(move |req| { |
249 | 114 | let store = Arc::clone(&store); |
250 | | - async move { handle_request(req, store).await } |
| 115 | + async move { request::handle(req, store).await } |
251 | 116 | }); |
252 | 117 |
|
253 | 118 | let conn = http1::Builder::new().serve_connection(io, svc); |
|
0 commit comments