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 = "skip", shared)]
27pub enum ResolveSourceRequestResult {
28 NotFound,
29 Static(ResolvedVc<StaticContent>, ResolvedVc<HeaderList>),
30 HttpProxy(OperationVc<ProxyResult>),
31}
32
33#[turbo_tasks::function(operation, root)]
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, root)]
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, root)]
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, root)]
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, root)]
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()
92 .strongly_consistent()
93 .await?;
94 'routes: loop {
95 let mut sources_op = route_tree_get_operation(route_tree, current_asset_path.clone());
96 'sources: loop {
97 for &get_content in sources_op.read_strongly_consistent().await?.iter() {
98 let content_vary = get_content_source_content_vary_operation(get_content)
99 .read_strongly_consistent()
100 .await?;
101 let content_data =
102 request_to_data(&request_overwrites, &request, &content_vary).await?;
103 let content_op = get_content_source_content_get_operation(
104 get_content,
105 current_asset_path.clone(),
106 content_data,
107 );
108 match &*content_op.read_strongly_consistent().await? {
109 ContentSourceContent::Rewrite(rewrite) => {
110 let rewrite = rewrite.await?;
111 if let Some(headers) = &rewrite.response_headers {
113 response_header_overwrites.extend(headers.await?.iter().cloned());
114 }
115 if let Some(headers) = &rewrite.request_headers {
116 request_overwrites.headers.clear();
117 for (name, value) in &*headers.await? {
118 request_overwrites.headers.insert(
119 HyperHeaderName::try_from(name.as_str())?,
120 HyperHeaderValue::try_from(value.as_str())?,
121 );
122 }
123 }
124 match &rewrite.ty {
126 RewriteType::Location { path_and_query } => {
127 let new_uri = Uri::try_from(path_and_query.as_str())?;
128 let new_asset_path =
129 urlencoding::decode(&new_uri.path()[1..])?.into_owned();
130 request_overwrites.uri = new_uri;
131 current_asset_path = new_asset_path.into();
132 continue 'routes;
133 }
134 RewriteType::ContentSource {
135 source,
136 path_and_query,
137 } => {
138 let new_uri = Uri::try_from(path_and_query.as_str())?;
139 let new_asset_path =
140 urlencoding::decode(&new_uri.path()[1..])?.into_owned();
141 request_overwrites.uri = new_uri;
142 current_asset_path = new_asset_path.into();
143 route_tree = content_source_get_routes_operation(*source)
144 .resolve()
145 .strongly_consistent()
146 .await?;
147 continue 'routes;
148 }
149 RewriteType::Sources {
150 sources: new_sources,
151 } => {
152 sources_op = *new_sources;
153 continue 'sources;
154 }
155 }
156 }
157 ContentSourceContent::NotFound => {
158 return Ok(ResolveSourceRequestResult::NotFound.cell());
159 }
160 ContentSourceContent::Static(static_content) => {
161 return Ok(ResolveSourceRequestResult::Static(
162 *static_content,
163 HeaderList::new(response_header_overwrites)
164 .to_resolved()
165 .await?,
166 )
167 .cell());
168 }
169 ContentSourceContent::HttpProxy(proxy_result) => {
170 return Ok(ResolveSourceRequestResult::HttpProxy(*proxy_result).cell());
171 }
172 ContentSourceContent::Next => continue,
173 }
174 }
175 break;
176 }
177 break;
178 }
179 Ok(ResolveSourceRequestResult::NotFound.cell())
180}
181
182static CACHE_BUSTER: AtomicU64 = AtomicU64::new(0);
183
184async fn request_to_data(
185 request: &SourceRequest,
186 original_request: &SourceRequest,
187 vary: &ContentSourceDataVary,
188) -> Result<ContentSourceData> {
189 let mut data = ContentSourceData::default();
190 if vary.method {
191 data.method = Some(request.method.clone().into());
192 }
193 if vary.url {
194 data.url = Some(request.uri.to_string().into());
195 }
196 if vary.original_url {
197 data.original_url = Some(original_request.uri.to_string().into());
198 }
199 if vary.body {
200 data.body = Some(request.body.clone().resolved_cell());
201 }
202 if vary.raw_query {
203 data.raw_query = Some(request.uri.query().unwrap_or("").into());
204 }
205 if vary.raw_headers {
206 data.raw_headers = Some(
207 request
208 .headers
209 .iter()
210 .map(|(name, value)| {
211 Ok((name.to_string().into(), value.to_str()?.to_string().into()))
212 })
213 .collect::<Result<Vec<_>>>()?,
214 );
215 }
216 if let Some(filter) = vary.query.as_ref() {
217 if let Some(query) = request.uri.query() {
218 let mut query: Query = serde_qs::from_str(query)?;
219 query.filter_with(filter);
220 data.query = Some(query);
221 } else {
222 data.query = Some(Query::default())
223 }
224 }
225 if let Some(filter) = vary.headers.as_ref() {
226 let mut headers = Headers::default();
227 for (header_name, header_value) in request.headers.iter() {
228 if !filter.contains(header_name.as_str()) {
229 continue;
230 }
231 match headers.entry(header_name.to_string()) {
232 Entry::Vacant(e) => {
233 if let Ok(s) = header_value.to_str() {
234 e.insert(HeaderValue::SingleString(s.to_string()));
235 } else {
236 e.insert(HeaderValue::SingleBytes(header_value.as_bytes().to_vec()));
237 }
238 }
239 Entry::Occupied(mut e) => {
240 if let Ok(s) = header_value.to_str() {
241 e.get_mut().extend_with_string(s.to_string());
242 } else {
243 e.get_mut()
244 .extend_with_bytes(header_value.as_bytes().to_vec());
245 }
246 }
247 }
248 }
249 data.headers = Some(headers);
250 }
251 if vary.cache_buster {
252 data.cache_buster = CACHE_BUSTER.fetch_add(1, Ordering::SeqCst);
253 }
254 Ok(data)
255}