1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::error::Error as StdError;

use crate::*;

/// Specifies the type of error
#[derive(Debug)]
pub enum Kind
{
	/// failure resolving proxy
	ResolveProxy,
	/// failure resolving host
	ResolveHost,
	/// failed connecting to host
	Connect,
	/// HTTP2 framing error
	Http2,
	/// failure reading from the specified Body
	BodyStreamFailure,
	/// outgoing interface could not be used
	InterfaceFailure,
	/// Redirect loop or too many redirects
	TooManyRedirects,
	/// The server sent nothing
	NothingFromServer,
	/// Failed to send on socket
	SendError,
	/// Failed to receive from socket
	RecvError,
	/// failure performing SSL handshake
	SslConnect,
	/// local client certificate problem
	SslLocalCertificate,
	/// The SSL cipher is invalid
	SslCipher,
	/// Remote server's SSL certificate is invalid
	SslCertificate,
	/// The remote server did not close via SSL
	SslShutdownFailed,
	/// unknown CURL error
	Curl(String),
	/// The expected was not the reported size
	PartialFile,
	/// The specified timeout was exceeded
	Timeout,
	/// The contents were not valid UTF-8
	NotUtf8(std::string::FromUtf8Error),
}

#[derive(Debug)]
pub struct Error
{
	kind: Kind,
	url: Option<String>,
}

impl Error
{
	pub fn new(kind: Kind, url: Option<String>)
		-> Error
	{
		Error
		{
			kind,
			url,
		}
	}

	pub fn kind(&self) -> &Kind
	{
		&self.kind
	}
}
impl std::fmt::Display for Error
{
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
	{
		if let Some(ref url) = self.url
		{
			std::fmt::Display::fmt(url, f)?;
			f.write_str(": ")?;
		}
		std::fmt::Display::fmt(self.description(), f)
	}
}

impl StdError for Error
{
	fn description(&self) -> &str
	{
		match &self.kind
		{
			Kind::ResolveProxy => "failure resolving proxy",
			Kind::ResolveHost => "failure resolving host",
			Kind::Connect => "failed connecting to host",
			Kind::Http2 => "HTTP2 framing error",
			Kind::BodyStreamFailure => "failure reading from the specified Body",
			Kind::InterfaceFailure => "outgoing interface could not be used",
			Kind::TooManyRedirects => "Redirect loop or too many redirects",
			Kind::NothingFromServer => "The server sent nothing",
			Kind::SendError => "Failed to send on socket",
			Kind::RecvError => "Failed to receive from socket",
			Kind::SslConnect => "failure performing SSL handshake",
			Kind::SslLocalCertificate => "local client certificate problem",
			Kind::SslCipher => "The SSL cipher is invalid",
			Kind::SslCertificate => "Remote server's SSL certificate is invalid",
			Kind::SslShutdownFailed => "The remote server did not securely close its socket over SSL",
			Kind::Curl(a) => &a,
			Kind::PartialFile => "The expected was not the reported size",
			Kind::Timeout => "The specified timeout was exceeded",
			Kind::NotUtf8(_) => "The contents were not UTF-8",
		}
	}

	fn source(&self) -> Option<&(dyn StdError + 'static)>
	{
		None
	}
}

pub(crate) fn kind_from_curl(c: sys::CURLcode) -> Kind
{
	match c
	{
		sys::CURLE_COULDNT_RESOLVE_PROXY => Kind::ResolveProxy,
		sys::CURLE_COULDNT_RESOLVE_HOST => Kind::ResolveHost,
		sys::CURLE_COULDNT_CONNECT => Kind::Connect,
		sys::CURLE_HTTP2 | sys::CURLE_HTTP2_STREAM => Kind::Http2,
		sys::CURLE_ABORTED_BY_CALLBACK => Kind::BodyStreamFailure,
		sys::CURLE_PARTIAL_FILE => Kind::PartialFile,
		sys::CURLE_SSL_CONNECT_ERROR => Kind::SslConnect,
		sys::CURLE_TOO_MANY_REDIRECTS => Kind::TooManyRedirects,
		sys::CURLE_INTERFACE_FAILED => Kind::InterfaceFailure,
		sys::CURLE_GOT_NOTHING => Kind::NothingFromServer,
		sys::CURLE_SEND_ERROR => Kind::SendError,
		sys::CURLE_RECV_ERROR => Kind::RecvError,
		sys::CURLE_SSL_CERTPROBLEM => Kind::SslLocalCertificate,
		sys::CURLE_SSL_CIPHER => Kind::SslCipher,
		sys::CURLE_SSL_CACERT => Kind::SslCertificate,
		sys::CURLE_SSL_SHUTDOWN_FAILED => Kind::SslShutdownFailed,
		sys::CURLE_OPERATION_TIMEDOUT => Kind::Timeout,
		a => Kind::Curl(format!("curl error {}", a)),
	}
}