igd_next/
errors.rs

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