1use std::fmt;
2
3use bytes::{BufMut, Bytes};
4
5use crate::frame::{self, Error, Head, Kind, Reason, StreamId};
6
7#[derive(Clone, Eq, PartialEq)]
8pub struct GoAway {
9 last_stream_id: StreamId,
10 error_code: Reason,
11 debug_data: Bytes,
12}
13
14impl GoAway {
15 pub fn new(last_stream_id: StreamId, reason: Reason) -> Self {
16 GoAway {
17 last_stream_id,
18 error_code: reason,
19 debug_data: Bytes::new(),
20 }
21 }
22
23 pub fn with_debug_data(last_stream_id: StreamId, reason: Reason, debug_data: Bytes) -> Self {
24 Self {
25 last_stream_id,
26 error_code: reason,
27 debug_data,
28 }
29 }
30
31 pub fn last_stream_id(&self) -> StreamId {
32 self.last_stream_id
33 }
34
35 pub fn reason(&self) -> Reason {
36 self.error_code
37 }
38
39 pub fn debug_data(&self) -> &Bytes {
40 &self.debug_data
41 }
42
43 pub fn load(head: Head, payload: &[u8]) -> Result<GoAway, Error> {
44 debug_assert_eq!(head.kind(), crate::frame::Kind::GoAway);
45
46 if !head.stream_id().is_zero() {
51 return Err(Error::InvalidStreamId);
52 }
53
54 if payload.len() < 8 {
55 return Err(Error::BadFrameSize);
56 }
57
58 let (last_stream_id, _) = StreamId::parse(&payload[..4]);
59 let error_code = unpack_octets_4!(payload, 4, u32);
60 let debug_data = Bytes::copy_from_slice(&payload[8..]);
61
62 Ok(GoAway {
63 last_stream_id,
64 error_code: error_code.into(),
65 debug_data,
66 })
67 }
68
69 pub fn encode<B: BufMut>(&self, dst: &mut B) {
70 tracing::trace!("encoding GO_AWAY; code={:?}", self.error_code);
71 let head = Head::new(Kind::GoAway, 0, StreamId::zero());
72 head.encode(8 + self.debug_data.len(), dst);
73 dst.put_u32(self.last_stream_id.into());
74 dst.put_u32(self.error_code.into());
75 dst.put(self.debug_data.slice(..));
76 }
77}
78
79impl<B> From<GoAway> for frame::Frame<B> {
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.debug_data.is_empty() {
92 builder.field("debug_data", &self.debug_data);
93 }
94
95 builder.finish()
96 }
97}