ez_ffmpeg/http_input/
error.rs1use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum ManifestKind {
12 Hls,
14 Dash,
16}
17
18impl fmt::Display for ManifestKind {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 Self::Hls => f.write_str("HLS"),
22 Self::Dash => f.write_str("DASH"),
23 }
24 }
25}
26
27#[derive(Debug, Clone, thiserror::Error)]
32#[non_exhaustive]
33pub enum HttpInputError {
34 #[error("invalid HTTP input URL: {reason}")]
36 InvalidUrl {
37 reason: &'static str,
39 },
40
41 #[error(
43 "HTTP input URLs must not contain userinfo; pass credentials with the Authorization header"
44 )]
45 UserinfoForbidden,
46
47 #[error(
49 "Rust HTTP input supports one HTTP resource per input; {kind} manifests \
50 open nested resources and are not supported. Use Input::from(url) with an \
51 FFmpeg build that includes HTTPS/TLS, or resolve the manifest outside \
52 ez-ffmpeg and provide a single media stream"
53 )]
54 ManifestUnsupported {
55 kind: ManifestKind,
57 },
58
59 #[error(
61 "Rust HTTP input rejected a nested resource requested by the demuxer. This \
62 input format is not supported by the single-resource HttpInput API"
63 )]
64 NestedResourceUnsupported,
65
66 #[error("HTTP server returned a non-identity Content-Encoding; media input requires identity")]
68 UnsupportedContentEncoding,
69
70 #[error("HTTP request failed with status {code}")]
72 Status {
73 code: u16,
75 },
76
77 #[error("HTTP authentication required")]
79 AuthenticationRequired,
80
81 #[error("HTTP access denied")]
83 AccessDenied,
84
85 #[error("HTTP proxy authentication required")]
87 ProxyAuthenticationRequired,
88
89 #[error("HTTP resource not found")]
91 NotFound,
92
93 #[error("HTTP request timed out")]
95 Timeout,
96
97 #[error("HTTP body idle timeout")]
99 ReadIdleTimeout,
100
101 #[error("TLS certificate or hostname verification failed")]
103 TlsVerification,
104
105 #[error("HTTP range is not satisfiable")]
107 RangeNotSatisfiable,
108
109 #[error("The HTTP server does not support byte-range requests required to seek this input")]
111 RangeIgnored,
112
113 #[error("HTTP resource changed between requests")]
115 ResourceChanged,
116
117 #[error("too many HTTP redirects")]
119 TooManyRedirects,
120
121 #[error("refusing HTTPS to HTTP redirect")]
123 HttpsDowngrade,
124
125 #[error("HTTP header '{name}' is reserved by HttpInput")]
127 HeaderReserved {
128 name: String,
130 },
131
132 #[error("invalid HTTP header '{name}'")]
134 HeaderInvalid {
135 name: String,
137 },
138
139 #[error("no usable TLS trust anchors; add a custom CA or install system roots")]
141 NoTrustAnchors,
142
143 #[error("invalid TLS certificate PEM")]
145 InvalidCertificate,
146
147 #[error("invalid TLS client identity PEM (need cert chain and unencrypted private key)")]
149 IdentityInvalid,
150
151 #[error("invalid HTTP proxy configuration")]
153 InvalidProxy,
154
155 #[error("HTTP timeouts must be greater than zero")]
157 InvalidTimeout,
158
159 #[error("HTTP transport error: {message}")]
161 Transport {
162 message: String,
164 },
165
166 #[error("HTTP input interrupted")]
168 Interrupted,
169
170 #[error("HTTP response body was truncated before the declared length")]
172 TruncatedBody,
173}
174
175impl HttpInputError {
176 pub(crate) fn to_errno(&self) -> i32 {
177 use ffmpeg_sys_next::{
178 AVERROR, AVERROR_EXIT, EACCES, ECONNRESET, EHOSTUNREACH, EINVAL, EIO, ELOOP, ENOENT,
179 ENOSYS, EPERM, ETIMEDOUT,
180 };
181 match self {
182 Self::Interrupted => AVERROR_EXIT,
183 Self::NotFound => AVERROR(ENOENT),
184 Self::Timeout | Self::ReadIdleTimeout => AVERROR(ETIMEDOUT),
185 Self::AuthenticationRequired
186 | Self::AccessDenied
187 | Self::ProxyAuthenticationRequired
188 | Self::TlsVerification => AVERROR(EACCES),
189 Self::NestedResourceUnsupported => AVERROR(EPERM),
190 Self::TooManyRedirects => AVERROR(ELOOP),
191 Self::InvalidUrl { .. }
192 | Self::UserinfoForbidden
193 | Self::HeaderReserved { .. }
194 | Self::HeaderInvalid { .. }
195 | Self::RangeNotSatisfiable
196 | Self::Status { code: 400 } => AVERROR(EINVAL),
197 Self::RangeIgnored => AVERROR(ENOSYS),
198 Self::Transport { message } if message.contains("reset") => AVERROR(ECONNRESET),
199 Self::Transport { message }
200 if message.contains("dns") || message.contains("resolve") =>
201 {
202 AVERROR(EHOSTUNREACH)
203 }
204 _ => AVERROR(EIO),
205 }
206 }
207}