deploy: 739870593c6d42e4686bf406a0a44e42c640af79

This commit is contained in:
dependabot[bot] 2025-04-04 20:52:48 +00:00
commit 922d7c38b6
59 changed files with 1197 additions and 2106 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,435 +1,217 @@
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/server.rs`."><title>server.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-42caa33d.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.84.1 (e71f9a9a9 2025-01-27)" data-channel="1.84.1" data-search-js="search-92e6798f.js" data-settings-js="settings-0f613d39.js" ><script src="../../static.files/storage-59e33391.js"></script><script defer src="../../static.files/src-script-56102188.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-5f194d8c.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer"></div><main><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">server/</div>server.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap"><div data-nosnippet><pre class="src-line-numbers">
<a href="#1" id="1">1</a>
<a href="#2" id="2">2</a>
<a href="#3" id="3">3</a>
<a href="#4" id="4">4</a>
<a href="#5" id="5">5</a>
<a href="#6" id="6">6</a>
<a href="#7" id="7">7</a>
<a href="#8" id="8">8</a>
<a href="#9" id="9">9</a>
<a href="#10" id="10">10</a>
<a href="#11" id="11">11</a>
<a href="#12" id="12">12</a>
<a href="#13" id="13">13</a>
<a href="#14" id="14">14</a>
<a href="#15" id="15">15</a>
<a href="#16" id="16">16</a>
<a href="#17" id="17">17</a>
<a href="#18" id="18">18</a>
<a href="#19" id="19">19</a>
<a href="#20" id="20">20</a>
<a href="#21" id="21">21</a>
<a href="#22" id="22">22</a>
<a href="#23" id="23">23</a>
<a href="#24" id="24">24</a>
<a href="#25" id="25">25</a>
<a href="#26" id="26">26</a>
<a href="#27" id="27">27</a>
<a href="#28" id="28">28</a>
<a href="#29" id="29">29</a>
<a href="#30" id="30">30</a>
<a href="#31" id="31">31</a>
<a href="#32" id="32">32</a>
<a href="#33" id="33">33</a>
<a href="#34" id="34">34</a>
<a href="#35" id="35">35</a>
<a href="#36" id="36">36</a>
<a href="#37" id="37">37</a>
<a href="#38" id="38">38</a>
<a href="#39" id="39">39</a>
<a href="#40" id="40">40</a>
<a href="#41" id="41">41</a>
<a href="#42" id="42">42</a>
<a href="#43" id="43">43</a>
<a href="#44" id="44">44</a>
<a href="#45" id="45">45</a>
<a href="#46" id="46">46</a>
<a href="#47" id="47">47</a>
<a href="#48" id="48">48</a>
<a href="#49" id="49">49</a>
<a href="#50" id="50">50</a>
<a href="#51" id="51">51</a>
<a href="#52" id="52">52</a>
<a href="#53" id="53">53</a>
<a href="#54" id="54">54</a>
<a href="#55" id="55">55</a>
<a href="#56" id="56">56</a>
<a href="#57" id="57">57</a>
<a href="#58" id="58">58</a>
<a href="#59" id="59">59</a>
<a href="#60" id="60">60</a>
<a href="#61" id="61">61</a>
<a href="#62" id="62">62</a>
<a href="#63" id="63">63</a>
<a href="#64" id="64">64</a>
<a href="#65" id="65">65</a>
<a href="#66" id="66">66</a>
<a href="#67" id="67">67</a>
<a href="#68" id="68">68</a>
<a href="#69" id="69">69</a>
<a href="#70" id="70">70</a>
<a href="#71" id="71">71</a>
<a href="#72" id="72">72</a>
<a href="#73" id="73">73</a>
<a href="#74" id="74">74</a>
<a href="#75" id="75">75</a>
<a href="#76" id="76">76</a>
<a href="#77" id="77">77</a>
<a href="#78" id="78">78</a>
<a href="#79" id="79">79</a>
<a href="#80" id="80">80</a>
<a href="#81" id="81">81</a>
<a href="#82" id="82">82</a>
<a href="#83" id="83">83</a>
<a href="#84" id="84">84</a>
<a href="#85" id="85">85</a>
<a href="#86" id="86">86</a>
<a href="#87" id="87">87</a>
<a href="#88" id="88">88</a>
<a href="#89" id="89">89</a>
<a href="#90" id="90">90</a>
<a href="#91" id="91">91</a>
<a href="#92" id="92">92</a>
<a href="#93" id="93">93</a>
<a href="#94" id="94">94</a>
<a href="#95" id="95">95</a>
<a href="#96" id="96">96</a>
<a href="#97" id="97">97</a>
<a href="#98" id="98">98</a>
<a href="#99" id="99">99</a>
<a href="#100" id="100">100</a>
<a href="#101" id="101">101</a>
<a href="#102" id="102">102</a>
<a href="#103" id="103">103</a>
<a href="#104" id="104">104</a>
<a href="#105" id="105">105</a>
<a href="#106" id="106">106</a>
<a href="#107" id="107">107</a>
<a href="#108" id="108">108</a>
<a href="#109" id="109">109</a>
<a href="#110" id="110">110</a>
<a href="#111" id="111">111</a>
<a href="#112" id="112">112</a>
<a href="#113" id="113">113</a>
<a href="#114" id="114">114</a>
<a href="#115" id="115">115</a>
<a href="#116" id="116">116</a>
<a href="#117" id="117">117</a>
<a href="#118" id="118">118</a>
<a href="#119" id="119">119</a>
<a href="#120" id="120">120</a>
<a href="#121" id="121">121</a>
<a href="#122" id="122">122</a>
<a href="#123" id="123">123</a>
<a href="#124" id="124">124</a>
<a href="#125" id="125">125</a>
<a href="#126" id="126">126</a>
<a href="#127" id="127">127</a>
<a href="#128" id="128">128</a>
<a href="#129" id="129">129</a>
<a href="#130" id="130">130</a>
<a href="#131" id="131">131</a>
<a href="#132" id="132">132</a>
<a href="#133" id="133">133</a>
<a href="#134" id="134">134</a>
<a href="#135" id="135">135</a>
<a href="#136" id="136">136</a>
<a href="#137" id="137">137</a>
<a href="#138" id="138">138</a>
<a href="#139" id="139">139</a>
<a href="#140" id="140">140</a>
<a href="#141" id="141">141</a>
<a href="#142" id="142">142</a>
<a href="#143" id="143">143</a>
<a href="#144" id="144">144</a>
<a href="#145" id="145">145</a>
<a href="#146" id="146">146</a>
<a href="#147" id="147">147</a>
<a href="#148" id="148">148</a>
<a href="#149" id="149">149</a>
<a href="#150" id="150">150</a>
<a href="#151" id="151">151</a>
<a href="#152" id="152">152</a>
<a href="#153" id="153">153</a>
<a href="#154" id="154">154</a>
<a href="#155" id="155">155</a>
<a href="#156" id="156">156</a>
<a href="#157" id="157">157</a>
<a href="#158" id="158">158</a>
<a href="#159" id="159">159</a>
<a href="#160" id="160">160</a>
<a href="#161" id="161">161</a>
<a href="#162" id="162">162</a>
<a href="#163" id="163">163</a>
<a href="#164" id="164">164</a>
<a href="#165" id="165">165</a>
<a href="#166" id="166">166</a>
<a href="#167" id="167">167</a>
<a href="#168" id="168">168</a>
<a href="#169" id="169">169</a>
<a href="#170" id="170">170</a>
<a href="#171" id="171">171</a>
<a href="#172" id="172">172</a>
<a href="#173" id="173">173</a>
<a href="#174" id="174">174</a>
<a href="#175" id="175">175</a>
<a href="#176" id="176">176</a>
<a href="#177" id="177">177</a>
<a href="#178" id="178">178</a>
<a href="#179" id="179">179</a>
<a href="#180" id="180">180</a>
<a href="#181" id="181">181</a>
<a href="#182" id="182">182</a>
<a href="#183" id="183">183</a>
<a href="#184" id="184">184</a>
<a href="#185" id="185">185</a>
<a href="#186" id="186">186</a>
<a href="#187" id="187">187</a>
<a href="#188" id="188">188</a>
<a href="#189" id="189">189</a>
<a href="#190" id="190">190</a>
<a href="#191" id="191">191</a>
<a href="#192" id="192">192</a>
<a href="#193" id="193">193</a>
<a href="#194" id="194">194</a>
<a href="#195" id="195">195</a>
<a href="#196" id="196">196</a>
<a href="#197" id="197">197</a>
<a href="#198" id="198">198</a>
<a href="#199" id="199">199</a>
<a href="#200" id="200">200</a>
<a href="#201" id="201">201</a>
<a href="#202" id="202">202</a>
<a href="#203" id="203">203</a>
<a href="#204" id="204">204</a>
<a href="#205" id="205">205</a>
<a href="#206" id="206">206</a>
<a href="#207" id="207">207</a>
<a href="#208" id="208">208</a>
<a href="#209" id="209">209</a>
<a href="#210" id="210">210</a>
<a href="#211" id="211">211</a>
<a href="#212" id="212">212</a>
<a href="#213" id="213">213</a>
<a href="#214" id="214">214</a>
<a href="#215" id="215">215</a>
<a href="#216" id="216">216</a>
<a href="#217" id="217">217</a></pre></div><pre class="rust"><code><span class="kw">use </span>std::env;
<span class="kw">use </span>std::io::Read;
<span class="kw">use </span>std::sync::Arc;
<span class="kw">use </span>clap::Parser;
<span class="kw">use </span>native_tls::{Identity, TlsAcceptor <span class="kw">as </span>NativeTlsAcceptor};
<span class="kw">use </span>serde_json::json;
<span class="kw">use </span>tokio::io::{AsyncReadExt, AsyncWriteExt};
<span class="kw">use </span>tokio_native_tls::TlsAcceptor;
<span class="kw">use </span>tracing::{debug, error, info};
<span class="kw">use </span>mrc::{get_property, playlist_clear, playlist_next, playlist_prev, quit, seek, set_property};
<span class="attr">#[derive(Parser)]
#[command(author, version, about)]
</span><span class="kw">struct </span>Config {
<span class="doccomment">/// The IP address and port to bind the server to
</span><span class="attr">#[arg(short, long, default_value = <span class="string">"127.0.0.1:8080"</span>)]
</span>bind: String,
<span class="doccomment">/// Path to MPV IPC socket
</span><span class="attr">#[arg(short, long, default_value = <span class="string">"/tmp/mpvsocket"</span>)]
</span>socket: String,
}
<span class="kw">async fn </span>handle_connection(
stream: tokio::net::TcpStream,
acceptor: Arc&lt;TlsAcceptor&gt;,
) -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<span class="kw">let </span><span class="kw-2">mut </span>stream = acceptor.accept(stream).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="kw">let </span><span class="kw-2">mut </span>buffer = <span class="macro">vec!</span>[<span class="number">0</span>; <span class="number">2048</span>];
<span class="kw">let </span>n = stream.read(<span class="kw-2">&amp;mut </span>buffer).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="kw">let </span>request = String::from_utf8_lossy(<span class="kw-2">&amp;</span>buffer[..n]);
<span class="macro">debug!</span>(<span class="string">"Received request:\n{}"</span>, request);
<span class="kw">let </span>headers = request.split(<span class="string">"\r\n"</span>).collect::&lt;Vec&lt;<span class="kw-2">&amp;</span>str&gt;&gt;();
<span class="kw">let </span>token_line = headers
.iter()
.find(|&amp;&amp;line| line.starts_with(<span class="string">"Authorization:"</span>));
<span class="kw">let </span>token = <span class="kw">match </span>token_line {
<span class="prelude-val">Some</span>(line) =&gt; line.split(<span class="string">" "</span>).nth(<span class="number">1</span>).unwrap_or_default(),
<span class="prelude-val">None </span>=&gt; <span class="string">""</span>,
};
<span class="kw">let </span>auth_token = <span class="kw">match </span>env::var(<span class="string">"AUTH_TOKEN"</span>) {
<span class="prelude-val">Ok</span>(token) =&gt; token,
<span class="prelude-val">Err</span>(<span class="kw">_</span>) =&gt; {
<span class="macro">error!</span>(<span class="string">"Authentication token is not set. Connection cannot be accepted."</span>);
stream.write_all(<span class="string">b"Authentication token not set\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="comment">// You know what? I do not care to panic when the authentication token is
// missing in the environment. Start the goddamned server and hell, even
// accept incoming connections. Authenticated requests will be refused
// when the token is incorrect or not set, so we can simply continue here.
</span><span class="kw">return </span><span class="prelude-val">Ok</span>(());
}
};
<span class="kw">if </span>token != auth_token {
stream.write_all(<span class="string">b"Authentication failed\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="kw">return </span><span class="prelude-val">Ok</span>(());
}
<span class="macro">info!</span>(<span class="string">"Client authenticated"</span>);
stream.write_all(<span class="string">b"Authenticated\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="kw">let </span>command = request.split(<span class="string">"\r\n\r\n"</span>).last().unwrap_or(<span class="string">""</span>);
<span class="macro">info!</span>(<span class="string">"Received command: {}"</span>, command);
<span class="kw">let </span>response = <span class="kw">match </span>process_command(command.trim()).<span class="kw">await </span>{
<span class="prelude-val">Ok</span>(response) =&gt; response,
<span class="prelude-val">Err</span>(e) =&gt; {
<span class="macro">error!</span>(<span class="string">"Error processing command: {}"</span>, e);
<span class="macro">format!</span>(<span class="string">"Error: {:?}"</span>, e)
}
};
<span class="kw">let </span>http_response = <span class="macro">format!</span>(
<span class="string">"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}"</span>,
response.len(),
response
);
stream.write_all(http_response.as_bytes()).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(())
}
<span class="kw">async fn </span>process_command(command: <span class="kw-2">&amp;</span>str) -&gt; <span class="prelude-ty">Result</span>&lt;String, String&gt; {
<span class="kw">match </span>command {
<span class="string">"pause" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">true</span>), <span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to pause: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Paused playback\n"</span>.to_string())
}
<span class="string">"play" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Unpausing playback"</span>);
set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to play: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Resumed playback\n"</span>.to_string())
}
<span class="string">"stop" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Stopping playback and quitting MPV"</span>);
quit(<span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to stop: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Stopped playback\n"</span>.to_string())
}
<span class="string">"next" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Skipping to next item in the playlist"</span>);
playlist_next(<span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to next: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Skipped to next item\n"</span>.to_string())
}
<span class="string">"prev" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Skipping to previous item in the playlist"</span>);
playlist_prev(<span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to previous: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Skipped to previous item\n"</span>.to_string())
}
<span class="string">"seek" </span>=&gt; {
<span class="kw">let </span>parts: Vec&lt;<span class="kw-2">&amp;</span>str&gt; = command.split_whitespace().collect();
<span class="kw">if let </span><span class="prelude-val">Some</span>(seconds) = parts.get(<span class="number">1</span>) {
<span class="kw">if let </span><span class="prelude-val">Ok</span>(sec) = seconds.parse::&lt;i32&gt;() {
<span class="macro">info!</span>(<span class="string">"Seeking to {} seconds"</span>, sec);
seek(sec.into(), <span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to seek: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="kw">return </span><span class="prelude-val">Ok</span>(<span class="macro">format!</span>(<span class="string">"Seeking to {} seconds\n"</span>, sec));
}
}
<span class="prelude-val">Err</span>(<span class="string">"Invalid seek command"</span>.to_string())
}
<span class="string">"clear" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Clearing the playlist"</span>);
playlist_clear(<span class="prelude-val">None</span>)
.<span class="kw">await
</span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to clear playlist: {:?}"</span>, e))<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(<span class="string">"Cleared playlist\n"</span>.to_string())
}
<span class="string">"list" </span>=&gt; {
<span class="macro">info!</span>(<span class="string">"Listing playlist items"</span>);
<span class="kw">match </span>get_property(<span class="string">"playlist"</span>, <span class="prelude-val">None</span>).<span class="kw">await </span>{
<span class="prelude-val">Ok</span>(<span class="prelude-val">Some</span>(data)) =&gt; <span class="prelude-val">Ok</span>(<span class="macro">format!</span>(
<span class="string">"Playlist: {}"</span>,
serde_json::to_string_pretty(<span class="kw-2">&amp;</span>data).unwrap()
)),
<span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>) =&gt; <span class="prelude-val">Err</span>(<span class="string">"No playlist data available"</span>.to_string()),
<span class="prelude-val">Err</span>(e) =&gt; <span class="prelude-val">Err</span>(<span class="macro">format!</span>(<span class="string">"Failed to fetch playlist: {:?}"</span>, e)),
}
}
<span class="kw">_ </span>=&gt; <span class="prelude-val">Err</span>(<span class="string">"Unknown command"</span>.to_string()),
}
}
<span class="kw">fn </span>create_tls_acceptor() -&gt; <span class="prelude-ty">Result</span>&lt;TlsAcceptor, Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<span class="kw">let </span>pfx_path = env::var(<span class="string">"TLS_PFX_PATH"</span>)
.map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PFX_PATH not set"</span>))<span class="question-mark">?</span>;
<span class="kw">let </span>password = env::var(<span class="string">"TLS_PASSWORD"</span>)
.map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PASSWORD not set"</span>))<span class="question-mark">?</span>;
<span class="kw">let </span><span class="kw-2">mut </span>file = std::fs::File::open(<span class="kw-2">&amp;</span>pfx_path)<span class="question-mark">?</span>;
<span class="kw">let </span><span class="kw-2">mut </span>identity = <span class="macro">vec!</span>[];
file.read_to_end(<span class="kw-2">&amp;mut </span>identity)<span class="question-mark">?</span>;
<span class="kw">let </span>identity = Identity::from_pkcs12(<span class="kw-2">&amp;</span>identity, <span class="kw-2">&amp;</span>password)<span class="question-mark">?</span>;
<span class="kw">let </span>native_acceptor = NativeTlsAcceptor::new(identity)<span class="question-mark">?</span>;
<span class="prelude-val">Ok</span>(TlsAcceptor::from(native_acceptor))
}
<span class="attr">#[tokio::main]
</span><span class="kw">async fn </span>main() -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
tracing_subscriber::fmt::init();
<span class="kw">let </span>config = Config::parse();
<span class="kw">if </span>!std::path::Path::new(<span class="kw-2">&amp;</span>config.socket).exists() {
<span class="macro">error!</span>(
<span class="string">"Error: MPV socket not found at '{}'. Is MPV running?"</span>,
config.socket
);
}
<span class="macro">info!</span>(<span class="string">"Server is starting..."</span>);
<span class="kw">match </span>create_tls_acceptor() {
<span class="prelude-val">Ok</span>(acceptor) =&gt; {
<span class="kw">let </span>acceptor = Arc::new(acceptor);
<span class="kw">let </span>listener = tokio::net::TcpListener::bind(<span class="kw-2">&amp;</span>config.bind).<span class="kw">await</span><span class="question-mark">?</span>;
<span class="macro">info!</span>(<span class="string">"Server is listening on {}"</span>, config.bind);
<span class="kw">loop </span>{
<span class="kw">let </span>(stream, <span class="kw">_</span>) = listener.accept().<span class="kw">await</span><span class="question-mark">?</span>;
<span class="macro">info!</span>(<span class="string">"New connection accepted."</span>);
<span class="kw">let </span>acceptor = Arc::clone(<span class="kw-2">&amp;</span>acceptor);
tokio::spawn(handle_connection(stream, acceptor));
}
}
<span class="prelude-val">Err</span>(e) =&gt; {
<span class="macro">error!</span>(<span class="string">"Failed to initialize TLS: {}"</span>, e);
<span class="kw">return </span><span class="prelude-val">Err</span>(e);
}
}
}
</code></pre></div></section></main></body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="generator" content="rustdoc"><meta name="description" content="Source of the Rust file `src/server.rs`."><title>server.rs - source</title><script>if(window.location.protocol!=="file:")document.head.insertAdjacentHTML("beforeend","SourceSerif4-Regular-6b053e98.ttf.woff2,FiraSans-Italic-81dc35de.woff2,FiraSans-Regular-0fe48ade.woff2,FiraSans-MediumItalic-ccf7e434.woff2,FiraSans-Medium-e1aa3f0a.woff2,SourceCodePro-Regular-8badfe75.ttf.woff2,SourceCodePro-Semibold-aa29a496.ttf.woff2".split(",").map(f=>`<link rel="preload" as="font" type="font/woff2" crossorigin href="../../static.files/${f}">`).join(""))</script><link rel="stylesheet" href="../../static.files/normalize-9960930a.css"><link rel="stylesheet" href="../../static.files/rustdoc-6c3ea77c.css"><meta name="rustdoc-vars" data-root-path="../../" data-static-root-path="../../static.files/" data-current-crate="server" data-themes="" data-resource-suffix="" data-rustdoc-version="1.86.0 (05f9846f8 2025-03-31)" data-channel="1.86.0" data-search-js="search-581efc7a.js" data-settings-js="settings-6dad6058.js" ><script src="../../static.files/storage-3a5871a4.js"></script><script defer src="../../static.files/src-script-b8d3f215.js"></script><script defer src="../../src-files.js"></script><script defer src="../../static.files/main-4d63596a.js"></script><noscript><link rel="stylesheet" href="../../static.files/noscript-893ab5e7.css"></noscript><link rel="alternate icon" type="image/png" href="../../static.files/favicon-32x32-6580c154.png"><link rel="icon" type="image/svg+xml" href="../../static.files/favicon-044be391.svg"></head><body class="rustdoc src"><!--[if lte IE 11]><div class="warning">This old browser is unsupported and will most likely display funky things.</div><![endif]--><nav class="sidebar"><div class="src-sidebar-title"><h2>Files</h2></div></nav><div class="sidebar-resizer"></div><main><rustdoc-search></rustdoc-search><section id="main-content" class="content"><div class="main-heading"><h1><div class="sub-heading">server/</div>server.rs</h1><rustdoc-toolbar></rustdoc-toolbar></div><div class="example-wrap digits-3"><pre class="rust"><code><a href=#1 id=1 data-nosnippet>1</a><span class="kw">use </span>std::env;
<a href=#2 id=2 data-nosnippet>2</a><span class="kw">use </span>std::io::Read;
<a href=#3 id=3 data-nosnippet>3</a><span class="kw">use </span>std::sync::Arc;
<a href=#4 id=4 data-nosnippet>4</a>
<a href=#5 id=5 data-nosnippet>5</a><span class="kw">use </span>clap::Parser;
<a href=#6 id=6 data-nosnippet>6</a><span class="kw">use </span>native_tls::{Identity, TlsAcceptor <span class="kw">as </span>NativeTlsAcceptor};
<a href=#7 id=7 data-nosnippet>7</a><span class="kw">use </span>serde_json::json;
<a href=#8 id=8 data-nosnippet>8</a><span class="kw">use </span>tokio::io::{AsyncReadExt, AsyncWriteExt};
<a href=#9 id=9 data-nosnippet>9</a><span class="kw">use </span>tokio_native_tls::TlsAcceptor;
<a href=#10 id=10 data-nosnippet>10</a><span class="kw">use </span>tracing::{debug, error, info};
<a href=#11 id=11 data-nosnippet>11</a>
<a href=#12 id=12 data-nosnippet>12</a><span class="kw">use </span>mrc::{get_property, playlist_clear, playlist_next, playlist_prev, quit, seek, set_property};
<a href=#13 id=13 data-nosnippet>13</a>
<a href=#14 id=14 data-nosnippet>14</a><span class="attr">#[derive(Parser)]
<a href=#15 id=15 data-nosnippet>15</a>#[command(author, version, about)]
<a href=#16 id=16 data-nosnippet>16</a></span><span class="kw">struct </span>Config {
<a href=#17 id=17 data-nosnippet>17</a> <span class="doccomment">/// The IP address and port to bind the server to
<a href=#18 id=18 data-nosnippet>18</a> </span><span class="attr">#[arg(short, long, default_value = <span class="string">"127.0.0.1:8080"</span>)]
<a href=#19 id=19 data-nosnippet>19</a> </span>bind: String,
<a href=#20 id=20 data-nosnippet>20</a>
<a href=#21 id=21 data-nosnippet>21</a> <span class="doccomment">/// Path to MPV IPC socket
<a href=#22 id=22 data-nosnippet>22</a> </span><span class="attr">#[arg(short, long, default_value = <span class="string">"/tmp/mpvsocket"</span>)]
<a href=#23 id=23 data-nosnippet>23</a> </span>socket: String,
<a href=#24 id=24 data-nosnippet>24</a>}
<a href=#25 id=25 data-nosnippet>25</a>
<a href=#26 id=26 data-nosnippet>26</a><span class="kw">async fn </span>handle_connection(
<a href=#27 id=27 data-nosnippet>27</a> stream: tokio::net::TcpStream,
<a href=#28 id=28 data-nosnippet>28</a> acceptor: Arc&lt;TlsAcceptor&gt;,
<a href=#29 id=29 data-nosnippet>29</a>) -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#30 id=30 data-nosnippet>30</a> <span class="kw">let </span><span class="kw-2">mut </span>stream = acceptor.accept(stream).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#31 id=31 data-nosnippet>31</a> <span class="kw">let </span><span class="kw-2">mut </span>buffer = <span class="macro">vec!</span>[<span class="number">0</span>; <span class="number">2048</span>];
<a href=#32 id=32 data-nosnippet>32</a>
<a href=#33 id=33 data-nosnippet>33</a> <span class="kw">let </span>n = stream.read(<span class="kw-2">&amp;mut </span>buffer).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#34 id=34 data-nosnippet>34</a> <span class="kw">let </span>request = String::from_utf8_lossy(<span class="kw-2">&amp;</span>buffer[..n]);
<a href=#35 id=35 data-nosnippet>35</a>
<a href=#36 id=36 data-nosnippet>36</a> <span class="macro">debug!</span>(<span class="string">"Received request:\n{}"</span>, request);
<a href=#37 id=37 data-nosnippet>37</a>
<a href=#38 id=38 data-nosnippet>38</a> <span class="kw">let </span>headers = request.split(<span class="string">"\r\n"</span>).collect::&lt;Vec&lt;<span class="kw-2">&amp;</span>str&gt;&gt;();
<a href=#39 id=39 data-nosnippet>39</a> <span class="kw">let </span>token_line = headers
<a href=#40 id=40 data-nosnippet>40</a> .iter()
<a href=#41 id=41 data-nosnippet>41</a> .find(|&amp;&amp;line| line.starts_with(<span class="string">"Authorization:"</span>));
<a href=#42 id=42 data-nosnippet>42</a> <span class="kw">let </span>token = <span class="kw">match </span>token_line {
<a href=#43 id=43 data-nosnippet>43</a> <span class="prelude-val">Some</span>(line) =&gt; line.split(<span class="string">" "</span>).nth(<span class="number">1</span>).unwrap_or_default(),
<a href=#44 id=44 data-nosnippet>44</a> <span class="prelude-val">None </span>=&gt; <span class="string">""</span>,
<a href=#45 id=45 data-nosnippet>45</a> };
<a href=#46 id=46 data-nosnippet>46</a>
<a href=#47 id=47 data-nosnippet>47</a> <span class="kw">let </span>auth_token = <span class="kw">match </span>env::var(<span class="string">"AUTH_TOKEN"</span>) {
<a href=#48 id=48 data-nosnippet>48</a> <span class="prelude-val">Ok</span>(token) =&gt; token,
<a href=#49 id=49 data-nosnippet>49</a> <span class="prelude-val">Err</span>(<span class="kw">_</span>) =&gt; {
<a href=#50 id=50 data-nosnippet>50</a> <span class="macro">error!</span>(<span class="string">"Authentication token is not set. Connection cannot be accepted."</span>);
<a href=#51 id=51 data-nosnippet>51</a> stream.write_all(<span class="string">b"Authentication token not set\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#52 id=52 data-nosnippet>52</a>
<a href=#53 id=53 data-nosnippet>53</a> <span class="comment">// You know what? I do not care to panic when the authentication token is
<a href=#54 id=54 data-nosnippet>54</a> // missing in the environment. Start the goddamned server and hell, even
<a href=#55 id=55 data-nosnippet>55</a> // accept incoming connections. Authenticated requests will be refused
<a href=#56 id=56 data-nosnippet>56</a> // when the token is incorrect or not set, so we can simply continue here.
<a href=#57 id=57 data-nosnippet>57</a> </span><span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#58 id=58 data-nosnippet>58</a> }
<a href=#59 id=59 data-nosnippet>59</a> };
<a href=#60 id=60 data-nosnippet>60</a>
<a href=#61 id=61 data-nosnippet>61</a> <span class="kw">if </span>token != auth_token {
<a href=#62 id=62 data-nosnippet>62</a> stream.write_all(<span class="string">b"Authentication failed\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#63 id=63 data-nosnippet>63</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(());
<a href=#64 id=64 data-nosnippet>64</a> }
<a href=#65 id=65 data-nosnippet>65</a>
<a href=#66 id=66 data-nosnippet>66</a> <span class="macro">info!</span>(<span class="string">"Client authenticated"</span>);
<a href=#67 id=67 data-nosnippet>67</a> stream.write_all(<span class="string">b"Authenticated\n"</span>).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#68 id=68 data-nosnippet>68</a>
<a href=#69 id=69 data-nosnippet>69</a> <span class="kw">let </span>command = request.split(<span class="string">"\r\n\r\n"</span>).last().unwrap_or(<span class="string">""</span>);
<a href=#70 id=70 data-nosnippet>70</a> <span class="macro">info!</span>(<span class="string">"Received command: {}"</span>, command);
<a href=#71 id=71 data-nosnippet>71</a>
<a href=#72 id=72 data-nosnippet>72</a> <span class="kw">let </span>response = <span class="kw">match </span>process_command(command.trim()).<span class="kw">await </span>{
<a href=#73 id=73 data-nosnippet>73</a> <span class="prelude-val">Ok</span>(response) =&gt; response,
<a href=#74 id=74 data-nosnippet>74</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#75 id=75 data-nosnippet>75</a> <span class="macro">error!</span>(<span class="string">"Error processing command: {}"</span>, e);
<a href=#76 id=76 data-nosnippet>76</a> <span class="macro">format!</span>(<span class="string">"Error: {:?}"</span>, e)
<a href=#77 id=77 data-nosnippet>77</a> }
<a href=#78 id=78 data-nosnippet>78</a> };
<a href=#79 id=79 data-nosnippet>79</a>
<a href=#80 id=80 data-nosnippet>80</a> <span class="kw">let </span>http_response = <span class="macro">format!</span>(
<a href=#81 id=81 data-nosnippet>81</a> <span class="string">"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}"</span>,
<a href=#82 id=82 data-nosnippet>82</a> response.len(),
<a href=#83 id=83 data-nosnippet>83</a> response
<a href=#84 id=84 data-nosnippet>84</a> );
<a href=#85 id=85 data-nosnippet>85</a> stream.write_all(http_response.as_bytes()).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#86 id=86 data-nosnippet>86</a>
<a href=#87 id=87 data-nosnippet>87</a> <span class="prelude-val">Ok</span>(())
<a href=#88 id=88 data-nosnippet>88</a>}
<a href=#89 id=89 data-nosnippet>89</a>
<a href=#90 id=90 data-nosnippet>90</a><span class="kw">async fn </span>process_command(command: <span class="kw-2">&amp;</span>str) -&gt; <span class="prelude-ty">Result</span>&lt;String, String&gt; {
<a href=#91 id=91 data-nosnippet>91</a> <span class="kw">match </span>command {
<a href=#92 id=92 data-nosnippet>92</a> <span class="string">"pause" </span>=&gt; {
<a href=#93 id=93 data-nosnippet>93</a> <span class="macro">info!</span>(<span class="string">"Pausing playback"</span>);
<a href=#94 id=94 data-nosnippet>94</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">true</span>), <span class="prelude-val">None</span>)
<a href=#95 id=95 data-nosnippet>95</a> .<span class="kw">await
<a href=#96 id=96 data-nosnippet>96</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to pause: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#97 id=97 data-nosnippet>97</a> <span class="prelude-val">Ok</span>(<span class="string">"Paused playback\n"</span>.to_string())
<a href=#98 id=98 data-nosnippet>98</a> }
<a href=#99 id=99 data-nosnippet>99</a>
<a href=#100 id=100 data-nosnippet>100</a> <span class="string">"play" </span>=&gt; {
<a href=#101 id=101 data-nosnippet>101</a> <span class="macro">info!</span>(<span class="string">"Unpausing playback"</span>);
<a href=#102 id=102 data-nosnippet>102</a> set_property(<span class="string">"pause"</span>, <span class="kw-2">&amp;</span><span class="macro">json!</span>(<span class="bool-val">false</span>), <span class="prelude-val">None</span>)
<a href=#103 id=103 data-nosnippet>103</a> .<span class="kw">await
<a href=#104 id=104 data-nosnippet>104</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to play: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#105 id=105 data-nosnippet>105</a> <span class="prelude-val">Ok</span>(<span class="string">"Resumed playback\n"</span>.to_string())
<a href=#106 id=106 data-nosnippet>106</a> }
<a href=#107 id=107 data-nosnippet>107</a>
<a href=#108 id=108 data-nosnippet>108</a> <span class="string">"stop" </span>=&gt; {
<a href=#109 id=109 data-nosnippet>109</a> <span class="macro">info!</span>(<span class="string">"Stopping playback and quitting MPV"</span>);
<a href=#110 id=110 data-nosnippet>110</a> quit(<span class="prelude-val">None</span>)
<a href=#111 id=111 data-nosnippet>111</a> .<span class="kw">await
<a href=#112 id=112 data-nosnippet>112</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to stop: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#113 id=113 data-nosnippet>113</a> <span class="prelude-val">Ok</span>(<span class="string">"Stopped playback\n"</span>.to_string())
<a href=#114 id=114 data-nosnippet>114</a> }
<a href=#115 id=115 data-nosnippet>115</a>
<a href=#116 id=116 data-nosnippet>116</a> <span class="string">"next" </span>=&gt; {
<a href=#117 id=117 data-nosnippet>117</a> <span class="macro">info!</span>(<span class="string">"Skipping to next item in the playlist"</span>);
<a href=#118 id=118 data-nosnippet>118</a> playlist_next(<span class="prelude-val">None</span>)
<a href=#119 id=119 data-nosnippet>119</a> .<span class="kw">await
<a href=#120 id=120 data-nosnippet>120</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to next: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#121 id=121 data-nosnippet>121</a> <span class="prelude-val">Ok</span>(<span class="string">"Skipped to next item\n"</span>.to_string())
<a href=#122 id=122 data-nosnippet>122</a> }
<a href=#123 id=123 data-nosnippet>123</a>
<a href=#124 id=124 data-nosnippet>124</a> <span class="string">"prev" </span>=&gt; {
<a href=#125 id=125 data-nosnippet>125</a> <span class="macro">info!</span>(<span class="string">"Skipping to previous item in the playlist"</span>);
<a href=#126 id=126 data-nosnippet>126</a> playlist_prev(<span class="prelude-val">None</span>)
<a href=#127 id=127 data-nosnippet>127</a> .<span class="kw">await
<a href=#128 id=128 data-nosnippet>128</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to skip to previous: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#129 id=129 data-nosnippet>129</a> <span class="prelude-val">Ok</span>(<span class="string">"Skipped to previous item\n"</span>.to_string())
<a href=#130 id=130 data-nosnippet>130</a> }
<a href=#131 id=131 data-nosnippet>131</a>
<a href=#132 id=132 data-nosnippet>132</a> <span class="string">"seek" </span>=&gt; {
<a href=#133 id=133 data-nosnippet>133</a> <span class="kw">let </span>parts: Vec&lt;<span class="kw-2">&amp;</span>str&gt; = command.split_whitespace().collect();
<a href=#134 id=134 data-nosnippet>134</a> <span class="kw">if let </span><span class="prelude-val">Some</span>(seconds) = parts.get(<span class="number">1</span>) {
<a href=#135 id=135 data-nosnippet>135</a> <span class="kw">if let </span><span class="prelude-val">Ok</span>(sec) = seconds.parse::&lt;i32&gt;() {
<a href=#136 id=136 data-nosnippet>136</a> <span class="macro">info!</span>(<span class="string">"Seeking to {} seconds"</span>, sec);
<a href=#137 id=137 data-nosnippet>137</a> seek(sec.into(), <span class="prelude-val">None</span>)
<a href=#138 id=138 data-nosnippet>138</a> .<span class="kw">await
<a href=#139 id=139 data-nosnippet>139</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to seek: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#140 id=140 data-nosnippet>140</a> <span class="kw">return </span><span class="prelude-val">Ok</span>(<span class="macro">format!</span>(<span class="string">"Seeking to {} seconds\n"</span>, sec));
<a href=#141 id=141 data-nosnippet>141</a> }
<a href=#142 id=142 data-nosnippet>142</a> }
<a href=#143 id=143 data-nosnippet>143</a> <span class="prelude-val">Err</span>(<span class="string">"Invalid seek command"</span>.to_string())
<a href=#144 id=144 data-nosnippet>144</a> }
<a href=#145 id=145 data-nosnippet>145</a>
<a href=#146 id=146 data-nosnippet>146</a> <span class="string">"clear" </span>=&gt; {
<a href=#147 id=147 data-nosnippet>147</a> <span class="macro">info!</span>(<span class="string">"Clearing the playlist"</span>);
<a href=#148 id=148 data-nosnippet>148</a> playlist_clear(<span class="prelude-val">None</span>)
<a href=#149 id=149 data-nosnippet>149</a> .<span class="kw">await
<a href=#150 id=150 data-nosnippet>150</a> </span>.map_err(|e| <span class="macro">format!</span>(<span class="string">"Failed to clear playlist: {:?}"</span>, e))<span class="question-mark">?</span>;
<a href=#151 id=151 data-nosnippet>151</a> <span class="prelude-val">Ok</span>(<span class="string">"Cleared playlist\n"</span>.to_string())
<a href=#152 id=152 data-nosnippet>152</a> }
<a href=#153 id=153 data-nosnippet>153</a>
<a href=#154 id=154 data-nosnippet>154</a> <span class="string">"list" </span>=&gt; {
<a href=#155 id=155 data-nosnippet>155</a> <span class="macro">info!</span>(<span class="string">"Listing playlist items"</span>);
<a href=#156 id=156 data-nosnippet>156</a> <span class="kw">match </span>get_property(<span class="string">"playlist"</span>, <span class="prelude-val">None</span>).<span class="kw">await </span>{
<a href=#157 id=157 data-nosnippet>157</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">Some</span>(data)) =&gt; <span class="prelude-val">Ok</span>(<span class="macro">format!</span>(
<a href=#158 id=158 data-nosnippet>158</a> <span class="string">"Playlist: {}"</span>,
<a href=#159 id=159 data-nosnippet>159</a> serde_json::to_string_pretty(<span class="kw-2">&amp;</span>data).unwrap()
<a href=#160 id=160 data-nosnippet>160</a> )),
<a href=#161 id=161 data-nosnippet>161</a> <span class="prelude-val">Ok</span>(<span class="prelude-val">None</span>) =&gt; <span class="prelude-val">Err</span>(<span class="string">"No playlist data available"</span>.to_string()),
<a href=#162 id=162 data-nosnippet>162</a> <span class="prelude-val">Err</span>(e) =&gt; <span class="prelude-val">Err</span>(<span class="macro">format!</span>(<span class="string">"Failed to fetch playlist: {:?}"</span>, e)),
<a href=#163 id=163 data-nosnippet>163</a> }
<a href=#164 id=164 data-nosnippet>164</a> }
<a href=#165 id=165 data-nosnippet>165</a> <span class="kw">_ </span>=&gt; <span class="prelude-val">Err</span>(<span class="string">"Unknown command"</span>.to_string()),
<a href=#166 id=166 data-nosnippet>166</a> }
<a href=#167 id=167 data-nosnippet>167</a>}
<a href=#168 id=168 data-nosnippet>168</a>
<a href=#169 id=169 data-nosnippet>169</a><span class="kw">fn </span>create_tls_acceptor() -&gt; <span class="prelude-ty">Result</span>&lt;TlsAcceptor, Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#170 id=170 data-nosnippet>170</a> <span class="kw">let </span>pfx_path = env::var(<span class="string">"TLS_PFX_PATH"</span>)
<a href=#171 id=171 data-nosnippet>171</a> .map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PFX_PATH not set"</span>))<span class="question-mark">?</span>;
<a href=#172 id=172 data-nosnippet>172</a> <span class="kw">let </span>password = env::var(<span class="string">"TLS_PASSWORD"</span>)
<a href=#173 id=173 data-nosnippet>173</a> .map_err(|<span class="kw">_</span>| std::io::Error::new(std::io::ErrorKind::NotFound, <span class="string">"TLS_PASSWORD not set"</span>))<span class="question-mark">?</span>;
<a href=#174 id=174 data-nosnippet>174</a>
<a href=#175 id=175 data-nosnippet>175</a> <span class="kw">let </span><span class="kw-2">mut </span>file = std::fs::File::open(<span class="kw-2">&amp;</span>pfx_path)<span class="question-mark">?</span>;
<a href=#176 id=176 data-nosnippet>176</a> <span class="kw">let </span><span class="kw-2">mut </span>identity = <span class="macro">vec!</span>[];
<a href=#177 id=177 data-nosnippet>177</a> file.read_to_end(<span class="kw-2">&amp;mut </span>identity)<span class="question-mark">?</span>;
<a href=#178 id=178 data-nosnippet>178</a>
<a href=#179 id=179 data-nosnippet>179</a> <span class="kw">let </span>identity = Identity::from_pkcs12(<span class="kw-2">&amp;</span>identity, <span class="kw-2">&amp;</span>password)<span class="question-mark">?</span>;
<a href=#180 id=180 data-nosnippet>180</a> <span class="kw">let </span>native_acceptor = NativeTlsAcceptor::new(identity)<span class="question-mark">?</span>;
<a href=#181 id=181 data-nosnippet>181</a> <span class="prelude-val">Ok</span>(TlsAcceptor::from(native_acceptor))
<a href=#182 id=182 data-nosnippet>182</a>}
<a href=#183 id=183 data-nosnippet>183</a>
<a href=#184 id=184 data-nosnippet>184</a><span class="attr">#[tokio::main]
<a href=#185 id=185 data-nosnippet>185</a></span><span class="kw">async fn </span>main() -&gt; <span class="prelude-ty">Result</span>&lt;(), Box&lt;<span class="kw">dyn </span>std::error::Error + Send + Sync&gt;&gt; {
<a href=#186 id=186 data-nosnippet>186</a> tracing_subscriber::fmt::init();
<a href=#187 id=187 data-nosnippet>187</a> <span class="kw">let </span>config = Config::parse();
<a href=#188 id=188 data-nosnippet>188</a>
<a href=#189 id=189 data-nosnippet>189</a> <span class="kw">if </span>!std::path::Path::new(<span class="kw-2">&amp;</span>config.socket).exists() {
<a href=#190 id=190 data-nosnippet>190</a> <span class="macro">error!</span>(
<a href=#191 id=191 data-nosnippet>191</a> <span class="string">"Error: MPV socket not found at '{}'. Is MPV running?"</span>,
<a href=#192 id=192 data-nosnippet>192</a> config.socket
<a href=#193 id=193 data-nosnippet>193</a> );
<a href=#194 id=194 data-nosnippet>194</a> }
<a href=#195 id=195 data-nosnippet>195</a>
<a href=#196 id=196 data-nosnippet>196</a> <span class="macro">info!</span>(<span class="string">"Server is starting..."</span>);
<a href=#197 id=197 data-nosnippet>197</a> <span class="kw">match </span>create_tls_acceptor() {
<a href=#198 id=198 data-nosnippet>198</a> <span class="prelude-val">Ok</span>(acceptor) =&gt; {
<a href=#199 id=199 data-nosnippet>199</a> <span class="kw">let </span>acceptor = Arc::new(acceptor);
<a href=#200 id=200 data-nosnippet>200</a> <span class="kw">let </span>listener = tokio::net::TcpListener::bind(<span class="kw-2">&amp;</span>config.bind).<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#201 id=201 data-nosnippet>201</a> <span class="macro">info!</span>(<span class="string">"Server is listening on {}"</span>, config.bind);
<a href=#202 id=202 data-nosnippet>202</a>
<a href=#203 id=203 data-nosnippet>203</a> <span class="kw">loop </span>{
<a href=#204 id=204 data-nosnippet>204</a> <span class="kw">let </span>(stream, <span class="kw">_</span>) = listener.accept().<span class="kw">await</span><span class="question-mark">?</span>;
<a href=#205 id=205 data-nosnippet>205</a> <span class="macro">info!</span>(<span class="string">"New connection accepted."</span>);
<a href=#206 id=206 data-nosnippet>206</a>
<a href=#207 id=207 data-nosnippet>207</a> <span class="kw">let </span>acceptor = Arc::clone(<span class="kw-2">&amp;</span>acceptor);
<a href=#208 id=208 data-nosnippet>208</a> tokio::spawn(handle_connection(stream, acceptor));
<a href=#209 id=209 data-nosnippet>209</a> }
<a href=#210 id=210 data-nosnippet>210</a> }
<a href=#211 id=211 data-nosnippet>211</a>
<a href=#212 id=212 data-nosnippet>212</a> <span class="prelude-val">Err</span>(e) =&gt; {
<a href=#213 id=213 data-nosnippet>213</a> <span class="macro">error!</span>(<span class="string">"Failed to initialize TLS: {}"</span>, e);
<a href=#214 id=214 data-nosnippet>214</a> <span class="kw">return </span><span class="prelude-val">Err</span>(e);
<a href=#215 id=215 data-nosnippet>215</a> }
<a href=#216 id=216 data-nosnippet>216</a> }
<a href=#217 id=217 data-nosnippet>217</a>}</code></pre></div></section></main></body></html>