|
1 | 1 | use crate::render::{HeaderContext, PageContext, RenderContext};
|
2 |
| -use crate::webserver::database::{stream_query_results, DbItem}; |
| 2 | +use crate::webserver::database::{stream_query_results, DbItem, ErrorWithStatus}; |
3 | 3 | use crate::{AppState, Config, ParsedSqlFile};
|
4 | 4 | use actix_web::dev::{fn_service, ServiceFactory, ServiceRequest};
|
5 | 5 | use actix_web::error::ErrorInternalServerError;
|
6 | 6 | use actix_web::http::header::{ContentType, Header, HttpDate, IfModifiedSince, LastModified};
|
| 7 | +use actix_web::http::{header, StatusCode}; |
7 | 8 | use actix_web::web::Form;
|
8 | 9 | use actix_web::{
|
9 | 10 | dev::ServiceResponse, middleware, middleware::Logger, web, web::Bytes, App, FromRequest,
|
10 | 11 | HttpResponse, HttpServer,
|
11 | 12 | };
|
12 | 13 |
|
13 |
| -use actix_web::body::MessageBody; |
| 14 | +use actix_web::body::{BoxBody, MessageBody}; |
14 | 15 | use actix_web_httpauth::headers::authorization::{Authorization, Basic};
|
15 | 16 | use anyhow::Context;
|
16 | 17 | use chrono::{DateTime, Utc};
|
@@ -145,56 +146,40 @@ async fn stream_response(
|
145 | 146 | async fn build_response_header_and_stream<S: Stream<Item = DbItem>>(
|
146 | 147 | app_state: Arc<AppState>,
|
147 | 148 | database_entries: S,
|
148 |
| -) -> actix_web::Result<ResponseWithWriter<S>> { |
| 149 | +) -> anyhow::Result<ResponseWithWriter<S>> { |
149 | 150 | let (sender, receiver) = mpsc::channel(MAX_PENDING_MESSAGES);
|
150 | 151 | let writer = ResponseWriter::new(sender);
|
151 | 152 | let mut head_context = HeaderContext::new(app_state, writer);
|
152 | 153 | let mut stream = Box::pin(database_entries);
|
153 | 154 | while let Some(item) = stream.next().await {
|
154 | 155 | match item {
|
155 |
| - DbItem::Row(data) => { |
156 |
| - match head_context.handle_row(data).await.map_err(|e| { |
157 |
| - log::error!("Error while handling header context data: {e}"); |
158 |
| - ErrorInternalServerError(e) |
159 |
| - })? { |
160 |
| - PageContext::Header(h) => { |
161 |
| - head_context = h; |
162 |
| - } |
163 |
| - PageContext::Body { |
164 |
| - mut http_response, |
| 156 | + DbItem::Row(data) => match head_context.handle_row(data).await? { |
| 157 | + PageContext::Header(h) => { |
| 158 | + head_context = h; |
| 159 | + } |
| 160 | + PageContext::Body { |
| 161 | + mut http_response, |
| 162 | + renderer, |
| 163 | + } => { |
| 164 | + let body_stream = tokio_stream::wrappers::ReceiverStream::new(receiver); |
| 165 | + let http_response = http_response.streaming(body_stream); |
| 166 | + return Ok(ResponseWithWriter { |
| 167 | + http_response, |
165 | 168 | renderer,
|
166 |
| - } => { |
167 |
| - let body_stream = tokio_stream::wrappers::ReceiverStream::new(receiver); |
168 |
| - let http_response = http_response.streaming(body_stream); |
169 |
| - return Ok(ResponseWithWriter { |
170 |
| - http_response, |
171 |
| - renderer, |
172 |
| - database_entries_stream: stream, |
173 |
| - }); |
174 |
| - } |
175 |
| - PageContext::Close(h) => { |
176 |
| - head_context = h; |
177 |
| - break; |
178 |
| - } |
| 169 | + database_entries_stream: stream, |
| 170 | + }); |
179 | 171 | }
|
180 |
| - } |
181 |
| - DbItem::FinishedQuery => { |
182 |
| - log::debug!("finished query"); |
183 |
| - } |
184 |
| - DbItem::Error(source_err) => { |
185 |
| - let err = anyhow::format_err!( |
186 |
| - "An error occurred at the top of your SQL file: {source_err:#}" |
187 |
| - ); |
188 |
| - log::error!("Response building error: {err}"); |
189 |
| - return Err(ErrorInternalServerError(err)); |
190 |
| - } |
| 172 | + PageContext::Close(h) => { |
| 173 | + head_context = h; |
| 174 | + break; |
| 175 | + } |
| 176 | + }, |
| 177 | + DbItem::FinishedQuery => log::debug!("finished query"), |
| 178 | + DbItem::Error(source_err) => return Err(source_err), |
191 | 179 | }
|
192 | 180 | }
|
193 | 181 | log::debug!("No SQL statements left to execute for the body of the response");
|
194 |
| - let (renderer, http_response) = head_context.close().await.map_err(|e| { |
195 |
| - log::error!("Error while closing header context: {e}"); |
196 |
| - ErrorInternalServerError(e) |
197 |
| - })?; |
| 182 | + let (renderer, http_response) = head_context.close().await?; |
198 | 183 | Ok(ResponseWithWriter {
|
199 | 184 | http_response,
|
200 | 185 | renderer,
|
@@ -237,18 +222,42 @@ async fn render_sql(
|
237 | 222 | .unwrap_or_else(|e| log::error!("could not send headers {e:?}"));
|
238 | 223 | stream_response(database_entries_stream, renderer).await;
|
239 | 224 | }
|
240 |
| - Err(e) => { |
241 |
| - log::error!("An error occured while building response headers: {e}"); |
242 |
| - let http_response = ErrorInternalServerError(e).into(); |
243 |
| - resp_send |
244 |
| - .send(http_response) |
245 |
| - .unwrap_or_else(|_| log::error!("could not send headers")); |
| 225 | + Err(err) => { |
| 226 | + send_anyhow_error(&err, resp_send); |
246 | 227 | }
|
247 | 228 | }
|
248 | 229 | });
|
249 | 230 | resp_recv.await.map_err(ErrorInternalServerError)
|
250 | 231 | }
|
251 | 232 |
|
| 233 | +fn send_anyhow_error(e: &anyhow::Error, resp_send: tokio::sync::oneshot::Sender<HttpResponse>) { |
| 234 | + log::error!("An error occurred before starting to send the response body: {e:#}"); |
| 235 | + let mut resp = HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR).set_body(BoxBody::new( |
| 236 | + format!("Sorry, but we were not able to process your request. \n\nError:\n\n {e:?}"), |
| 237 | + )); |
| 238 | + resp.headers_mut().insert( |
| 239 | + header::CONTENT_TYPE, |
| 240 | + header::HeaderValue::from_static("text/plain"), |
| 241 | + ); |
| 242 | + if let Some(&ErrorWithStatus { status }) = e.downcast_ref() { |
| 243 | + *resp.status_mut() = status; |
| 244 | + if status == StatusCode::UNAUTHORIZED { |
| 245 | + resp.headers_mut().insert( |
| 246 | + header::WWW_AUTHENTICATE, |
| 247 | + header::HeaderValue::from_static( |
| 248 | + "Basic realm=\"Authentication required\", charset=\"UTF-8\"", |
| 249 | + ), |
| 250 | + ); |
| 251 | + resp = resp.set_body(BoxBody::new( |
| 252 | + "Sorry, but you are not authorized to access this page.", |
| 253 | + )); |
| 254 | + } |
| 255 | + }; |
| 256 | + resp_send |
| 257 | + .send(resp) |
| 258 | + .unwrap_or_else(|_| log::error!("could not send headers")); |
| 259 | +} |
| 260 | + |
252 | 261 | type ParamMap = HashMap<String, SingleOrVec>;
|
253 | 262 |
|
254 | 263 | #[derive(Debug)]
|
|
0 commit comments