1 | use crate::frame::{Error, Frame, Head, Kind, StreamId}; |
2 | use bytes::BufMut; |
3 | |
4 | const ACK_FLAG: u8 = 0x1; |
5 | |
6 | pub type Payload = [u8; 8]; |
7 | |
8 | #[derive (Debug, Eq, PartialEq)] |
9 | pub struct Ping { |
10 | ack: bool, |
11 | payload: Payload, |
12 | } |
13 | |
14 | // This was just 8 randomly generated bytes. We use something besides just |
15 | // zeroes to distinguish this specific PING from any other. |
16 | const SHUTDOWN_PAYLOAD: Payload = [0x0b, 0x7b, 0xa2, 0xf0, 0x8b, 0x9b, 0xfe, 0x54]; |
17 | const USER_PAYLOAD: Payload = [0x3b, 0x7c, 0xdb, 0x7a, 0x0b, 0x87, 0x16, 0xb4]; |
18 | |
19 | impl Ping { |
20 | #[cfg (feature = "unstable" )] |
21 | pub const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD; |
22 | |
23 | #[cfg (not(feature = "unstable" ))] |
24 | pub(crate) const SHUTDOWN: Payload = SHUTDOWN_PAYLOAD; |
25 | |
26 | #[cfg (feature = "unstable" )] |
27 | pub const USER: Payload = USER_PAYLOAD; |
28 | |
29 | #[cfg (not(feature = "unstable" ))] |
30 | pub(crate) const USER: Payload = USER_PAYLOAD; |
31 | |
32 | pub fn new(payload: Payload) -> Ping { |
33 | Ping { |
34 | ack: false, |
35 | payload, |
36 | } |
37 | } |
38 | |
39 | pub fn pong(payload: Payload) -> Ping { |
40 | Ping { ack: true, payload } |
41 | } |
42 | |
43 | pub fn is_ack(&self) -> bool { |
44 | self.ack |
45 | } |
46 | |
47 | pub fn payload(&self) -> &Payload { |
48 | &self.payload |
49 | } |
50 | |
51 | pub fn into_payload(self) -> Payload { |
52 | self.payload |
53 | } |
54 | |
55 | /// Builds a `Ping` frame from a raw frame. |
56 | pub fn load(head: Head, bytes: &[u8]) -> Result<Ping, Error> { |
57 | debug_assert_eq!(head.kind(), crate::frame::Kind::Ping); |
58 | |
59 | // PING frames are not associated with any individual stream. If a PING |
60 | // frame is received with a stream identifier field value other than |
61 | // 0x0, the recipient MUST respond with a connection error |
62 | // (Section 5.4.1) of type PROTOCOL_ERROR. |
63 | if !head.stream_id().is_zero() { |
64 | return Err(Error::InvalidStreamId); |
65 | } |
66 | |
67 | // In addition to the frame header, PING frames MUST contain 8 octets of opaque |
68 | // data in the payload. |
69 | if bytes.len() != 8 { |
70 | return Err(Error::BadFrameSize); |
71 | } |
72 | |
73 | let mut payload = [0; 8]; |
74 | payload.copy_from_slice(bytes); |
75 | |
76 | // The PING frame defines the following flags: |
77 | // |
78 | // ACK (0x1): When set, bit 0 indicates that this PING frame is a PING |
79 | // response. An endpoint MUST set this flag in PING responses. An |
80 | // endpoint MUST NOT respond to PING frames containing this flag. |
81 | let ack = head.flag() & ACK_FLAG != 0; |
82 | |
83 | Ok(Ping { ack, payload }) |
84 | } |
85 | |
86 | pub fn encode<B: BufMut>(&self, dst: &mut B) { |
87 | let sz = self.payload.len(); |
88 | tracing::trace!("encoding PING; ack= {} len= {}" , self.ack, sz); |
89 | |
90 | let flags = if self.ack { ACK_FLAG } else { 0 }; |
91 | let head = Head::new(Kind::Ping, flags, StreamId::zero()); |
92 | |
93 | head.encode(sz, dst); |
94 | dst.put_slice(&self.payload); |
95 | } |
96 | } |
97 | |
98 | impl<T> From<Ping> for Frame<T> { |
99 | fn from(src: Ping) -> Frame<T> { |
100 | Frame::Ping(src) |
101 | } |
102 | } |
103 | |