async_io_typed/
lib.rs

1// Copyright 2022 Jacob Kiesel
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![doc = include_str!("../README.md")]
16
17use std::io;
18
19use bincode::Options;
20
21#[cfg(test)]
22mod tests;
23
24mod duplex;
25mod read;
26mod write;
27
28pub use duplex::*;
29pub use read::*;
30pub use write::*;
31
32pub(crate) const U16_MARKER: u8 = 252;
33pub(crate) const U32_MARKER: u8 = 253;
34pub(crate) const U64_MARKER: u8 = 254;
35pub(crate) const ZST_MARKER: u8 = 255;
36
37// These values chosen such that zeroed data and data that is all ones doesn't resemble either of them.
38pub(crate) const CHECKSUM_ENABLED: u8 = 2;
39pub(crate) const CHECKSUM_DISABLED: u8 = 3;
40
41// Current protocol version. The original implementation had no version number, version 2 is the first version with a version number.
42pub(crate) const PROTOCOL_VERSION: u64 = 2;
43
44/// Errors that might arise while using a typed stream.
45#[derive(Debug, thiserror::Error)]
46pub enum Error {
47    /// Error from `std::io`
48    #[error("io error {0}")]
49    Io(#[from] io::Error),
50    /// Error from the `bincode` crate
51    #[error("bincode serialization/deserialization error {0}")]
52    Bincode(#[from] bincode::Error),
53    /// A message was sent that exceeded the configured length limit
54    #[error("message sent exceeded configured length limit")]
55    SentMessageTooLarge,
56    /// A message was received that exceeded the configured length limit
57    #[error("message received exceeded configured length limit, terminating connection")]
58    ReceivedMessageTooLarge,
59    /// A checksum mismatch occurred, indicating that the data was corrupted. This error will never occur if
60    /// either side of the channel has checksums disabled.
61    #[error("checksum mismatch, data corrupted or there was a protocol mismatch")]
62    ChecksumMismatch {
63        sent_checksum: u64,
64        computed_checksum: u64,
65    },
66    /// The peer is using an incompatible protocol version.
67    #[error("the peer is using an incompatible protocol version. Our version {our_version}, Their version {their_version}")]
68    ProtocolVersionMismatch {
69        our_version: u64,
70        their_version: u64,
71    },
72    /// When exchanging information about whether to use a checksum or not, the peer sent us something unexpected.
73    #[error("checksum handshake failed, expected {CHECKSUM_ENABLED} or {CHECKSUM_DISABLED}, got {checksum_value}")]
74    ChecksumHandshakeFailed { checksum_value: u8 },
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
78pub enum ChecksumEnabled {
79    Yes,
80    No,
81}
82
83impl From<bool> for ChecksumEnabled {
84    fn from(value: bool) -> Self {
85        if value {
86            ChecksumEnabled::Yes
87        } else {
88            ChecksumEnabled::No
89        }
90    }
91}
92
93impl From<ChecksumEnabled> for bool {
94    fn from(value: ChecksumEnabled) -> Self {
95        value == ChecksumEnabled::Yes
96    }
97}
98
99impl From<ChecksumEnabled> for ChecksumReadState {
100    fn from(value: ChecksumEnabled) -> Self {
101        match value {
102            ChecksumEnabled::Yes => ChecksumReadState::Yes,
103            ChecksumEnabled::No => ChecksumReadState::No,
104        }
105    }
106}
107
108fn bincode_options(size_limit: u64) -> impl Options {
109    // Two of these are defaults, so you might say this is over specified. I say it's future proof, as
110    // bincode default changes won't introduce accidental breaking changes.
111    bincode::DefaultOptions::new()
112        .with_limit(size_limit)
113        .with_little_endian()
114        .with_varint_encoding()
115        .reject_trailing_bytes()
116}