1use crate::{
4 helper,
5 request::RequestContext,
6 timing::{ServerTiming, TimingMetric},
7};
8use bytes::Bytes;
9use etag::EntityTag;
10use serde::Serialize;
11use smallvec::SmallVec;
12use std::{
13 marker::PhantomData,
14 time::{Duration, Instant},
15};
16use zino_core::{
17 JsonValue, SharedString, Uuid, error::Error, extension::JsonValueExt, trace::TraceContext,
18 validation::Validation,
19};
20use zino_storage::NamedFile;
21
22#[cfg(feature = "cookie")]
23use cookie::Cookie;
24
25mod rejection;
26mod response_code;
27mod webhook;
28
29pub use rejection::{ExtractRejection, Rejection};
30pub use response_code::ResponseCode;
31pub use webhook::WebHook;
32
33#[cfg(feature = "http02")]
35pub type StatusCode = http02::StatusCode;
36
37#[cfg(not(feature = "http02"))]
39pub type StatusCode = http::StatusCode;
40
41pub type DataTransformer = fn(data: &JsonValue) -> Result<Bytes, Error>;
43
44#[derive(Debug, Clone, Serialize)]
46#[serde(rename_all = "snake_case")]
47pub struct Response<S: ResponseCode> {
48 #[serde(rename = "type")]
50 #[serde(skip_serializing_if = "Option::is_none")]
51 type_uri: Option<SharedString>,
52 #[serde(skip_serializing_if = "Option::is_none")]
54 title: Option<SharedString>,
55 #[serde(rename = "status")]
57 status_code: u16,
58 #[serde(rename = "error")]
60 #[serde(skip_serializing_if = "Option::is_none")]
61 error_code: Option<S::ErrorCode>,
62 #[serde(rename = "code")]
64 #[serde(skip_serializing_if = "Option::is_none")]
65 business_code: Option<S::BusinessCode>,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 detail: Option<SharedString>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 instance: Option<SharedString>,
72 success: bool,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 message: Option<SharedString>,
77 #[serde(skip)]
79 start_time: Instant,
80 #[serde(skip_serializing_if = "Uuid::is_nil")]
82 request_id: Uuid,
83 #[serde(rename = "data")]
85 #[serde(skip_serializing_if = "JsonValue::is_null")]
86 json_data: JsonValue,
87 #[serde(skip)]
89 bytes_data: Bytes,
90 #[serde(skip)]
92 data_transformer: Option<DataTransformer>,
93 #[serde(skip)]
95 content_type: Option<SharedString>,
96 #[serde(skip)]
98 trace_context: Option<TraceContext>,
99 #[serde(skip)]
101 server_timing: ServerTiming,
102 #[serde(skip)]
104 headers: SmallVec<[(SharedString, String); 8]>,
105 #[serde(skip)]
107 phantom: PhantomData<S>,
108}
109
110impl<S: ResponseCode> Response<S> {
111 pub fn new(code: S) -> Self {
113 let success = code.is_success();
114 let message = code.message();
115 let mut res = Self {
116 type_uri: code.type_uri(),
117 title: code.title(),
118 status_code: code.status_code(),
119 error_code: code.error_code(),
120 business_code: code.business_code(),
121 detail: None,
122 instance: None,
123 success,
124 message: None,
125 start_time: Instant::now(),
126 request_id: Uuid::nil(),
127 json_data: JsonValue::Null,
128 bytes_data: Bytes::new(),
129 data_transformer: None,
130 content_type: None,
131 trace_context: None,
132 server_timing: ServerTiming::new(),
133 headers: SmallVec::new(),
134 phantom: PhantomData,
135 };
136 if success {
137 res.message = message;
138 } else {
139 res.detail = message;
140 }
141 res
142 }
143
144 pub fn with_context<Ctx: RequestContext>(code: S, ctx: &Ctx) -> Self {
146 let success = code.is_success();
147 let message = code.message();
148 let mut res = Self {
149 type_uri: code.type_uri(),
150 title: code.title(),
151 status_code: code.status_code(),
152 error_code: code.error_code(),
153 business_code: code.business_code(),
154 detail: None,
155 instance: (!success).then(|| ctx.instance().into()),
156 success,
157 message: None,
158 start_time: ctx.start_time(),
159 request_id: ctx.request_id(),
160 json_data: JsonValue::Null,
161 bytes_data: Bytes::new(),
162 data_transformer: None,
163 content_type: None,
164 trace_context: None,
165 server_timing: ServerTiming::new(),
166 headers: SmallVec::new(),
167 phantom: PhantomData,
168 };
169 if success {
170 res.message = message;
171 } else {
172 res.detail = message;
173 }
174 res.trace_context = Some(ctx.new_trace_context());
175 res
176 }
177
178 pub fn context<Ctx: RequestContext>(mut self, ctx: &Ctx) -> Self {
180 self.instance = (!self.is_success()).then(|| ctx.instance().into());
181 self.start_time = ctx.start_time();
182 self.request_id = ctx.request_id();
183 self.trace_context = Some(ctx.new_trace_context());
184 self
185 }
186
187 #[cfg(feature = "view")]
189 pub fn render<T: Serialize>(mut self, template_name: &str, data: T) -> Self {
190 let result = serde_json::to_value(data)
191 .map_err(|err| err.into())
192 .and_then(|mut value| {
193 if let Some(data) = value.as_object_mut() {
194 let mut map = zino_core::Map::new();
195 map.append(data);
196 crate::view::render(template_name, map)
197 } else {
198 Err(zino_core::warn!("invalid template data"))
199 }
200 });
201 match result {
202 Ok(content) => {
203 self.json_data = content.into();
204 self.bytes_data = Bytes::new();
205 self.content_type = Some("text/html; charset=utf-8".into());
206 }
207 Err(err) => {
208 let code = S::INTERNAL_SERVER_ERROR;
209 self.type_uri = code.type_uri();
210 self.title = code.title();
211 self.status_code = code.status_code();
212 self.error_code = code.error_code();
213 self.business_code = code.business_code();
214 self.success = false;
215 self.detail = Some(err.to_string().into());
216 self.message = None;
217 self.json_data = JsonValue::Null;
218 self.bytes_data = Bytes::new();
219 }
220 }
221 self
222 }
223
224 pub fn set_code(&mut self, code: S) {
226 let success = code.is_success();
227 let message = code.message();
228 self.type_uri = code.type_uri();
229 self.title = code.title();
230 self.status_code = code.status_code();
231 self.error_code = code.error_code();
232 self.business_code = code.business_code();
233 self.success = success;
234 if success {
235 self.detail = None;
236 self.message = message;
237 } else {
238 self.detail = message;
239 self.message = None;
240 }
241 }
242
243 #[inline]
245 pub fn set_status_code(&mut self, status_code: impl Into<u16>) {
246 self.status_code = status_code.into();
247 }
248
249 #[inline]
251 pub fn set_error_code(&mut self, error_code: impl Into<S::ErrorCode>) {
252 self.error_code = Some(error_code.into());
253 }
254
255 #[inline]
257 pub fn set_business_code(&mut self, business_code: impl Into<S::BusinessCode>) {
258 self.business_code = Some(business_code.into());
259 }
260
261 #[inline]
263 pub fn set_instance(&mut self, instance: impl Into<SharedString>) {
264 self.instance = Some(instance.into());
265 }
266
267 pub fn set_message(&mut self, message: impl Into<SharedString>) {
270 fn inner<S: ResponseCode>(res: &mut Response<S>, message: SharedString) {
271 if res.is_success() {
272 res.detail = None;
273 res.message = Some(message);
274 } else {
275 res.detail = Some(message);
276 res.message = None;
277 }
278 }
279 inner::<S>(self, message.into())
280 }
281
282 pub fn set_error_message(&mut self, error: impl Into<Error>) {
284 fn inner<S: ResponseCode>(res: &mut Response<S>, error: Error) {
285 let message = error.to_string().into();
286 if res.is_success() {
287 res.detail = None;
288 res.message = Some(message);
289 } else {
290 res.detail = Some(message);
291 res.message = None;
292 }
293 }
294 inner::<S>(self, error.into())
295 }
296
297 #[inline]
299 pub fn set_data<T: Serialize>(&mut self, data: &T) {
300 match serde_json::to_value(data) {
301 Ok(value) => {
302 self.json_data = value;
303 self.bytes_data = Bytes::new();
304 }
305 Err(err) => self.set_error_message(err),
306 }
307 }
308
309 #[inline]
311 pub fn set_json_data(&mut self, data: impl Into<JsonValue>) {
312 self.json_data = data.into();
313 self.bytes_data = Bytes::new();
314 }
315
316 #[inline]
318 pub fn set_bytes_data(&mut self, data: impl Into<Bytes>) {
319 self.json_data = JsonValue::Null;
320 self.bytes_data = data.into();
321 }
322
323 #[inline]
325 pub fn set_validation_data(&mut self, validation: Validation) {
326 self.json_data = validation.into_map().into();
327 self.bytes_data = Bytes::new();
328 }
329
330 #[inline]
332 pub fn set_data_transformer(&mut self, transformer: DataTransformer) {
333 self.data_transformer = Some(transformer);
334 }
335
336 #[inline]
351 pub fn set_content_type(&mut self, content_type: impl Into<SharedString>) {
352 self.content_type = Some(content_type.into());
353 }
354
355 #[inline]
357 pub fn set_form_response(&mut self, data: impl Into<JsonValue>) {
358 fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
359 res.set_json_data(data);
360 res.set_content_type("application/x-www-form-urlencoded");
361 res.set_data_transformer(|data| {
362 let mut bytes = Vec::new();
363 serde_qs::to_writer(&data, &mut bytes)?;
364 Ok(bytes.into())
365 });
366 }
367 inner::<S>(self, data.into())
368 }
369
370 #[inline]
372 pub fn set_json_response(&mut self, data: impl Into<JsonValue>) {
373 fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
374 res.set_json_data(data);
375 res.set_data_transformer(|data| Ok(serde_json::to_vec(&data)?.into()));
376 }
377 inner::<S>(self, data.into())
378 }
379
380 #[inline]
382 pub fn set_jsonlines_response(&mut self, data: impl Into<JsonValue>) {
383 fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
384 res.set_json_data(data);
385 res.set_content_type("application/jsonlines; charset=utf-8");
386 res.set_data_transformer(|data| Ok(data.to_jsonlines(Vec::new())?.into()));
387 }
388 inner::<S>(self, data.into())
389 }
390
391 #[inline]
393 pub fn set_csv_response(&mut self, data: impl Into<JsonValue>) {
394 fn inner<S: ResponseCode>(res: &mut Response<S>, data: JsonValue) {
395 res.set_json_data(data);
396 res.set_content_type("text/csv; charset=utf-8");
397 res.set_data_transformer(|data| Ok(data.to_csv(Vec::new())?.into()));
398 }
399 inner::<S>(self, data.into())
400 }
401
402 #[inline]
404 pub fn set_text_response(&mut self, data: impl Into<String>) {
405 self.set_json_data(data.into());
406 self.set_content_type("text/plain; charset=utf-8");
407 }
408
409 #[inline]
411 pub fn set_bytes_response(&mut self, data: impl Into<Bytes>) {
412 self.set_bytes_data(data);
413 self.set_content_type("application/octet-stream");
414 }
415
416 #[inline]
418 pub(crate) fn set_request_id(&mut self, request_id: Uuid) {
419 self.request_id = request_id;
420 }
421
422 #[inline]
424 pub(crate) fn set_trace_context(&mut self, trace_context: Option<TraceContext>) {
425 self.trace_context = trace_context;
426 }
427
428 #[inline]
430 pub(crate) fn set_start_time(&mut self, start_time: Instant) {
431 self.start_time = start_time;
432 }
433
434 #[cfg(feature = "cookie")]
436 #[inline]
437 pub fn set_cookie(&mut self, cookie: &Cookie<'_>) {
438 self.insert_header("set-cookie", cookie.to_string());
439 }
440
441 pub fn record_server_timing(
443 &mut self,
444 name: impl Into<SharedString>,
445 description: impl Into<Option<SharedString>>,
446 duration: impl Into<Option<Duration>>,
447 ) {
448 fn inner<S: ResponseCode>(
449 res: &mut Response<S>,
450 name: SharedString,
451 description: Option<SharedString>,
452 duration: Option<Duration>,
453 ) {
454 let metric = TimingMetric::new(name, description, duration);
455 res.server_timing.push(metric);
456 }
457 inner::<S>(self, name.into(), description.into(), duration.into())
458 }
459
460 #[inline]
462 pub fn insert_header(&mut self, name: impl Into<SharedString>, value: impl ToString) {
463 self.headers.push((name.into(), value.to_string()));
464 }
465
466 #[inline]
468 pub fn get_header(&self, name: &str) -> Option<&str> {
469 self.headers
470 .iter()
471 .find_map(|(key, value)| (key == name).then_some(value.as_str()))
472 }
473
474 #[inline]
476 pub fn status_code(&self) -> u16 {
477 self.status_code
478 }
479
480 #[inline]
482 pub fn error_code(&self) -> Option<&S::ErrorCode> {
483 self.error_code.as_ref()
484 }
485
486 #[inline]
488 pub fn business_code(&self) -> Option<&S::BusinessCode> {
489 self.business_code.as_ref()
490 }
491
492 #[inline]
494 pub fn is_success(&self) -> bool {
495 self.success
496 }
497
498 #[inline]
500 pub fn has_context(&self) -> bool {
501 self.trace_context.is_some() && !self.request_id.is_nil()
502 }
503
504 #[inline]
506 pub fn message(&self) -> Option<&str> {
507 self.detail
508 .as_ref()
509 .or(self.message.as_ref())
510 .map(|s| s.as_ref())
511 }
512
513 #[inline]
515 pub fn request_id(&self) -> Uuid {
516 self.request_id
517 }
518
519 #[inline]
521 pub fn trace_id(&self) -> Uuid {
522 if let Some(ref trace_context) = self.trace_context {
523 Uuid::from_u128(trace_context.trace_id())
524 } else {
525 Uuid::nil()
526 }
527 }
528
529 #[inline]
531 pub fn content_type(&self) -> &str {
532 self.content_type.as_deref().unwrap_or_else(|| {
533 if !self.bytes_data.is_empty() {
534 "application/octet-stream"
535 } else if self.is_success() {
536 "application/json; charset=utf-8"
537 } else {
538 "application/problem+json; charset=utf-8"
539 }
540 })
541 }
542
543 #[inline]
545 pub fn headers(&self) -> &[(SharedString, String)] {
546 &self.headers
547 }
548
549 pub fn trace_context(&self) -> (String, String) {
551 if let Some(ref trace_context) = self.trace_context {
552 (trace_context.traceparent(), trace_context.tracestate())
553 } else {
554 let mut trace_context = TraceContext::new();
555 trace_context.record_trace_state();
556 (trace_context.traceparent(), trace_context.tracestate())
557 }
558 }
559
560 #[inline]
562 pub fn server_timing(&self) -> String {
563 self.server_timing.to_string()
564 }
565
566 pub fn read_bytes(&mut self) -> Result<Bytes, Error> {
568 let has_bytes_data = !self.bytes_data.is_empty();
569 let has_json_data = !self.json_data.is_null();
570 let bytes_opt = if has_bytes_data {
571 Some(self.bytes_data.clone())
572 } else if has_json_data {
573 if let Some(transformer) = self.data_transformer.as_ref() {
574 Some(transformer(&self.json_data)?)
575 } else {
576 None
577 }
578 } else {
579 None
580 };
581 if let Some(bytes) = bytes_opt {
582 let etag = EntityTag::from_data(&bytes);
583 self.insert_header("x-etag", etag);
584 return Ok(bytes);
585 }
586
587 let content_type = self.content_type();
588 let (bytes, etag_opt) = if crate::helper::check_json_content_type(content_type) {
589 let (capacity, etag_opt) = if has_json_data {
590 let data = serde_json::to_vec(&self.json_data)?;
591 let etag = EntityTag::from_data(&data);
592 (data.len() + 128, Some(etag))
593 } else {
594 (128, None)
595 };
596 let mut bytes = Vec::with_capacity(capacity);
597 serde_json::to_writer(&mut bytes, &self)?;
598 (bytes, etag_opt)
599 } else if has_json_data {
600 let value = &self.json_data;
601 let bytes = if content_type.starts_with("text/csv") {
602 value.to_csv(Vec::new())?
603 } else if content_type.starts_with("application/jsonlines") {
604 value.to_jsonlines(Vec::new())?
605 } else if let JsonValue::String(s) = value {
606 s.as_bytes().to_vec()
607 } else {
608 value.to_string().into_bytes()
609 };
610 (bytes, None)
611 } else {
612 (Vec::new(), None)
613 };
614 let etag = etag_opt.unwrap_or_else(|| EntityTag::from_data(&bytes));
615 self.insert_header("x-etag", etag);
616 Ok(bytes.into())
617 }
618
619 pub fn response_time(&self) -> Duration {
625 let start_time = self.start_time;
626 #[cfg(feature = "metrics")]
627 {
628 let labels = [("status_code", self.status_code().to_string())];
629 metrics::gauge!("zino_http_requests_in_flight").decrement(1.0);
630 metrics::counter!("zino_http_responses_total", &labels).increment(1);
631 metrics::histogram!("zino_http_requests_duration_seconds", &labels,)
632 .record(start_time.elapsed().as_secs_f64());
633 }
634 start_time.elapsed()
635 }
636
637 pub fn send_file(&mut self, file: NamedFile) {
639 let mut displayed_inline = false;
640 if let Some(content_type) = file.content_type() {
641 displayed_inline = helper::displayed_inline(content_type);
642 self.set_content_type(content_type.to_string());
643 }
644 if !displayed_inline {
645 if let Some(file_name) = file.file_name() {
646 self.insert_header(
647 "content-disposition",
648 format!(r#"attachment; filename="{file_name}""#),
649 );
650 }
651 }
652 self.insert_header("etag", file.etag());
653 self.set_bytes_data(Bytes::from(file));
654 }
655
656 pub fn finalize(mut self) -> impl Iterator<Item = (SharedString, String)> {
658 let request_id = self.request_id();
659 if !request_id.is_nil() {
660 self.insert_header("x-request-id", request_id.to_string());
661 }
662
663 let (traceparent, tracestate) = self.trace_context();
664 self.insert_header("traceparent", traceparent);
665 self.insert_header("tracestate", tracestate);
666
667 let duration = self.response_time();
668 self.record_server_timing("total", None, Some(duration));
669 self.insert_header("server-timing", self.server_timing());
670
671 self.headers.into_iter()
672 }
673}
674
675impl Response<StatusCode> {
676 #[inline]
678 pub fn ok() -> Self {
679 Response::new(StatusCode::OK)
680 }
681
682 #[inline]
684 pub fn created() -> Self {
685 Response::new(StatusCode::CREATED)
686 }
687
688 #[inline]
690 pub fn bad_request() -> Self {
691 Response::new(StatusCode::BAD_REQUEST)
692 }
693
694 #[inline]
696 pub fn not_found() -> Self {
697 Response::new(StatusCode::NOT_FOUND)
698 }
699
700 #[inline]
702 pub fn internal_server_error() -> Self {
703 Response::new(StatusCode::INTERNAL_SERVER_ERROR)
704 }
705}
706
707impl<S: ResponseCode> Default for Response<S> {
708 #[inline]
709 fn default() -> Self {
710 Self::new(S::OK)
711 }
712}
713
714impl<S: ResponseCode> From<Validation> for Response<S> {
715 fn from(validation: Validation) -> Self {
716 if validation.is_success() {
717 Self::new(S::OK)
718 } else {
719 let mut res = Self::new(S::BAD_REQUEST);
720 res.set_validation_data(validation);
721 res
722 }
723 }
724}