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
use futures::future::Future;
use hyper::service::service_fn_ok;
use hyper::Body;
use hyper::Response;
use hyper::Server;
use rspotify::spotify::oauth2::SpotifyOAuth;
use rspotify::spotify::oauth2::TokenInfo;
use rspotify::spotify::util::generate_random_string;
use std::sync::mpsc::channel;
use std::thread;

pub fn get_token_hyper(spotify_oauth: &mut SpotifyOAuth) -> Option<TokenInfo> {
    match spotify_oauth.get_cached_token() {
        Some(token_info) => Some(token_info),
        None => {
            let (tx, rx) = channel::<TokenInfo>();

            let state = generate_random_string(16);
            let auth_url = spotify_oauth.get_authorize_url(Some(&state), None);
            let (_tx_stop, rx_stop) = futures::sync::oneshot::channel::<()>();

            let spotify_oauth = spotify_oauth.clone();
            thread::spawn(move || {
                let tx = tx.clone();

                webbrowser::open(&auth_url).unwrap();

                let addr = ([127, 0, 0, 1], 8888).into();

                let new_svc = move || {
                    let spotify_oauth = spotify_oauth.clone();
                    let tx = tx.clone();

                    service_fn_ok(move |req| {
                        let tx = tx.clone();

                        let mut uri = req.uri().to_string();
                        let path = req.uri().path();

                        let token = spotify_oauth
                            .parse_response_code(&mut uri)
                            .and_then(|code| spotify_oauth.get_access_token(&code));

                        let success = "<html><head></head><body><script>window.close();</script></body></html>";
                        let failure = "<html><head></head><body>Invalid path</body></html>";

                        let resp = match token {
                            Some(token) => {
                                if path == "/callback" {
                                    tx.send(token).unwrap();
                                    success
                                } else {
                                    failure
                                }
                            }
                            _ => failure,
                        };

                        Response::new(Body::from(resp))
                    })
                };

                let server = Server::bind(&addr)
                    .serve(new_svc)
                    .with_graceful_shutdown(rx_stop)
                    .map_err(|e| eprintln!("server error: {}", e));

                // Run this server for... forever!
                hyper::rt::run(server);
            });

            rx.recv().ok()
        }
    }
}