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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::future::Future;
use std::pin::Pin;

use data_url::DataUrl;
use headers::HeaderValue;
use net_traits::http_status::HttpStatus;
use net_traits::request::Request;
use net_traits::response::{Response, ResponseBody};
use net_traits::{NetworkError, ResourceFetchTiming};

use crate::fetch::methods::{DoneChannel, FetchContext};
use crate::protocols::ProtocolHandler;

#[derive(Default)]
pub struct DataProtocolHander {}

impl ProtocolHandler for DataProtocolHander {
    fn load(
        &self,
        request: &mut Request,
        _done_chan: &mut DoneChannel,
        _context: &FetchContext,
    ) -> Pin<Box<dyn Future<Output = Response> + Send>> {
        let url = request.current_url();

        assert_eq!(url.scheme(), "data");

        let response = match DataUrl::process(url.clone().as_str()) {
            Ok(data_url) => match data_url.decode_to_vec() {
                Ok((bytes, _fragment_id)) => {
                    let mut response =
                        Response::new(url, ResourceFetchTiming::new(request.timing_type()));
                    *response.body.lock().unwrap() = ResponseBody::Done(bytes);
                    let mime = data_url.mime_type();
                    response.headers.insert(
                        http::header::CONTENT_TYPE,
                        HeaderValue::from_str(&mime.to_string()).unwrap(),
                    );
                    response.status = HttpStatus::default();
                    Some(response)
                },
                Err(_) => None,
            },
            Err(_) => None,
        }
        .unwrap_or_else(|| {
            Response::network_error(NetworkError::Internal("Decoding data URL failed".into()))
        });

        Box::pin(std::future::ready(response))
    }

    fn is_fetchable(&self) -> bool {
        true
    }
}