1use std::fmt;
2
3use ntex_bytes::{BufMut, Bytes, BytesMut};
4
5use crate::frame::{self, FrameError, Head, Kind, Reason, StreamId};
6
7#[derive(Clone, Eq, PartialEq)]
8pub struct GoAway {
9 last_stream_id: StreamId,
10 error_code: Reason,
11 data: Bytes,
12}
13
14impl GoAway {
15 pub fn new(reason: Reason) -> Self {
16 GoAway {
17 last_stream_id: 0.into(),
18 data: Bytes::new(),
19 error_code: reason,
20 }
21 }
22
23 pub fn set_last_stream_id(mut self, id: StreamId) -> Self {
24 self.last_stream_id = id;
25 self
26 }
27
28 pub fn set_data<T>(mut self, data: T) -> Self
29 where
30 Bytes: From<T>,
31 {
32 self.data = data.into();
33 self
34 }
35
36 pub fn set_reason(mut self, error_code: Reason) -> Self {
37 self.error_code = error_code;
38 self
39 }
40
41 pub fn last_stream_id(&self) -> StreamId {
42 self.last_stream_id
43 }
44
45 pub fn reason(&self) -> Reason {
46 self.error_code
47 }
48
49 pub fn data(&self) -> &Bytes {
50 &self.data
51 }
52
53 pub fn load(payload: &[u8]) -> Result<GoAway, FrameError> {
54 if payload.len() < 8 {
55 return Err(FrameError::BadFrameSize);
56 }
57
58 let (last_stream_id, _) = StreamId::parse(&payload[..4]);
59 let error_code = unpack_octets_4!(payload, 4, u32);
60 let data = Bytes::copy_from_slice(&payload[8..]);
61
62 Ok(GoAway {
63 last_stream_id,
64 data,
65 error_code: error_code.into(),
66 })
67 }
68
69 pub fn encode(&self, dst: &mut BytesMut) {
70 log::trace!("encoding GO_AWAY; code={:?}", self.error_code);
71 let head = Head::new(Kind::GoAway, 0, StreamId::zero());
72 head.encode(8 + self.data.len(), dst);
73 dst.put_u32(self.last_stream_id.into());
74 dst.put_u32(self.error_code.into());
75 dst.extend_from_slice(&self.data);
76 }
77}
78
79impl From<GoAway> for frame::Frame {
80 fn from(src: GoAway) -> Self {
81 frame::Frame::GoAway(src)
82 }
83}
84
85impl fmt::Debug for GoAway {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 let mut builder = f.debug_struct("GoAway");
88 builder.field("error_code", &self.error_code);
89 builder.field("last_stream_id", &self.last_stream_id);
90
91 if !self.data.is_empty() {
92 builder.field("data", &self.data);
93 }
94
95 builder.finish()
96 }
97}