@@ -7,6 +7,7 @@ use crate::webserver::content_security_policy::ContentSecurityPolicy;
77use crate :: webserver:: database:: execute_queries:: stop_at_first_error;
88use crate :: webserver:: database:: { execute_queries:: stream_query_results_with_conn, DbItem } ;
99use crate :: webserver:: http_request_info:: extract_request_info;
10+ use crate :: webserver:: server_timing:: ServerTiming ;
1011use crate :: webserver:: ErrorWithStatus ;
1112use crate :: { AppConfig , AppState , ParsedSqlFile , DEFAULT_404_FILE } ;
1213use actix_web:: dev:: { fn_service, ServiceFactory , ServiceRequest } ;
@@ -46,6 +47,7 @@ pub struct RequestContext {
4647 pub is_embedded : bool ,
4748 pub source_path : PathBuf ,
4849 pub content_security_policy : ContentSecurityPolicy ,
50+ pub server_timing : Option < ServerTiming > ,
4951}
5052
5153async fn stream_response ( stream : impl Stream < Item = DbItem > , mut renderer : AnyRenderBodyContext ) {
@@ -104,7 +106,14 @@ async fn build_response_header_and_stream<S: Stream<Item = DbItem>>(
104106 let writer = ResponseWriter :: new ( sender) ;
105107 let mut head_context = HeaderContext :: new ( app_state, request_context, writer) ;
106108 let mut stream = Box :: pin ( database_entries) ;
109+ let mut first_row = true ;
107110 while let Some ( item) = stream. next ( ) . await {
111+ if first_row {
112+ if let Some ( ref mut timing) = head_context. request_context . server_timing {
113+ timing. record ( "query" ) ;
114+ }
115+ first_row = false ;
116+ }
108117 let page_context = match item {
109118 DbItem :: Row ( data) => head_context. handle_row ( data) . await ?,
110119 DbItem :: FinishedQuery => {
@@ -167,21 +176,32 @@ async fn render_sql(
167176 let app_state = srv_req
168177 . app_data :: < web:: Data < AppState > > ( )
169178 . ok_or_else ( || ErrorInternalServerError ( "no state" ) ) ?
170- . clone ( ) // Cheap reference count increase
179+ . clone ( )
171180 . into_inner ( ) ;
172181
182+ let mut server_timing = if !app_state. config . environment . is_prod ( ) {
183+ Some ( ServerTiming :: new ( ) )
184+ } else {
185+ None
186+ } ;
187+
173188 let mut req_param = extract_request_info ( srv_req, Arc :: clone ( & app_state) )
174189 . await
175190 . map_err ( |e| anyhow_err_to_actix ( e, & app_state) ) ?;
176191 log:: debug!( "Received a request with the following parameters: {req_param:?}" ) ;
177192
193+ if let Some ( ref mut timing) = server_timing {
194+ timing. record ( "parse" ) ;
195+ }
196+
178197 let ( resp_send, resp_recv) = tokio:: sync:: oneshot:: channel :: < HttpResponse > ( ) ;
179198 let source_path: PathBuf = sql_file. source_path . clone ( ) ;
180199 actix_web:: rt:: spawn ( async move {
181200 let request_context = RequestContext {
182201 is_embedded : req_param. get_variables . contains_key ( "_sqlpage_embed" ) ,
183202 source_path,
184203 content_security_policy : ContentSecurityPolicy :: with_random_nonce ( ) ,
204+ server_timing,
185205 } ;
186206 let mut conn = None ;
187207 let database_entries_stream =
0 commit comments