Skip to main content

igd_next/
errors.rs

1use std::error;
2use std::fmt;
3use std::io;
4use std::str;
5#[cfg(feature = "aio_tokio")]
6use std::string::FromUtf8Error;
7
8#[cfg(feature = "aio_tokio")]
9use tokio::time::error::Elapsed;
10
11
12/// Errors that can occur when sending the request to the gateway.
13#[derive(Debug)]
14pub enum RequestError {
15    /// attohttp error
16    AttoHttpError(attohttpc::Error),
17    /// IO Error
18    IoError(io::Error),
19    /// The response from the gateway could not be parsed.
20    InvalidResponse(String),
21    /// The gateway returned an unhandled error code and description.
22    ErrorCode(u16, String),
23    /// Action is not supported by the gateway
24    UnsupportedAction(String),
25    /// When using the aio feature.
26    #[cfg(feature = "aio_tokio")]
27    HyperError(hyper::Error),
28    /// When using the aio feature.
29    #[cfg(feature = "aio_tokio")]
30    HyperClientError(hyper_util::client::legacy::Error),
31
32    #[cfg(feature = "aio_tokio")]
33    /// http crate error type
34    HttpError(http::Error),
35
36    #[cfg(feature = "aio_tokio")]
37    /// Error parsing HTTP body
38    Utf8Error(FromUtf8Error),
39}
40
41impl From<attohttpc::Error> for RequestError {
42    fn from(err: attohttpc::Error) -> RequestError {
43        RequestError::AttoHttpError(err)
44    }
45}
46
47impl From<io::Error> for RequestError {
48    fn from(err: io::Error) -> RequestError {
49        RequestError::IoError(err)
50    }
51}
52
53#[cfg(feature = "aio_tokio")]
54impl From<http::Error> for RequestError {
55    fn from(err: http::Error) -> RequestError {
56        RequestError::HttpError(err)
57    }
58}
59
60#[cfg(feature = "aio_tokio")]
61impl From<hyper::Error> for RequestError {
62    fn from(err: hyper::Error) -> RequestError {
63        RequestError::HyperError(err)
64    }
65}
66
67#[cfg(feature = "aio_tokio")]
68impl From<FromUtf8Error> for RequestError {
69    fn from(err: FromUtf8Error) -> RequestError {
70        RequestError::Utf8Error(err)
71    }
72}
73
74#[cfg(feature = "aio_tokio")]
75impl From<Elapsed> for RequestError {
76    fn from(_err: Elapsed) -> RequestError {
77        RequestError::IoError(io::Error::new(io::ErrorKind::TimedOut, "timer failed"))
78    }
79}
80
81#[cfg(feature = "aio_tokio")]
82impl From<hyper_util::client::legacy::Error> for RequestError {
83    fn from(err: hyper_util::client::legacy::Error) -> Self {
84        RequestError::HyperClientError(err)
85    }
86}
87
88impl fmt::Display for RequestError {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        match *self {
91            RequestError::AttoHttpError(ref e) => write!(f, "HTTP error {e}"),
92            RequestError::InvalidResponse(ref e) => write!(f, "Invalid response from gateway: {e}"),
93            RequestError::IoError(ref e) => write!(f, "IO error. {e}"),
94            RequestError::ErrorCode(n, ref e) => write!(f, "Gateway response error {n}: {e}"),
95            RequestError::UnsupportedAction(ref e) => write!(f, "Gateway does not support action: {e}"),
96            #[cfg(feature = "aio_tokio")]
97            RequestError::HyperError(ref e) => write!(f, "Hyper Error: {e}"),
98            #[cfg(feature = "aio_tokio")]
99            RequestError::HyperClientError(ref e) => write!(f, "Hyper Client Error: {e}"),
100            #[cfg(feature = "aio_tokio")]
101            RequestError::HttpError(ref e) => write!(f, "Http  Error: {e}"),
102            #[cfg(feature = "aio_tokio")]
103            RequestError::Utf8Error(ref e) => write!(f, "Utf8Error Error: {e}"),
104        }
105    }
106}
107
108impl std::error::Error for RequestError {
109    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
110        match *self {
111            RequestError::AttoHttpError(ref e) => Some(e),
112            RequestError::InvalidResponse(..) => None,
113            RequestError::IoError(ref e) => Some(e),
114            RequestError::ErrorCode(..) => None,
115            RequestError::UnsupportedAction(..) => None,
116            #[cfg(feature = "aio_tokio")]
117            RequestError::HyperError(ref e) => Some(e),
118            #[cfg(feature = "aio_tokio")]
119            RequestError::HyperClientError(ref e) => Some(e),
120            #[cfg(feature = "aio_tokio")]
121            RequestError::HttpError(ref e) => Some(e),
122            #[cfg(feature = "aio_tokio")]
123            RequestError::Utf8Error(ref e) => Some(e),
124        }
125    }
126}
127
128/// Errors returned by `Gateway::get_external_ip`
129#[derive(Debug)]
130pub enum GetExternalIpError {
131    /// The client is not authorized to perform the operation.
132    ActionNotAuthorized,
133    /// Some other error occured performing the request.
134    RequestError(RequestError),
135}
136
137/// Errors returned by `Gateway::remove_port`
138#[derive(Debug)]
139pub enum RemovePortError {
140    /// The client is not authorized to perform the operation.
141    ActionNotAuthorized,
142    /// No such port mapping.
143    NoSuchPortMapping,
144    /// Some other error occured performing the request.
145    RequestError(RequestError),
146}
147
148/// Errors returned by `Gateway::add_any_port` and `Gateway::get_any_address`
149#[derive(Debug)]
150pub enum AddAnyPortError {
151    /// The client is not authorized to perform the operation.
152    ActionNotAuthorized,
153    /// Can not add a mapping for local port 0.
154    InternalPortZeroInvalid,
155    /// The gateway does not have any free ports.
156    NoPortsAvailable,
157    /// The gateway can only map internal ports to same-numbered external ports
158    /// and this external port is in use.
159    ExternalPortInUse,
160    /// The gateway only supports permanent leases (ie. a `lease_duration` of 0).
161    OnlyPermanentLeasesSupported,
162    /// The description was too long for the gateway to handle.
163    DescriptionTooLong,
164    /// Some other error occured performing the request.
165    RequestError(RequestError),
166}
167
168impl From<RequestError> for AddAnyPortError {
169    fn from(err: RequestError) -> AddAnyPortError {
170        AddAnyPortError::RequestError(err)
171    }
172}
173
174impl From<GetExternalIpError> for AddAnyPortError {
175    fn from(err: GetExternalIpError) -> AddAnyPortError {
176        match err {
177            GetExternalIpError::ActionNotAuthorized => AddAnyPortError::ActionNotAuthorized,
178            GetExternalIpError::RequestError(e) => AddAnyPortError::RequestError(e),
179        }
180    }
181}
182
183/// Errors returned by `Gateway::add_port`
184#[derive(Debug)]
185pub enum AddPortError {
186    /// The client is not authorized to perform the operation.
187    ActionNotAuthorized,
188    /// Can not add a mapping for local port 0.
189    InternalPortZeroInvalid,
190    /// External port number 0 (any port) is considered invalid by the gateway.
191    ExternalPortZeroInvalid,
192    /// The requested mapping conflicts with a mapping assigned to another client.
193    PortInUse,
194    /// The gateway requires that the requested internal and external ports are the same.
195    SamePortValuesRequired,
196    /// The gateway only supports permanent leases (ie. a `lease_duration` of 0).
197    OnlyPermanentLeasesSupported,
198    /// The description was too long for the gateway to handle.
199    DescriptionTooLong,
200    /// Some other error occured performing the request.
201    RequestError(RequestError),
202}
203
204impl fmt::Display for GetExternalIpError {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        match *self {
207            GetExternalIpError::ActionNotAuthorized => write!(f, "The client is not authorized to remove the port"),
208            GetExternalIpError::RequestError(ref e) => write!(f, "Request Error. {e}"),
209        }
210    }
211}
212
213impl From<io::Error> for GetExternalIpError {
214    fn from(err: io::Error) -> GetExternalIpError {
215        GetExternalIpError::RequestError(RequestError::from(err))
216    }
217}
218
219impl std::error::Error for GetExternalIpError {
220    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
221        None
222    }
223}
224
225impl fmt::Display for RemovePortError {
226    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227        match *self {
228            RemovePortError::ActionNotAuthorized => write!(f, "The client is not authorized to remove the port"),
229            RemovePortError::NoSuchPortMapping => write!(f, "The port was not mapped"),
230            RemovePortError::RequestError(ref e) => write!(f, "Request error. {e}"),
231        }
232    }
233}
234
235impl std::error::Error for RemovePortError {
236    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
237        None
238    }
239}
240
241impl fmt::Display for AddAnyPortError {
242    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
243        match *self {
244            AddAnyPortError::ActionNotAuthorized => {
245                write!(f, "The client is not authorized to remove the port")
246            }
247            AddAnyPortError::InternalPortZeroInvalid => {
248                write!(f, "Can not add a mapping for local port 0")
249            }
250            AddAnyPortError::NoPortsAvailable => {
251                write!(f, "The gateway does not have any free ports")
252            }
253            AddAnyPortError::OnlyPermanentLeasesSupported => {
254                write!(
255                    f,
256                    "The gateway only supports permanent leases (ie. a `lease_duration` of 0),"
257                )
258            }
259            AddAnyPortError::ExternalPortInUse => {
260                write!(
261                    f,
262                    "The gateway can only map internal ports to same-numbered external ports and this external port is in use."
263                )
264            }
265            AddAnyPortError::DescriptionTooLong => {
266                write!(f, "The description was too long for the gateway to handle.")
267            }
268            AddAnyPortError::RequestError(ref e) => write!(f, "Request error. {e}"),
269        }
270    }
271}
272
273impl std::error::Error for AddAnyPortError {
274    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
275        None
276    }
277}
278
279impl fmt::Display for AddPortError {
280    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281        match *self {
282            AddPortError::ActionNotAuthorized => write!(f, "The client is not authorized to map this port."),
283            AddPortError::InternalPortZeroInvalid => write!(f, "Can not add a mapping for local port 0"),
284            AddPortError::ExternalPortZeroInvalid => write!(
285                f,
286                "External port number 0 (any port) is considered invalid by the gateway."
287            ),
288            AddPortError::PortInUse => write!(
289                f,
290                "The requested mapping conflicts with a mapping assigned to another client."
291            ),
292            AddPortError::SamePortValuesRequired => write!(
293                f,
294                "The gateway requires that the requested internal and external ports are the same."
295            ),
296            AddPortError::OnlyPermanentLeasesSupported => write!(
297                f,
298                "The gateway only supports permanent leases (ie. a `lease_duration` of 0),"
299            ),
300            AddPortError::DescriptionTooLong => write!(f, "The description was too long for the gateway to handle."),
301            AddPortError::RequestError(ref e) => write!(f, "Request error. {e}"),
302        }
303    }
304}
305
306impl std::error::Error for AddPortError {
307    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
308        None
309    }
310}
311
312/// Errors than can occur while trying to find the gateway.
313#[derive(Debug)]
314pub enum SearchError {
315    /// Http/Hyper error
316    HttpError(attohttpc::Error),
317    /// Unable to process the response
318    InvalidResponse,
319    /// Did not receive any valid response within timeout
320    NoResponseWithinTimeout,
321    /// IO Error
322    IoError(io::Error),
323    /// UTF-8 decoding error
324    Utf8Error(str::Utf8Error),
325    /// XML processing error
326    XmlError(xmltree::ParseError),
327    /// When using the aio feature.
328    #[cfg(feature = "aio_tokio")]
329    HyperError(hyper::Error),
330    /// When using the aio feature.
331    #[cfg(feature = "aio_tokio")]
332    HyperClientError(hyper_util::client::legacy::Error),
333    /// Error parsing URI
334    #[cfg(feature = "aio_tokio")]
335    InvalidUri(hyper::http::uri::InvalidUri),
336}
337
338impl From<attohttpc::Error> for SearchError {
339    fn from(err: attohttpc::Error) -> SearchError {
340        SearchError::HttpError(err)
341    }
342}
343
344impl From<io::Error> for SearchError {
345    fn from(err: io::Error) -> SearchError {
346        SearchError::IoError(err)
347    }
348}
349
350impl From<str::Utf8Error> for SearchError {
351    fn from(err: str::Utf8Error) -> SearchError {
352        SearchError::Utf8Error(err)
353    }
354}
355
356impl From<xmltree::ParseError> for SearchError {
357    fn from(err: xmltree::ParseError) -> SearchError {
358        SearchError::XmlError(err)
359    }
360}
361
362#[cfg(feature = "aio_tokio")]
363impl From<hyper::Error> for SearchError {
364    fn from(err: hyper::Error) -> SearchError {
365        SearchError::HyperError(err)
366    }
367}
368
369#[cfg(feature = "aio_tokio")]
370impl From<hyper::http::uri::InvalidUri> for SearchError {
371    fn from(err: hyper::http::uri::InvalidUri) -> SearchError {
372        SearchError::InvalidUri(err)
373    }
374}
375
376#[cfg(feature = "aio_tokio")]
377impl From<Elapsed> for SearchError {
378    fn from(_err: Elapsed) -> SearchError {
379        SearchError::IoError(io::Error::new(io::ErrorKind::TimedOut, "search timed out"))
380    }
381}
382
383#[cfg(feature = "aio_tokio")]
384impl From<hyper_util::client::legacy::Error> for SearchError {
385    fn from(err: hyper_util::client::legacy::Error) -> Self {
386        SearchError::HyperClientError(err)
387    }
388}
389
390impl fmt::Display for SearchError {
391    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392        match *self {
393            SearchError::HttpError(ref e) => write!(f, "HTTP error {e}"),
394            SearchError::InvalidResponse => write!(f, "Invalid response"),
395            SearchError::NoResponseWithinTimeout => write!(f, "No response within timeout"),
396            SearchError::IoError(ref e) => write!(f, "IO error: {e}"),
397            SearchError::Utf8Error(ref e) => write!(f, "UTF-8 error: {e}"),
398            SearchError::XmlError(ref e) => write!(f, "XML error: {e}"),
399            #[cfg(feature = "aio_tokio")]
400            SearchError::HyperError(ref e) => write!(f, "Hyper Error: {e}"),
401            #[cfg(feature = "aio_tokio")]
402            SearchError::HyperClientError(ref e) => write!(f, "Hyper Client Error: {e}"),
403            #[cfg(feature = "aio_tokio")]
404            SearchError::InvalidUri(ref e) => write!(f, "InvalidUri Error: {e}"),
405        }
406    }
407}
408
409impl error::Error for SearchError {
410    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
411        match *self {
412            SearchError::HttpError(ref e) => Some(e),
413            SearchError::InvalidResponse => None,
414            SearchError::NoResponseWithinTimeout => None,
415            SearchError::IoError(ref e) => Some(e),
416            SearchError::Utf8Error(ref e) => Some(e),
417            SearchError::XmlError(ref e) => Some(e),
418            #[cfg(feature = "aio_tokio")]
419            SearchError::HyperError(ref e) => Some(e),
420            #[cfg(feature = "aio_tokio")]
421            SearchError::HyperClientError(ref e) => Some(e),
422            #[cfg(feature = "aio_tokio")]
423            SearchError::InvalidUri(ref e) => Some(e),
424        }
425    }
426}
427
428/// Errors than can occur while getting a port mapping
429#[derive(Debug)]
430pub enum GetGenericPortMappingEntryError {
431    /// The client is not authorized to perform the operation.
432    ActionNotAuthorized,
433    /// The specified array index is out of bounds.
434    SpecifiedArrayIndexInvalid,
435    /// Some other error occured performing the request.
436    RequestError(RequestError),
437}
438
439impl From<RequestError> for GetGenericPortMappingEntryError {
440    fn from(err: RequestError) -> GetGenericPortMappingEntryError {
441        match err {
442            RequestError::ErrorCode(606, _) => GetGenericPortMappingEntryError::ActionNotAuthorized,
443            RequestError::ErrorCode(713, _) => GetGenericPortMappingEntryError::SpecifiedArrayIndexInvalid,
444            other => GetGenericPortMappingEntryError::RequestError(other),
445        }
446    }
447}
448
449impl fmt::Display for GetGenericPortMappingEntryError {
450    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
451        match *self {
452            GetGenericPortMappingEntryError::ActionNotAuthorized => {
453                write!(f, "The client is not authorized to look up port mappings.")
454            }
455            GetGenericPortMappingEntryError::SpecifiedArrayIndexInvalid => {
456                write!(f, "The provided index into the port mapping list is invalid.")
457            }
458            GetGenericPortMappingEntryError::RequestError(ref e) => e.fmt(f),
459        }
460    }
461}
462
463impl std::error::Error for GetGenericPortMappingEntryError {}
464
465/// An error type that emcompasses all possible errors.
466#[derive(Debug)]
467pub enum Error {
468    /// `AddAnyPortError`
469    AddAnyPortError(AddAnyPortError),
470    /// `AddPortError`
471    AddPortError(AddPortError),
472    /// `GetExternalIpError`
473    GetExternalIpError(GetExternalIpError),
474    /// `RemovePortError`
475    RemovePortError(RemovePortError),
476    /// `RequestError`
477    RequestError(RequestError),
478    /// `SearchError`
479    SearchError(SearchError),
480}
481
482/// A result type where the error is `igd::Error`.
483pub type Result<T = ()> = std::result::Result<T, Error>;
484
485impl fmt::Display for Error {
486    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
487        match *self {
488            Error::AddAnyPortError(ref e) => e.fmt(f),
489            Error::AddPortError(ref e) => e.fmt(f),
490            Error::GetExternalIpError(ref e) => e.fmt(f),
491            Error::RemovePortError(ref e) => e.fmt(f),
492            Error::RequestError(ref e) => e.fmt(f),
493            Error::SearchError(ref e) => e.fmt(f),
494        }
495    }
496}
497
498impl error::Error for Error {
499    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
500        match *self {
501            Error::AddAnyPortError(ref e) => Some(e),
502            Error::AddPortError(ref e) => Some(e),
503            Error::GetExternalIpError(ref e) => Some(e),
504            Error::RemovePortError(ref e) => Some(e),
505            Error::RequestError(ref e) => Some(e),
506            Error::SearchError(ref e) => Some(e),
507        }
508    }
509}
510
511impl From<AddAnyPortError> for Error {
512    fn from(err: AddAnyPortError) -> Error {
513        Error::AddAnyPortError(err)
514    }
515}
516
517impl From<AddPortError> for Error {
518    fn from(err: AddPortError) -> Error {
519        Error::AddPortError(err)
520    }
521}
522
523impl From<GetExternalIpError> for Error {
524    fn from(err: GetExternalIpError) -> Error {
525        Error::GetExternalIpError(err)
526    }
527}
528
529impl From<RemovePortError> for Error {
530    fn from(err: RemovePortError) -> Error {
531        Error::RemovePortError(err)
532    }
533}
534
535impl From<RequestError> for Error {
536    fn from(err: RequestError) -> Error {
537        Error::RequestError(err)
538    }
539}
540
541impl From<SearchError> for Error {
542    fn from(err: SearchError) -> Error {
543        Error::SearchError(err)
544    }
545}