junobuild_storage/http/
response.rs

1use crate::constants::{
2    RESPONSE_STATUS_CODE_308, RESPONSE_STATUS_CODE_404, RESPONSE_STATUS_CODE_406,
3    RESPONSE_STATUS_CODE_500,
4};
5use crate::http::headers::build_redirect_headers;
6use crate::http::types::{HeaderField, HttpResponse, StatusCode};
7use crate::http::utils::{
8    build_encodings, build_response_headers, build_response_redirect_headers, streaming_strategy,
9};
10use crate::strategies::StorageStateStrategy;
11use crate::types::config::{StorageConfigIFrame, StorageConfigRedirect};
12use crate::types::store::Asset;
13use junobuild_collections::types::rules::Memory;
14
15pub fn build_asset_response(
16    requested_url: String,
17    requested_headers: Vec<HeaderField>,
18    certificate_version: Option<u16>,
19    asset: Option<(Asset, Memory)>,
20    rewrite_source: Option<String>,
21    status_code: StatusCode,
22    storage_state: &impl StorageStateStrategy,
23) -> HttpResponse {
24    match asset {
25        Some((asset, memory)) => {
26            let encodings = build_encodings(requested_headers);
27
28            for encoding_type in encodings.iter() {
29                if let Some(encoding) = asset.encodings.get(encoding_type) {
30                    let headers = build_response_headers(
31                        &requested_url,
32                        &asset,
33                        encoding,
34                        encoding_type,
35                        &certificate_version,
36                        &rewrite_source,
37                        &storage_state.get_config(),
38                    );
39
40                    let Asset { key, .. } = &asset;
41
42                    match headers {
43                        Ok(headers) => {
44                            let body = storage_state.get_content_chunks(encoding, 0, &memory);
45
46                            // TODO: support for HTTP response 304
47                            // On hold til DFINITY foundation implements:
48                            // "Add etag support to icx-proxy" - https://dfinity.atlassian.net/browse/BOUN-446
49                            // See const STATUS_CODES_TO_CERTIFY: [u16; 2] = [200, 304]; in sdk certified asset canister for implementation reference
50
51                            match body {
52                                Some(body) => {
53                                    return HttpResponse {
54                                        body: body.clone(),
55                                        headers: headers.clone(),
56                                        status_code,
57                                        streaming_strategy: streaming_strategy(
58                                            key,
59                                            encoding,
60                                            encoding_type,
61                                            &headers,
62                                            &memory,
63                                        ),
64                                    }
65                                }
66                                None => {
67                                    error_response(
68                                        RESPONSE_STATUS_CODE_500,
69                                        "No chunks found.".to_string(),
70                                    );
71                                }
72                            }
73                        }
74                        Err(err) => {
75                            return error_response(
76                                RESPONSE_STATUS_CODE_406,
77                                ["Permission denied. Invalid headers. ", err].join(""),
78                            );
79                        }
80                    }
81                }
82            }
83
84            error_response(
85                RESPONSE_STATUS_CODE_500,
86                "No asset encoding found.".to_string(),
87            )
88        }
89        None => error_response(RESPONSE_STATUS_CODE_404, "No asset found.".to_string()),
90    }
91}
92
93pub fn build_redirect_response(
94    requested_url: String,
95    certificate_version: Option<u16>,
96    redirect: &StorageConfigRedirect,
97    iframe: &StorageConfigIFrame,
98) -> HttpResponse {
99    let headers = build_response_redirect_headers(
100        &requested_url,
101        &redirect.location,
102        iframe,
103        &certificate_version,
104    )
105    .unwrap();
106
107    HttpResponse {
108        body: Vec::new().clone(),
109        headers: headers.clone(),
110        status_code: redirect.status_code,
111        streaming_strategy: None,
112    }
113}
114
115pub fn build_redirect_raw_response(
116    redirect_url: &str,
117    iframe: &StorageConfigIFrame,
118) -> HttpResponse {
119    let headers = build_redirect_headers(redirect_url, iframe);
120
121    HttpResponse {
122        body: Vec::new().clone(),
123        headers: headers.clone(),
124        status_code: RESPONSE_STATUS_CODE_308,
125        streaming_strategy: None,
126    }
127}
128
129pub fn error_response(status_code: StatusCode, body: String) -> HttpResponse {
130    HttpResponse {
131        body: body.as_bytes().to_vec(),
132        headers: Vec::new(),
133        status_code,
134        streaming_strategy: None,
135    }
136}