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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! BaseURL and URL-reference resolution (ISO/IEC 23009-1 §5.6.5 over RFC 3986
//! §5, as implemented by the `url` crate — SP3). `std` only.
//!
//! A DASH client turns a relative `SegmentTemplate`/`BaseURL` reference into the
//! URL it fetches by joining a `BaseURL` chain onto the MPD's own URL. With no
//! location (an in-memory MPD) the join happens against a fixed synthetic
//! root, `transmux-relative:///`, and [`render`] — the ONE place that knows
//! about it — turns the result back into a reference.
//!
//! # Containment with no base
//!
//! A relative input never resolves to anything but a relative result: RFC 3986
//! §5.2.4 `remove_dot_segments` drops a `..` that would climb above the root,
//! so ../../../etc/passwd is `etc/passwd`, never `/etc/passwd` (a consumer
//! that does `local_dir.join(resolved)` must not see an absolute path from an
//! untrusted MPD). A result is absolute-path (`/x`) only when the MPD itself
//! wrote an absolute-path reference (`/x`) in the chain or as the reference.
//! Percent-encoded dot segments (`%2e%2e`) are dot segments, as in WHATWG.
//! An input using the synthetic scheme itself is rejected (`None`).
//!
//! The `url` crate implements the WHATWG URL algorithm; it differs from strict
//! RFC 3986 in two §5.4 rows (see `tests/it/base_url.rs`).
use Url;
/// Scheme of the synthetic root (never leaves this module's results).
const SYNTHETIC_SCHEME: &str = "transmux-relative";
/// The synthetic root a base-less resolution joins against.
const SYNTHETIC_BASE: &str = "transmux-relative:///";
/// The first control character or whitespace in `s`, if any.
///
/// RFC 3986 §2 restricts a URI to a small US-ASCII subset, and a raw CR, LF, tab
/// or space has no meaning in one. The WHATWG parser behind [`resolve`]
/// silently DELETES tab, CR and LF instead of rejecting them (so
/// `Url::join("a\r\nHost: x")` would yield a clean-looking `aHost:%20x`), which
/// is why this check runs on the raw text BEFORE any join and cannot be left to
/// the parser. Accepting such characters would let a reference smuggle a second
/// request line or header into anything that later assembles an HTTP request
/// from the resolved URL (CRLF injection).
/// Resolve `reference` against `base` (RFC 3986 §5.2 as the `url` crate
/// implements it). `base` is the MPD's own URL (a source URL, or
/// `Url::from_file_path`); `None` for an in-memory MPD, in which case a result
/// that stays relative is returned relative. `None` is returned when the
/// reference contains a control character or whitespace
/// ([`first_forbidden_char`]) or does not parse.
/// Resolve a `BaseURL` chain (outermost first) followed by a segment
/// reference. Each entry is joined onto the running result, so a relative
/// entry composes with the ones above it and an absolute one resets the chain.
/// Empty and whitespace-only entries are skipped — ISO/IEC 23009-1 §5.6.5 makes
/// several `BaseURL`s alternates for the same level, so an empty one
/// contributes nothing. `None` for a control character or whitespace in any
/// entry or the reference ([`first_forbidden_char`]), for an input that names
/// the synthetic scheme, or for a join error. With `base == None` a relative
/// outcome stays relative and contained (see the module docs).
/// `true` for `/x` (but not `//host/x`): an absolute-path reference.
/// `true` when `r` starts with the synthetic scheme (`transmux-relative:`),
/// case-insensitively.
/// The single place the synthetic root is stripped. A URL that is not under it
/// is its own text. A synthetic URL with an authority (`//host/x`) keeps its
/// `//host/x` form. Otherwise the path is returned as a reference: relative
/// (the leading `/` of the synthetic root removed, so nothing can climb out of
/// it) unless `absolute_path` says the MPD wrote an absolute-path reference.