1pub mod asset_graph;
2pub mod combined;
3pub mod conditional;
4pub mod headers;
5pub mod issue_context;
6pub mod lazy_instantiated;
7pub mod query;
8pub mod request;
9pub(crate) mod resolve;
10pub mod route_tree;
11pub mod router;
12pub mod static_assets;
13pub mod wrapping_source;
14
15use std::collections::BTreeSet;
16
17use anyhow::Result;
18use futures::{TryStreamExt, stream::Stream as StreamTrait};
19use serde::{Deserialize, Serialize};
20use turbo_rcstr::RcStr;
21use turbo_tasks::{
22 Completion, NonLocalValue, OperationVc, ResolvedVc, TaskInput, Upcast, ValueDefault, Vc,
23 trace::TraceRawVcs, util::SharedError,
24};
25use turbo_tasks_bytes::{Bytes, Stream, StreamRead};
26use turbo_tasks_fs::FileSystemPath;
27use turbo_tasks_hash::{DeterministicHash, DeterministicHasher, Xxh3Hash64Hasher};
28use turbopack_core::version::{Version, VersionedContent};
29
30use self::{
31 headers::Headers, issue_context::IssueFilePathContentSource, query::Query,
32 route_tree::RouteTree,
33};
34
35#[turbo_tasks::value(shared, operation)]
37pub struct ProxyResult {
38 pub status: u16,
40 pub headers: Vec<(RcStr, RcStr)>,
42 #[turbo_tasks(trace_ignore)]
44 pub body: Body,
45}
46
47#[turbo_tasks::value_impl]
48impl Version for ProxyResult {
49 #[turbo_tasks::function]
50 async fn id(&self) -> Result<Vc<RcStr>> {
51 let mut hash = Xxh3Hash64Hasher::new();
52 hash.write_u16(self.status);
53 for (name, value) in &self.headers {
54 name.deterministic_hash(&mut hash);
55 value.deterministic_hash(&mut hash);
56 }
57 let mut read = self.body.read();
58 while let Some(chunk) = read.try_next().await? {
59 hash.write_bytes(&chunk);
60 }
61 Ok(Vc::cell(hash.finish().to_string().into()))
62 }
63}
64
65#[turbo_tasks::value_trait]
67pub trait GetContentSourceContent {
68 #[turbo_tasks::function]
71 fn vary(self: Vc<Self>) -> Vc<ContentSourceDataVary> {
72 ContentSourceDataVary::default().cell()
73 }
74
75 #[turbo_tasks::function]
77 fn get(self: Vc<Self>, path: RcStr, data: ContentSourceData) -> Vc<ContentSourceContent>;
78}
79
80#[turbo_tasks::value(transparent)]
81pub struct GetContentSourceContents(Vec<ResolvedVc<Box<dyn GetContentSourceContent>>>);
82
83#[turbo_tasks::value]
84pub struct StaticContent {
85 pub content: ResolvedVc<Box<dyn VersionedContent>>,
86 pub status_code: u16,
87 pub headers: ResolvedVc<HeaderList>,
88}
89
90#[turbo_tasks::value(shared)]
91pub enum ContentSourceContent {
94 NotFound,
95 Static(ResolvedVc<StaticContent>),
96 HttpProxy(OperationVc<ProxyResult>),
97 Rewrite(ResolvedVc<Rewrite>),
98 Next,
100}
101
102#[turbo_tasks::value_trait]
106pub trait ContentSourceSideEffect {
107 #[turbo_tasks::function]
108 fn apply(self: Vc<Self>) -> Vc<Completion>;
109}
110
111#[turbo_tasks::value_impl]
112impl GetContentSourceContent for ContentSourceContent {
113 #[turbo_tasks::function]
114 fn get(self: Vc<Self>, _path: RcStr, _data: ContentSourceData) -> Vc<ContentSourceContent> {
115 self
116 }
117}
118
119#[turbo_tasks::value_impl]
120impl ContentSourceContent {
121 #[turbo_tasks::function]
122 pub async fn static_content(
123 content: ResolvedVc<Box<dyn VersionedContent>>,
124 ) -> Result<Vc<ContentSourceContent>> {
125 Ok(ContentSourceContent::Static(
126 StaticContent {
127 content,
128 status_code: 200,
129 headers: HeaderList::empty().to_resolved().await?,
130 }
131 .resolved_cell(),
132 )
133 .cell())
134 }
135
136 #[turbo_tasks::function]
137 pub fn static_with_headers(
138 content: ResolvedVc<Box<dyn VersionedContent>>,
139 status_code: u16,
140 headers: ResolvedVc<HeaderList>,
141 ) -> Vc<ContentSourceContent> {
142 ContentSourceContent::Static(
143 StaticContent {
144 content,
145 status_code,
146 headers,
147 }
148 .resolved_cell(),
149 )
150 .cell()
151 }
152
153 #[turbo_tasks::function]
154 pub fn not_found() -> Vc<ContentSourceContent> {
155 ContentSourceContent::NotFound.cell()
156 }
157}
158
159#[turbo_tasks::value(transparent)]
161pub struct HeaderList(Vec<(RcStr, RcStr)>);
162
163#[turbo_tasks::value_impl]
164impl HeaderList {
165 #[turbo_tasks::function]
166 pub fn new(headers: Vec<(RcStr, RcStr)>) -> Vc<Self> {
167 HeaderList(headers).cell()
168 }
169
170 #[turbo_tasks::function]
171 pub fn empty() -> Vc<Self> {
172 HeaderList(vec![]).cell()
173 }
174}
175
176#[derive(
183 PartialEq,
184 Eq,
185 NonLocalValue,
186 TraceRawVcs,
187 Serialize,
188 Deserialize,
189 Clone,
190 Debug,
191 Hash,
192 Default,
193 TaskInput,
194)]
195pub struct ContentSourceData {
196 pub method: Option<RcStr>,
198 pub url: Option<RcStr>,
200 pub original_url: Option<RcStr>,
203 pub query: Option<Query>,
205 pub raw_query: Option<RcStr>,
207 pub headers: Option<Headers>,
210 pub raw_headers: Option<Vec<(RcStr, RcStr)>>,
213 pub body: Option<ResolvedVc<Body>>,
215 pub cache_buster: u64,
217}
218
219pub type BodyChunk = Result<Bytes, SharedError>;
220
221#[turbo_tasks::value(shared)]
223#[derive(Default, Clone, Debug)]
224pub struct Body {
225 #[turbo_tasks(trace_ignore)]
226 chunks: Stream<BodyChunk>,
227}
228
229impl Body {
230 pub fn new(chunks: Vec<BodyChunk>) -> Self {
232 Self {
233 chunks: Stream::new_closed(chunks),
234 }
235 }
236
237 pub fn read(&self) -> StreamRead<BodyChunk> {
239 self.chunks.read()
240 }
241
242 pub fn from_stream<T: StreamTrait<Item = BodyChunk> + Send + Unpin + 'static>(
243 source: T,
244 ) -> Self {
245 Self {
246 chunks: Stream::from(source),
247 }
248 }
249}
250
251impl<T: Into<Bytes>> From<T> for Body {
252 fn from(value: T) -> Self {
253 Body::new(vec![Ok(value.into())])
254 }
255}
256
257impl ValueDefault for Body {
258 fn value_default() -> Vc<Self> {
259 Body::default().cell()
260 }
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, TraceRawVcs, Hash, Serialize, Deserialize, NonLocalValue)]
265pub enum ContentSourceDataFilter {
266 All,
267 Subset(BTreeSet<String>),
268}
269
270impl ContentSourceDataFilter {
271 pub fn extend(&mut self, other: &ContentSourceDataFilter) {
273 match self {
274 ContentSourceDataFilter::All => {}
275 ContentSourceDataFilter::Subset(set) => match other {
276 ContentSourceDataFilter::All => *self = ContentSourceDataFilter::All,
277 ContentSourceDataFilter::Subset(set2) => set.extend(set2.iter().cloned()),
278 },
279 }
280 }
281
282 pub fn extend_options(
285 this: &mut Option<ContentSourceDataFilter>,
286 other: &Option<ContentSourceDataFilter>,
287 ) {
288 if let Some(this) = this.as_mut() {
289 if let Some(other) = other.as_ref() {
290 this.extend(other);
291 }
292 } else {
293 this.clone_from(other);
294 }
295 }
296
297 pub fn contains(&self, key: &str) -> bool {
299 match self {
300 ContentSourceDataFilter::All => true,
301 ContentSourceDataFilter::Subset(set) => set.contains(key),
302 }
303 }
304
305 pub fn fulfills(
308 this: &Option<ContentSourceDataFilter>,
309 other: &Option<ContentSourceDataFilter>,
310 ) -> bool {
311 match (this, other) {
312 (_, None) => true,
313 (None, Some(_)) => false,
314 (Some(this), Some(other)) => match (this, other) {
315 (ContentSourceDataFilter::All, _) => true,
316 (_, ContentSourceDataFilter::All) => false,
317 (ContentSourceDataFilter::Subset(this), ContentSourceDataFilter::Subset(other)) => {
318 this.is_superset(other)
319 }
320 },
321 }
322 }
323}
324
325#[turbo_tasks::value(shared)]
329#[derive(Debug, Default, Clone, Hash)]
330pub struct ContentSourceDataVary {
331 pub method: bool,
332 pub url: bool,
333 pub original_url: bool,
334 pub query: Option<ContentSourceDataFilter>,
335 pub raw_query: bool,
336 pub headers: Option<ContentSourceDataFilter>,
337 pub raw_headers: bool,
338 pub body: bool,
339 pub cache_buster: bool,
343 pub placeholder_for_future_extensions: (),
344}
345
346impl ContentSourceDataVary {
347 pub fn extend(&mut self, other: &ContentSourceDataVary) {
350 let ContentSourceDataVary {
351 method,
352 url,
353 original_url,
354 query,
355 raw_query,
356 headers,
357 raw_headers,
358 body,
359 cache_buster,
360 placeholder_for_future_extensions: _,
361 } = self;
362 *method = *method || other.method;
363 *url = *url || other.url;
364 *original_url = *original_url || other.original_url;
365 *body = *body || other.body;
366 *cache_buster = *cache_buster || other.cache_buster;
367 *raw_query = *raw_query || other.raw_query;
368 *raw_headers = *raw_headers || other.raw_headers;
369 ContentSourceDataFilter::extend_options(query, &other.query);
370 ContentSourceDataFilter::extend_options(headers, &other.headers);
371 }
372
373 pub fn fulfills(&self, other: &ContentSourceDataVary) -> bool {
376 let ContentSourceDataVary {
378 method,
379 url,
380 original_url,
381 query,
382 raw_query,
383 headers,
384 raw_headers,
385 body,
386 cache_buster,
387 placeholder_for_future_extensions: _,
388 } = self;
389 if other.method && !method {
390 return false;
391 }
392 if other.url && !url {
393 return false;
394 }
395 if other.original_url && !original_url {
396 return false;
397 }
398 if other.body && !body {
399 return false;
400 }
401 if other.raw_query && !raw_query {
402 return false;
403 }
404 if other.raw_headers && !raw_headers {
405 return false;
406 }
407 if other.cache_buster && !cache_buster {
408 return false;
409 }
410 if !ContentSourceDataFilter::fulfills(query, &other.query) {
411 return false;
412 }
413 if !ContentSourceDataFilter::fulfills(headers, &other.headers) {
414 return false;
415 }
416 true
417 }
418}
419
420#[turbo_tasks::value_trait]
422pub trait ContentSource {
423 #[turbo_tasks::function]
424 fn get_routes(self: Vc<Self>) -> Vc<RouteTree>;
425
426 #[turbo_tasks::function]
428 fn get_children(self: Vc<Self>) -> Vc<ContentSources> {
429 ContentSources::empty()
430 }
431}
432
433pub trait ContentSourceExt {
434 fn issue_file_path(
435 self: Vc<Self>,
436 file_path: FileSystemPath,
437 description: RcStr,
438 ) -> Vc<Box<dyn ContentSource>>;
439}
440
441impl<T> ContentSourceExt for T
442where
443 T: Upcast<Box<dyn ContentSource>>,
444{
445 fn issue_file_path(
446 self: Vc<Self>,
447 file_path: FileSystemPath,
448 description: RcStr,
449 ) -> Vc<Box<dyn ContentSource>> {
450 Vc::upcast(IssueFilePathContentSource::new_file_path(
451 file_path,
452 description,
453 Vc::upcast(self),
454 ))
455 }
456}
457
458#[turbo_tasks::value(transparent)]
459pub struct ContentSources(Vec<ResolvedVc<Box<dyn ContentSource>>>);
460
461#[turbo_tasks::value_impl]
462impl ContentSources {
463 #[turbo_tasks::function]
464 pub fn empty() -> Vc<Self> {
465 Vc::cell(Vec::new())
466 }
467}
468
469#[turbo_tasks::value]
472pub struct NoContentSource;
473
474#[turbo_tasks::value_impl]
475impl NoContentSource {
476 #[turbo_tasks::function]
477 pub fn new() -> Vc<Self> {
478 NoContentSource.cell()
479 }
480}
481#[turbo_tasks::value_impl]
482impl ContentSource for NoContentSource {
483 #[turbo_tasks::function]
484 fn get_routes(&self) -> Vc<RouteTree> {
485 RouteTree::empty()
486 }
487}
488
489#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
490pub enum RewriteType {
491 Location {
492 path_and_query: RcStr,
495 },
496 ContentSource {
497 source: OperationVc<Box<dyn ContentSource>>,
500 path_and_query: RcStr,
503 },
504 Sources {
505 sources: OperationVc<GetContentSourceContents>,
508 },
509}
510
511#[turbo_tasks::value(shared)]
514#[derive(Debug)]
515pub struct Rewrite {
516 pub ty: RewriteType,
517
518 pub response_headers: Option<ResolvedVc<HeaderList>>,
521
522 pub request_headers: Option<ResolvedVc<HeaderList>>,
525}
526
527pub struct RewriteBuilder {
528 rewrite: Rewrite,
529}
530
531impl RewriteBuilder {
532 pub fn new(path_and_query: RcStr) -> Self {
533 Self {
534 rewrite: Rewrite {
535 ty: RewriteType::Location { path_and_query },
536 response_headers: None,
537 request_headers: None,
538 },
539 }
540 }
541
542 pub fn new_source_with_path_and_query(
543 source: OperationVc<Box<dyn ContentSource>>,
544 path_and_query: RcStr,
545 ) -> Self {
546 Self {
547 rewrite: Rewrite {
548 ty: RewriteType::ContentSource {
549 source,
550 path_and_query,
551 },
552 response_headers: None,
553 request_headers: None,
554 },
555 }
556 }
557
558 pub fn new_sources(sources: OperationVc<GetContentSourceContents>) -> Self {
559 Self {
560 rewrite: Rewrite {
561 ty: RewriteType::Sources { sources },
562 response_headers: None,
563 request_headers: None,
564 },
565 }
566 }
567
568 pub fn response_headers(mut self, headers: ResolvedVc<HeaderList>) -> Self {
571 self.rewrite.response_headers = Some(headers);
572 self
573 }
574
575 pub fn request_headers(mut self, headers: ResolvedVc<HeaderList>) -> Self {
578 self.rewrite.request_headers = Some(headers);
579 self
580 }
581
582 pub fn build(self) -> Vc<Rewrite> {
583 self.rewrite.cell()
584 }
585}