turbopack_dev_server/source/
resolve.rs1use std::{
2 collections::btree_map::Entry,
3 sync::atomic::{AtomicU64, Ordering},
4};
5
6use anyhow::Result;
7use hyper::{
8 Uri,
9 header::{HeaderName as HyperHeaderName, HeaderValue as HyperHeaderValue},
10};
11use turbo_rcstr::RcStr;
12use turbo_tasks::{OperationVc, ResolvedVc, TransientInstance, Vc};
13
14use crate::source::{
15 ContentSource, ContentSourceContent, ContentSourceData, ContentSourceDataVary,
16 GetContentSourceContent, GetContentSourceContents, HeaderList, ProxyResult, RewriteType,
17 StaticContent,
18 headers::{HeaderValue, Headers},
19 query::Query,
20 request::SourceRequest,
21 route_tree::RouteTree,
22};
23
24#[turbo_tasks::value(serialization = "none", shared)]
27pub enum ResolveSourceRequestResult {
28 NotFound,
29 Static(ResolvedVc<StaticContent>, ResolvedVc<HeaderList>),
30 HttpProxy(OperationVc<ProxyResult>),
31}
32
33#[turbo_tasks::function(operation)]
34fn content_source_get_routes_operation(
35 source: OperationVc<Box<dyn ContentSource>>,
36) -> Vc<RouteTree> {
37 source.connect().get_routes()
38}
39
40#[turbo_tasks::function(operation)]
41fn route_tree_get_operation(
42 route_tree: ResolvedVc<RouteTree>,
43 asset_path: RcStr,
44) -> Vc<GetContentSourceContents> {
45 route_tree.get(asset_path)
46}
47
48#[turbo_tasks::function(operation)]
49fn get_content_source_content_vary_operation(
50 get_content: ResolvedVc<Box<dyn GetContentSourceContent>>,
51) -> Vc<ContentSourceDataVary> {
52 get_content.vary()
53}
54
55#[turbo_tasks::function(operation)]
56fn get_content_source_content_get_operation(
57 get_content: ResolvedVc<Box<dyn GetContentSourceContent>>,
58 path: RcStr,
59 data: ContentSourceData,
60) -> Vc<ContentSourceContent> {
61 get_content.get(path, data)
62}
63
64#[turbo_tasks::function(operation)]
81pub async fn resolve_source_request(
82 source: OperationVc<Box<dyn ContentSource>>,
83 request: TransientInstance<SourceRequest>,
84) -> Result<Vc<ResolveSourceRequestResult>> {
85 let original_path = request.uri.path().to_string();
86 let mut current_asset_path: RcStr = urlencoding::decode(&original_path[1..])?.into();
88 let mut request_overwrites = (*request).clone();
89 let mut response_header_overwrites = Vec::new();
90 let mut route_tree = content_source_get_routes_operation(source)
91 .resolve_strongly_consistent()
92 .await?;
93 'routes: loop {
94 let mut sources_op = route_tree_get_operation(route_tree, current_asset_path.clone());
95 'sources: loop {
96 for &get_content in sources_op.read_strongly_consistent().await?.iter() {
97 let content_vary = get_content_source_content_vary_operation(get_content)
98 .read_strongly_consistent()
99 .await?;
100 let content_data =
101 request_to_data(&request_overwrites, &request, &content_vary).await?;
102 let content_op = get_content_source_content_get_operation(
103 get_content,
104 current_asset_path.clone(),
105 content_data,
106 );
107 match &*content_op.read_strongly_consistent().await? {
108 ContentSourceContent::Rewrite(rewrite) => {
109 let rewrite = rewrite.await?;
110 if let Some(headers) = &rewrite.response_headers {
112 response_header_overwrites.extend(headers.await?.iter().cloned());
113 }
114 if let Some(headers) = &rewrite.request_headers {
115 request_overwrites.headers.clear();
116 for (name, value) in &*headers.await? {
117 request_overwrites.headers.insert(
118 HyperHeaderName::try_from(name.as_str())?,
119 HyperHeaderValue::try_from(value.as_str())?,
120 );
121 }
122 }
123 match &rewrite.ty {
125 RewriteType::Location { path_and_query } => {
126 let new_uri = Uri::try_from(path_and_query.as_str())?;
127 let new_asset_path =
128 urlencoding::decode(&new_uri.path()[1..])?.into_owned();
129 request_overwrites.uri = new_uri;
130 current_asset_path = new_asset_path.into();
131 continue 'routes;
132 }
133 RewriteType::ContentSource {
134 source,
135 path_and_query,
136 } => {
137 let new_uri = Uri::try_from(path_and_query.as_str())?;
138 let new_asset_path =
139 urlencoding::decode(&new_uri.path()[1..])?.into_owned();
140 request_overwrites.uri = new_uri;
141 current_asset_path = new_asset_path.into();
142 route_tree = content_source_get_routes_operation(*source)
143 .resolve_strongly_consistent()
144 .await?;
145 continue 'routes;
146 }
147 RewriteType::Sources {
148 sources: new_sources,
149 } => {
150 sources_op = *new_sources;
151 continue 'sources;
152 }
153 }
154 }
155 ContentSourceContent::NotFound => {
156 return Ok(ResolveSourceRequestResult::NotFound.cell());
157 }
158 ContentSourceContent::Static(static_content) => {
159 return Ok(ResolveSourceRequestResult::Static(
160 *static_content,
161 HeaderList::new(response_header_overwrites)
162 .to_resolved()
163 .await?,
164 )
165 .cell());
166 }
167 ContentSourceContent::HttpProxy(proxy_result) => {
168 return Ok(ResolveSourceRequestResult::HttpProxy(*proxy_result).cell());
169 }
170 ContentSourceContent::Next => continue,
171 }
172 }
173 break;
174 }
175 break;
176 }
177 Ok(ResolveSourceRequestResult::NotFound.cell())
178}
179
180static CACHE_BUSTER: AtomicU64 = AtomicU64::new(0);
181
182async fn request_to_data(
183 request: &SourceRequest,
184 original_request: &SourceRequest,
185 vary: &ContentSourceDataVary,
186) -> Result<ContentSourceData> {
187 let mut data = ContentSourceData::default();
188 if vary.method {
189 data.method = Some(request.method.clone().into());
190 }
191 if vary.url {
192 data.url = Some(request.uri.to_string().into());
193 }
194 if vary.original_url {
195 data.original_url = Some(original_request.uri.to_string().into());
196 }
197 if vary.body {
198 data.body = Some(request.body.clone().resolved_cell());
199 }
200 if vary.raw_query {
201 data.raw_query = Some(request.uri.query().unwrap_or("").into());
202 }
203 if vary.raw_headers {
204 data.raw_headers = Some(
205 request
206 .headers
207 .iter()
208 .map(|(name, value)| {
209 Ok((name.to_string().into(), value.to_str()?.to_string().into()))
210 })
211 .collect::<Result<Vec<_>>>()?,
212 );
213 }
214 if let Some(filter) = vary.query.as_ref() {
215 if let Some(query) = request.uri.query() {
216 let mut query: Query = serde_qs::from_str(query)?;
217 query.filter_with(filter);
218 data.query = Some(query);
219 } else {
220 data.query = Some(Query::default())
221 }
222 }
223 if let Some(filter) = vary.headers.as_ref() {
224 let mut headers = Headers::default();
225 for (header_name, header_value) in request.headers.iter() {
226 if !filter.contains(header_name.as_str()) {
227 continue;
228 }
229 match headers.entry(header_name.to_string()) {
230 Entry::Vacant(e) => {
231 if let Ok(s) = header_value.to_str() {
232 e.insert(HeaderValue::SingleString(s.to_string()));
233 } else {
234 e.insert(HeaderValue::SingleBytes(header_value.as_bytes().to_vec()));
235 }
236 }
237 Entry::Occupied(mut e) => {
238 if let Ok(s) = header_value.to_str() {
239 e.get_mut().extend_with_string(s.to_string());
240 } else {
241 e.get_mut()
242 .extend_with_bytes(header_value.as_bytes().to_vec());
243 }
244 }
245 }
246 }
247 data.headers = Some(headers);
248 }
249 if vary.cache_buster {
250 data.cache_buster = CACHE_BUSTER.fetch_add(1, Ordering::SeqCst);
251 }
252 Ok(data)
253}