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