Add capacity error types

pull/168/head
WiredSound 4 years ago
parent aaebb432f0
commit 0b34bee94f
  1. 81
      src/error.rs
  2. 4
      src/handshake/machine.rs
  3. 21
      src/protocol/frame/mod.rs
  4. 9
      src/protocol/message.rs
  5. 19
      src/protocol/mod.rs

@ -1,6 +1,6 @@
//! Error handling. //! Error handling.
use std::{borrow::Cow, error::Error as ErrorTrait, fmt, io, result, str, string}; use std::{error::Error as ErrorTrait, fmt, io, result, str, string};
use crate::protocol::{frame::coding::Data, Message}; use crate::protocol::{frame::coding::Data, Message};
use http::Response; use http::Response;
@ -46,7 +46,7 @@ pub enum Error {
/// - When reading: buffer capacity exhausted. /// - When reading: buffer capacity exhausted.
/// - When writing: your message is bigger than the configured max message size /// - When writing: your message is bigger than the configured max message size
/// (64MB by default). /// (64MB by default).
Capacity(Cow<'static, str>), Capacity(CapacityErrorType),
/// Protocol violation. /// Protocol violation.
Protocol(ProtocolErrorType), Protocol(ProtocolErrorType),
/// Message send queue full. /// Message send queue full.
@ -146,38 +146,39 @@ impl From<tls::Error> for Error {
impl From<httparse::Error> for Error { impl From<httparse::Error> for Error {
fn from(err: httparse::Error) -> Self { fn from(err: httparse::Error) -> Self {
match err { match err {
httparse::Error::TooManyHeaders => Error::Capacity("Too many headers".into()), httparse::Error::TooManyHeaders => Error::Capacity(CapacityErrorType::TooManyHeaders),
e => Error::Protocol(ProtocolErrorType::HttparseError(e)), e => Error::Protocol(ProtocolErrorType::HttparseError(e)),
} }
} }
} }
/// Indicates the specific type/cause of URL error. /// Indicates the specific type/cause of a capacity error.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum UrlErrorType { pub enum CapacityErrorType {
/// TLS is used despite not being compiled with the TLS feature enabled. /// Too many headers provided (see [`httparse::Error::TooManyHeaders`]).
TlsFeatureNotEnabled, TooManyHeaders,
/// The URL does not include a host name. /// Received header is too long.
NoHostName, HeaderTooLong,
/// Failed to connect with this URL. /// Message is bigger than the maximum allowed size.
UnableToConnect(String), MessageTooLong {
/// Unsupported URL scheme used (only `ws://` or `wss://` may be used). /// The size of the message.
UnsupportedUrlScheme, size: usize,
/// The URL host name, though included, is empty. /// The maximum allowed message size.
EmptyHostName, max_size: usize,
/// The URL does not include a path/query. },
NoPathOrQuery, /// TCP buffer is full.
TcpBufferFull,
} }
impl fmt::Display for UrlErrorType { impl fmt::Display for CapacityErrorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
UrlErrorType::TlsFeatureNotEnabled => write!(f, "TLS support not compiled in"), CapacityErrorType::TooManyHeaders => write!(f, "Too many headers"),
UrlErrorType::NoHostName => write!(f, "No host name in the URL"), CapacityErrorType::HeaderTooLong => write!(f, "Header too long"),
UrlErrorType::UnableToConnect(uri) => write!(f, "Unable to connect to {}", uri), CapacityErrorType::MessageTooLong { size, max_size } => {
UrlErrorType::UnsupportedUrlScheme => write!(f, "URL scheme not supported"), write!(f, "Message too long: {} > {}", size, max_size)
UrlErrorType::EmptyHostName => write!(f, "URL contains empty host name"), }
UrlErrorType::NoPathOrQuery => write!(f, "No path/query in URL"), CapacityErrorType::TcpBufferFull => write!(f, "Incoming TCP buffer is full"),
} }
} }
} }
@ -302,3 +303,33 @@ impl fmt::Display for ProtocolErrorType {
} }
} }
} }
/// Indicates the specific type/cause of URL error.
#[derive(Debug, PartialEq, Eq)]
pub enum UrlErrorType {
/// TLS is used despite not being compiled with the TLS feature enabled.
TlsFeatureNotEnabled,
/// The URL does not include a host name.
NoHostName,
/// Failed to connect with this URL.
UnableToConnect(String),
/// Unsupported URL scheme used (only `ws://` or `wss://` may be used).
UnsupportedUrlScheme,
/// The URL host name, though included, is empty.
EmptyHostName,
/// The URL does not include a path/query.
NoPathOrQuery,
}
impl fmt::Display for UrlErrorType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UrlErrorType::TlsFeatureNotEnabled => write!(f, "TLS support not compiled in"),
UrlErrorType::NoHostName => write!(f, "No host name in the URL"),
UrlErrorType::UnableToConnect(uri) => write!(f, "Unable to connect to {}", uri),
UrlErrorType::UnsupportedUrlScheme => write!(f, "URL scheme not supported"),
UrlErrorType::EmptyHostName => write!(f, "URL contains empty host name"),
UrlErrorType::NoPathOrQuery => write!(f, "No path/query in URL"),
}
}
}

@ -3,7 +3,7 @@ use log::*;
use std::io::{Cursor, Read, Write}; use std::io::{Cursor, Read, Write};
use crate::{ use crate::{
error::{Error, ProtocolErrorType, Result}, error::{CapacityErrorType, Error, ProtocolErrorType, Result},
util::NonBlockingResult, util::NonBlockingResult,
}; };
use input_buffer::{InputBuffer, MIN_READ}; use input_buffer::{InputBuffer, MIN_READ};
@ -46,7 +46,7 @@ impl<Stream: Read + Write> HandshakeMachine<Stream> {
let read = buf let read = buf
.prepare_reserve(MIN_READ) .prepare_reserve(MIN_READ)
.with_limit(usize::max_value()) // TODO limit size .with_limit(usize::max_value()) // TODO limit size
.map_err(|_| Error::Capacity("Header too long".into()))? .map_err(|_| Error::Capacity(CapacityErrorType::HeaderTooLong))?
.read_from(&mut self.stream) .read_from(&mut self.stream)
.no_block()?; .no_block()?;
match read { match read {

@ -8,7 +8,7 @@ mod mask;
pub use self::frame::{CloseFrame, Frame, FrameHeader}; pub use self::frame::{CloseFrame, Frame, FrameHeader};
use crate::error::{Error, Result}; use crate::error::{CapacityErrorType, Error, Result};
use input_buffer::{InputBuffer, MIN_READ}; use input_buffer::{InputBuffer, MIN_READ};
use log::*; use log::*;
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write}; use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read, Write};
@ -133,9 +133,10 @@ impl FrameCodec {
// Enforce frame size limit early and make sure `length` // Enforce frame size limit early and make sure `length`
// is not too big (fits into `usize`). // is not too big (fits into `usize`).
if length > max_size as u64 { if length > max_size as u64 {
return Err(Error::Capacity( return Err(Error::Capacity(CapacityErrorType::MessageTooLong {
format!("Message length too big: {} > {}", length, max_size).into(), size: length as usize,
)); max_size,
}));
} }
let input_size = cursor.get_ref().len() as u64 - cursor.position(); let input_size = cursor.get_ref().len() as u64 - cursor.position();
@ -155,7 +156,7 @@ impl FrameCodec {
.in_buffer .in_buffer
.prepare_reserve(MIN_READ) .prepare_reserve(MIN_READ)
.with_limit(usize::max_value()) .with_limit(usize::max_value())
.map_err(|_| Error::Capacity("Incoming TCP buffer is full".into()))? .map_err(|_| Error::Capacity(CapacityErrorType::TcpBufferFull))?
.read_from(stream)?; .read_from(stream)?;
if size == 0 { if size == 0 {
trace!("no frame received"); trace!("no frame received");
@ -206,6 +207,8 @@ impl FrameCodec {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::error::{CapacityErrorType, Error};
use super::{Frame, FrameSocket}; use super::{Frame, FrameSocket};
use std::io::Cursor; use std::io::Cursor;
@ -266,9 +269,9 @@ mod tests {
fn size_limit_hit() { fn size_limit_hit() {
let raw = Cursor::new(vec![0x82, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]); let raw = Cursor::new(vec![0x82, 0x07, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
let mut sock = FrameSocket::new(raw); let mut sock = FrameSocket::new(raw);
assert_eq!( match sock.read_frame(Some(5)) {
sock.read_frame(Some(5)).unwrap_err().to_string(), Err(Error::Capacity(CapacityErrorType::MessageTooLong { size: 7, max_size: 5 })) => {}
"Space limit exceeded: Message length too big: 7 > 5" _ => panic!(),
); }
} }
} }

@ -6,7 +6,7 @@ use std::{
}; };
use super::frame::CloseFrame; use super::frame::CloseFrame;
use crate::error::{Error, Result}; use crate::error::{CapacityErrorType, Error, Result};
mod string_collect { mod string_collect {
use utf8::DecodeError; use utf8::DecodeError;
@ -122,9 +122,10 @@ impl IncompleteMessage {
let portion_size = tail.as_ref().len(); let portion_size = tail.as_ref().len();
// Be careful about integer overflows here. // Be careful about integer overflows here.
if my_size > max_size || portion_size > max_size - my_size { if my_size > max_size || portion_size > max_size - my_size {
return Err(Error::Capacity( return Err(Error::Capacity(CapacityErrorType::MessageTooLong {
format!("Message too big: {} + {} > {}", my_size, portion_size, max_size).into(), size: my_size + portion_size,
)); max_size,
}));
} }
match self.collector { match self.collector {

@ -669,6 +669,7 @@ impl<T> CheckConnectionReset for Result<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Message, Role, WebSocket, WebSocketConfig}; use super::{Message, Role, WebSocket, WebSocketConfig};
use crate::error::{CapacityErrorType, Error};
use std::{io, io::Cursor}; use std::{io, io::Cursor};
@ -711,10 +712,11 @@ mod tests {
]); ]);
let limit = WebSocketConfig { max_message_size: Some(10), ..WebSocketConfig::default() }; let limit = WebSocketConfig { max_message_size: Some(10), ..WebSocketConfig::default() };
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit)); let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit));
assert_eq!(
socket.read_message().unwrap_err().to_string(), match socket.read_message() {
"Space limit exceeded: Message too big: 7 + 6 > 10" Err(Error::Capacity(CapacityErrorType::MessageTooLong { size: 13, max_size: 10 })) => {}
); _ => panic!(),
}
} }
#[test] #[test]
@ -722,9 +724,10 @@ mod tests {
let incoming = Cursor::new(vec![0x82, 0x03, 0x01, 0x02, 0x03]); let incoming = Cursor::new(vec![0x82, 0x03, 0x01, 0x02, 0x03]);
let limit = WebSocketConfig { max_message_size: Some(2), ..WebSocketConfig::default() }; let limit = WebSocketConfig { max_message_size: Some(2), ..WebSocketConfig::default() };
let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit)); let mut socket = WebSocket::from_raw_socket(WriteMoc(incoming), Role::Client, Some(limit));
assert_eq!(
socket.read_message().unwrap_err().to_string(), match socket.read_message() {
"Space limit exceeded: Message too big: 0 + 3 > 2" Err(Error::Capacity(CapacityErrorType::MessageTooLong { size: 3, max_size: 2 })) => {}
); _ => panic!(),
}
} }
} }

Loading…
Cancel
Save