1use crate::{
4 field::text::Ascii,
5 h1::{
6 field::{self, expect},
7 switch, target,
8 },
9 payload::response::Method,
10 request::{self, span},
11};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum Error {
15 ContentLengthWithTransferEncoding,
16 UnframedRequestTransferEncoding,
17 Http10TransferEncoding,
18 ForbiddenResponseFraming,
19 SuccessfulConnectIsTransition,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub enum Response {
24 Framing(Error),
25 Transition(switch::Error),
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29pub enum RequestError {
30 Authority(target::Error),
31 Framing(Error),
32 Expectation(expect::Error),
33}
34
35#[derive(Clone, Copy, PartialEq, Eq)]
36#[repr(u8)]
37pub enum Transfer {
38 None,
39 Chunked,
40 Other,
41}
42
43#[derive(Clone, Copy, PartialEq, Eq)]
44pub struct Message {
45 content_length: Option<usize>,
46 transfer: Transfer,
47}
48
49impl Message {
50 pub const fn new(length: Option<usize>, transfer: Transfer) -> Self {
51 Self {
52 content_length: length,
53 transfer,
54 }
55 }
56}
57
58#[derive(Clone, Copy, Default, PartialEq, Eq)]
60pub struct Fields {
61 content_length: Option<usize>,
62 transfer: field::Transfer,
63}
64
65impl Fields {
66 pub fn try_length(mut self, value: &[u8]) -> Result<Self, field::Error> {
67 if self.content_length.is_some() {
68 return Err(field::Error::DuplicateContentLength);
69 }
70 if self.transfer.has_encoding() {
71 return Err(field::Error::ContentLengthWithTransferEncoding);
72 }
73 let length = Ascii::new(crate::field::Error::trim_ows(value))
74 .parse_usize()
75 .map_err(field::Error::InvalidContentLength)?;
76 self.content_length = Some(length);
77 Ok(self)
78 }
79
80 pub fn try_set(mut self, length: usize) -> Result<Self, field::Error> {
83 if self.content_length.is_some() {
84 return Err(field::Error::DuplicateContentLength);
85 }
86 if self.transfer.has_encoding() {
87 return Err(field::Error::ContentLengthWithTransferEncoding);
88 }
89 self.content_length = Some(length);
90 Ok(self)
91 }
92
93 pub fn try_coding(mut self, value: &[u8]) -> Result<Self, field::Error> {
94 if self.content_length.is_some() {
95 return Err(field::Error::ContentLengthWithTransferEncoding);
96 }
97 let transfer = self
98 .transfer
99 .parse(value)
100 .map_err(field::Error::InvalidTransferEncoding)?;
101 self.transfer = transfer;
102 Ok(self)
103 }
104
105 pub fn try_append(self, value: field::Transfer) -> Result<Self, field::Error> {
108 if self.content_length.is_some() {
109 return Err(field::Error::ContentLengthWithTransferEncoding);
110 }
111 let transfer = self
112 .transfer
113 .try_join(value)
114 .map_err(field::Error::InvalidTransferEncoding)?;
115 Ok(Self {
116 content_length: self.content_length,
117 transfer,
118 })
119 }
120
121 pub const fn length(self) -> Option<usize> {
122 self.content_length
123 }
124
125 pub const fn has_coding(self) -> bool {
126 self.transfer.has_encoding()
127 }
128
129 pub const fn is_chunked(self) -> bool {
130 self.transfer.is_chunked()
131 }
132
133 pub(in crate::h1) const fn has_other(self) -> bool {
134 self.transfer.has_other()
135 }
136
137 pub const fn finish(self) -> Message {
138 Message::new(self.content_length, self.transfer.framing())
139 }
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq)]
143pub enum RequestBody {
144 Empty,
145 Fixed(usize),
146 Chunked,
147}
148
149#[derive(Clone, Copy, PartialEq, Eq)]
153pub struct Admission {
154 target: target::Target,
155 authority: target::Plan,
156 body: RequestBody,
157 expectation: expect::Decision,
158}
159
160impl Admission {
161 pub const fn target(self) -> target::Target {
162 self.target
163 }
164
165 pub const fn authority(self) -> target::Plan {
166 self.authority
167 }
168
169 pub const fn body(self) -> RequestBody {
170 self.body
171 }
172
173 pub const fn expectation(self) -> expect::Decision {
174 self.expectation
175 }
176
177 pub fn layout(
178 self,
179 target: span::Span,
180 host: Option<span::Span>,
181 ) -> Result<request::Layout, target::Error> {
182 let span = self.authority.span(self.target, host)?;
183 self.target.layout(target, span)
184 }
185
186 pub fn admit(
187 fields: Fields,
188 auth: target::Request,
189 expect: expect::State,
190 target: target::Target,
191 version: &[u8],
192 policy: target::Policy,
193 ) -> Result<Self, RequestError> {
194 let plan = auth
195 .finish_plan(target, version)
196 .and_then(|plan| match policy {
197 target::Policy::AllowEmpty => Ok(plan),
198 target::Policy::Require => plan.require(),
199 })
200 .map_err(RequestError::Authority)?;
201 let body = fields
202 .finish()
203 .try_version(version)
204 .and_then(Message::request)
205 .map_err(RequestError::Framing)?;
206 let decision = if version == b"HTTP/1.0" {
207 expect::Decision::None
208 } else {
209 expect.finish(body).map_err(RequestError::Expectation)?
210 };
211 Ok(Self {
212 target,
213 authority: plan,
214 body,
215 expectation: decision,
216 })
217 }
218}
219
220#[derive(Clone, Copy, PartialEq, Eq)]
221pub enum ResponseBody {
222 None,
223 Fixed(usize),
224 Chunked,
225 UntilEof,
226 Empty(Empty),
227}
228
229#[derive(Clone, Copy, PartialEq, Eq)]
232#[repr(C)]
233pub struct Control {
234 keep_alive_timeout_secs: u64,
235 connection: field::Connection,
236 reuse: Reuse,
237}
238
239#[derive(Clone, Copy, PartialEq, Eq)]
240#[repr(u8)]
241enum Reuse {
242 Close = 1,
243 Reusable = 2,
244 Timed = 3,
245}
246
247impl Control {
248 pub const fn new(
249 version: u8,
250 status: u16,
251 body: ResponseBody,
252 conn: field::Connection,
253 peer: field::KeepAlive,
254 ) -> Self {
255 let persist = version == 1 || version == 0 && conn.is_keep();
256 let (secs, reuse) = if status == 101
257 || !persist
258 || conn.is_close()
259 || conn.is_upgrade()
260 || matches!(
261 body,
262 ResponseBody::UntilEof | ResponseBody::Empty(Empty::UntilEof)
263 ) {
264 (0, Reuse::Close)
265 } else {
266 match peer.timeout_secs() {
267 Some(timeout) => (timeout, Reuse::Timed),
268 None => (0, Reuse::Reusable),
269 }
270 };
271 Self {
272 keep_alive_timeout_secs: secs,
273 connection: conn,
274 reuse,
275 }
276 }
277
278 pub const fn connection(self) -> field::Connection {
279 self.connection
280 }
281
282 pub const fn is_reusable(self) -> bool {
283 matches!(self.reuse, Reuse::Reusable | Reuse::Timed)
284 }
285
286 pub const fn timeout(self) -> Option<u64> {
287 match self.reuse {
288 Reuse::Timed => Some(self.keep_alive_timeout_secs),
289 Reuse::Close | Reuse::Reusable => None,
290 }
291 }
292}
293
294#[derive(Clone, Copy, PartialEq, Eq)]
298pub enum Outcome {
299 Informational,
300 Message(ResponseBody),
301 Switch(switch::Kind),
302}
303
304#[derive(Clone, Copy, PartialEq, Eq)]
307pub enum Empty {
308 Fixed,
309 Chunked,
310 UntilEof,
311}
312
313impl Message {
314 pub(in crate::h1) fn try_version(self, version: &[u8]) -> Result<Self, Error> {
315 if version == b"HTTP/1.0" && !matches!(self.transfer, Transfer::None) {
316 return Err(Error::Http10TransferEncoding);
317 }
318 Ok(self)
319 }
320
321 const fn is_unframed(self) -> bool {
322 self.content_length.is_none() && matches!(self.transfer, Transfer::None)
323 }
324
325 pub const fn request(self) -> Result<RequestBody, Error> {
326 if !matches!(self.transfer, Transfer::None) {
327 if self.content_length.is_some() {
328 return Err(Error::ContentLengthWithTransferEncoding);
329 }
330 return match self.transfer {
331 Transfer::Chunked => Ok(RequestBody::Chunked),
332 Transfer::Other => Err(Error::UnframedRequestTransferEncoding),
333 Transfer::None => Ok(RequestBody::Empty),
334 };
335 }
336 match self.content_length {
337 None | Some(0) => Ok(RequestBody::Empty),
338 Some(length) => Ok(RequestBody::Fixed(length)),
339 }
340 }
341
342 #[inline]
343 pub const fn response(self, request: Method, status: u16) -> Result<ResponseBody, Error> {
344 self.for_content(crate::payload::response::Content::new(request, status))
345 }
346
347 pub(in crate::h1) const fn for_content(
348 self,
349 content: crate::payload::response::Content,
350 ) -> Result<ResponseBody, Error> {
351 if self.content_length.is_some() && !matches!(self.transfer, Transfer::None) {
352 return Err(Error::ContentLengthWithTransferEncoding);
353 }
354 use crate::payload::response::Content;
355 match content {
356 Content::Forbidden => {
357 if !self.is_unframed() {
358 return Err(Error::ForbiddenResponseFraming);
359 }
360 return Ok(ResponseBody::None);
361 }
362 Content::Suppressed => return Ok(ResponseBody::None),
363 Content::Tunnel => return Err(Error::SuccessfulConnectIsTransition),
364 Content::Empty => {
365 if !matches!(self.content_length, None | Some(0)) {
366 return Err(Error::ForbiddenResponseFraming);
367 }
368 let empty = match self.transfer {
369 Transfer::None if matches!(self.content_length, Some(0)) => Empty::Fixed,
370 Transfer::None => Empty::UntilEof,
371 Transfer::Chunked => Empty::Chunked,
372 Transfer::Other => Empty::UntilEof,
373 };
374 return Ok(ResponseBody::Empty(empty));
375 }
376 Content::Allowed => {}
377 }
378 if !matches!(self.transfer, Transfer::None) {
379 return Ok(match self.transfer {
380 Transfer::Chunked => ResponseBody::Chunked,
381 Transfer::Other => ResponseBody::UntilEof,
382 Transfer::None => ResponseBody::UntilEof,
383 });
384 }
385 match self.content_length {
386 Some(length) => Ok(ResponseBody::Fixed(length)),
387 None => Ok(ResponseBody::UntilEof),
388 }
389 }
390}
391
392impl Outcome {
396 pub const fn admit(
397 request: Method,
398 intent: switch::Intent,
399 status: u16,
400 framing: Message,
401 conn: field::Connection,
402 upgrade: Option<switch::Selection>,
403 ) -> Result<Self, Response> {
404 if status == 101 {
405 return match intent {
406 switch::Intent::Upgrade => {
407 if !conn.is_upgrade() {
408 Err(Response::Transition(
409 switch::Error::MissingConnectionUpgrade,
410 ))
411 } else if upgrade.is_none() {
412 Err(Response::Transition(switch::Error::MissingSelection))
413 } else if !framing.is_unframed() {
414 Err(Response::Framing(Error::ForbiddenResponseFraming))
415 } else {
416 Ok(Self::Switch(switch::Kind::Upgrade))
417 }
418 }
419 switch::Intent::None | switch::Intent::Connect => {
420 Err(Response::Transition(switch::Error::UnexpectedSwitch))
421 }
422 };
423 }
424
425 if status < 200 {
426 if !framing.is_unframed() {
427 return Err(Response::Framing(Error::ForbiddenResponseFraming));
428 }
429 return Ok(Self::Informational);
430 }
431
432 if matches!(intent, switch::Intent::Connect) && status < 300 {
433 return Ok(Self::Switch(switch::Kind::Connect));
434 }
435
436 match framing.response(request, status) {
437 Ok(body) => Ok(Self::Message(body)),
438 Err(error) => Err(Response::Framing(error)),
439 }
440 }
441}
442
443const _: () = assert!(core::mem::size_of::<Fields>() <= 3 * core::mem::size_of::<usize>());
444const _: () = assert!(core::mem::size_of::<Admission>() <= 7 * core::mem::size_of::<usize>());
445const _: () = assert!(core::mem::size_of::<Control>() == 16);
446const _: () = assert!(core::mem::size_of::<Option<Control>>() == 16);