goose_http/response/
mod.rs1use std::{fmt, io};
7
8use bytes::Bytes;
9use futures_core::Stream;
10
11use crate::{
12 common::{HttpVersion, StatusCode},
13 headers::{HeaderName, Headers, header_keys},
14};
15
16pub type BoxBodyStream = Box<dyn Stream<Item = io::Result<Bytes>> + Send + Unpin>;
18
19pub enum ResponseBody {
21 Empty,
23 Full(Bytes),
25 Stream(BoxBodyStream),
27}
28
29impl fmt::Debug for ResponseBody {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 ResponseBody::Empty => f.write_str("ResponseBody::Empty"),
33 ResponseBody::Full(bytes) => f.debug_tuple("ResponseBody::Full").field(bytes).finish(),
34 ResponseBody::Stream(_) => f.write_str("ResponseBody::Stream(<stream>)"),
35 }
36 }
37}
38
39impl ResponseBody {
40 pub fn is_empty(&self) -> bool {
42 matches!(self, ResponseBody::Empty)
43 }
44
45 pub fn len(&self) -> Option<usize> {
47 match self {
48 ResponseBody::Empty => Some(0),
49 ResponseBody::Full(bytes) => Some(bytes.len()),
50 ResponseBody::Stream(_) => None,
51 }
52 }
53
54 pub fn is_stream(&self) -> bool {
56 matches!(self, ResponseBody::Stream(_))
57 }
58}
59
60#[derive(Debug)]
62pub struct Response {
63 version: HttpVersion,
64 status: StatusCode,
65 reason: Option<String>,
66 headers: Headers,
67 body: ResponseBody,
68 trailers: Option<Headers>,
69}
70
71impl Response {
72 pub fn new(status: StatusCode) -> Self {
74 Self {
75 version: HttpVersion::HTTP_1_1,
76 reason: None,
77 status,
78 headers: Headers::new(),
79 body: ResponseBody::Empty,
80 trailers: None,
81 }
82 }
83
84 pub fn builder(status: StatusCode) -> ResponseBuilder {
86 ResponseBuilder::new(status)
87 }
88
89 pub fn version(&self) -> HttpVersion {
91 self.version
92 }
93
94 pub fn status(&self) -> StatusCode {
96 self.status
97 }
98
99 pub fn reason_phrase(&self) -> &str {
101 self.reason
102 .as_deref()
103 .or_else(|| self.status.canonical_reason())
104 .unwrap_or("")
105 }
106
107 pub fn headers(&self) -> &Headers {
109 &self.headers
110 }
111
112 pub fn headers_mut(&mut self) -> &mut Headers {
114 &mut self.headers
115 }
116
117 pub fn body(&self) -> &ResponseBody {
119 &self.body
120 }
121
122 pub fn set_status(&mut self, status: StatusCode) {
124 self.status = status;
125 }
126
127 pub fn take_body(&mut self) -> ResponseBody {
129 std::mem::replace(&mut self.body, ResponseBody::Empty)
130 }
131
132 pub fn set_body(&mut self, body: ResponseBody) {
134 self.body = body;
135 }
136
137 pub fn set_body_bytes(&mut self, bytes: impl Into<Bytes>) {
139 self.body = ResponseBody::Full(bytes.into());
140 }
141
142 pub fn set_body_static(&mut self, bytes: &'static [u8]) {
144 self.body = ResponseBody::Full(Bytes::from_static(bytes));
145 }
146
147 pub fn set_body_text_static(&mut self, text: &'static str) {
149 self.set_body_static(text.as_bytes());
150 }
151
152 pub fn strip_body_for_head(&mut self) {
155 if let ResponseBody::Full(bytes) = &self.body {
156 if !self.headers.contains(header_keys::CONTENT_LENGTH) {
157 self.headers
158 .insert(header_keys::CONTENT_LENGTH, bytes.len().to_string());
159 }
160 }
161 self.body = ResponseBody::Empty;
162 }
163
164 pub fn trailers(&self) -> Option<&Headers> {
166 self.trailers.as_ref()
167 }
168
169 pub fn set_trailers(&mut self, trailers: Headers) {
171 self.trailers = Some(trailers);
172 }
173
174 pub fn take_trailers(&mut self) -> Option<Headers> {
176 self.trailers.take()
177 }
178
179 pub fn body_mut(&mut self) -> &mut ResponseBody {
181 &mut self.body
182 }
183
184 pub fn set_version(&mut self, version: HttpVersion) {
186 self.version = version;
187 }
188
189 pub fn set_reason(&mut self, reason: impl Into<String>) {
191 self.reason = Some(reason.into());
192 }
193}
194
195#[derive(Debug)]
197pub struct ResponseBuilder {
198 version: HttpVersion,
199 status: StatusCode,
200 reason: Option<String>,
201 headers: Headers,
202 body: ResponseBody,
203 trailers: Option<Headers>,
204}
205
206impl ResponseBuilder {
207 fn new(status: StatusCode) -> Self {
208 Self {
209 version: HttpVersion::HTTP_1_1,
210 status,
211 reason: None,
212 headers: Headers::new(),
213 body: ResponseBody::Empty,
214 trailers: None,
215 }
216 }
217
218 pub fn version(mut self, version: HttpVersion) -> Self {
220 self.version = version;
221 self
222 }
223
224 pub fn reason(mut self, reason: impl Into<String>) -> Self {
226 self.reason = Some(reason.into());
227 self
228 }
229
230 pub fn header(mut self, name: impl Into<HeaderName>, value: impl Into<String>) -> Self {
232 self.headers.insert(name, value);
233 self
234 }
235
236 pub fn body(mut self, body: ResponseBody) -> Self {
238 self.body = body;
239 self
240 }
241
242 pub fn body_bytes(mut self, bytes: impl Into<Bytes>) -> Self {
244 self.body = ResponseBody::Full(bytes.into());
245 self
246 }
247
248 pub fn body_static(mut self, bytes: &'static [u8]) -> Self {
250 self.body = ResponseBody::Full(Bytes::from_static(bytes));
251 self
252 }
253
254 pub fn text_static(self, text: &'static str) -> Self {
256 self.body_static(text.as_bytes())
257 }
258
259 pub fn trailers(mut self, trailers: Headers) -> Self {
261 self.trailers = Some(trailers);
262 self
263 }
264
265 pub fn build(self) -> Response {
267 Response {
268 version: self.version,
269 status: self.status,
270 reason: self.reason,
271 headers: self.headers,
272 body: self.body,
273 trailers: self.trailers,
274 }
275 }
276}