Skip to content

Commit f23023d

Browse files
authored
Use the proxy request identifier for storing the VA cookie in proxy sites (#3091)
1 parent da74077 commit f23023d

File tree

9 files changed

+149
-10
lines changed

9 files changed

+149
-10
lines changed

.github/workflows/deploy-preview.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
deploy-v1-cloudflare:
1111
name: Deploy v1 to Cloudflare Pages
1212
runs-on: ubuntu-latest
13-
environment:
13+
environment:
1414
name: ${{ github.ref == 'refs/heads/main' && '1c-production' || '1c-preview' }}
1515
url: ${{ steps.deploy.outputs.deployment-url }}
1616
permissions:
@@ -57,7 +57,7 @@ jobs:
5757
deploy-v2-vercel:
5858
name: Deploy v2 to Vercel (preview)
5959
runs-on: ubuntu-latest
60-
environment:
60+
environment:
6161
name: 2v-preview
6262
url: ${{ steps.deploy.outputs.deployment-url }}
6363
outputs:
@@ -78,7 +78,7 @@ jobs:
7878
deploy-v2-cloudflare:
7979
name: Deploy v2 to Cloudflare Worker (preview)
8080
runs-on: ubuntu-latest
81-
environment:
81+
environment:
8282
name: 2c-preview
8383
url: ${{ steps.deploy.outputs.deployment-url }}
8484
outputs:

packages/gitbook-v2/src/lib/data/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export * from './pages';
44
export * from './urls';
55
export * from './errors';
66
export * from './lookup';
7+
export * from './proxy';
8+
export * from './visitor';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, it } from 'bun:test';
2+
import { getProxyRequestIdentifier, isProxyRequest } from './proxy';
3+
4+
describe('isProxyRequest', () => {
5+
it('should return true for proxy requests', () => {
6+
const proxyRequestURL = new URL('https://proxy.gitbook.site/sites/site_foo/hello/world');
7+
expect(isProxyRequest(proxyRequestURL)).toBe(true);
8+
});
9+
10+
it('should return false for non-proxy requests', () => {
11+
const nonProxyRequestURL = new URL('https://example.com/docs/foo/hello/world');
12+
expect(isProxyRequest(nonProxyRequestURL)).toBe(false);
13+
});
14+
});
15+
16+
describe('getProxyRequestIdentifier', () => {
17+
it('should return the correct identifier for proxy requests', () => {
18+
const proxyRequestURL = new URL('https://proxy.gitbook.site/sites/site_foo/hello/world');
19+
expect(getProxyRequestIdentifier(proxyRequestURL)).toBe('sites/site_foo');
20+
});
21+
});
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Check if the request to the site was through a proxy.
3+
*/
4+
export function isProxyRequest(requestURL: URL): boolean {
5+
return (
6+
requestURL.host === 'proxy.gitbook.site' || requestURL.host === 'proxy.gitbook-staging.site'
7+
);
8+
}
9+
10+
export function getProxyRequestIdentifier(requestURL: URL): string {
11+
// For proxy requests, we extract the site ID from the pathname
12+
// e.g. https://proxy.gitbook.site/site/siteId/...
13+
const pathname = requestURL.pathname.slice(1).split('/');
14+
return pathname.slice(0, 2).join('/');
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from 'bun:test';
2+
import { getVisitorAuthBasePath } from './visitor';
3+
4+
describe('getVisitorAuthBasePath', () => {
5+
it('should return the correct base path for proxy requests', () => {
6+
expect(
7+
getVisitorAuthBasePath(
8+
new URL('https://proxy.gitbook.site/sites/site_foo/hello/world'),
9+
{
10+
site: 'site_foo',
11+
siteSpace: 'sitesp_foo',
12+
basePath: '/foo',
13+
siteBasePath: '/foo',
14+
organization: 'org_foo',
15+
space: 'space_foo',
16+
pathname: '/hello/world',
17+
complete: false,
18+
apiToken: 'api_token_foo',
19+
canonicalUrl: 'https://example.com/docs/foo/hello/world',
20+
}
21+
)
22+
).toBe('/sites/site_foo/');
23+
});
24+
25+
it('should return the correct base path for non-proxy requests', () => {
26+
expect(
27+
getVisitorAuthBasePath(new URL('https://example.com/docs/foo/hello/world'), {
28+
site: 'site_foo',
29+
siteSpace: 'sitesp_foo',
30+
basePath: '/foo/',
31+
siteBasePath: '/foo/',
32+
organization: 'org_foo',
33+
space: 'space_foo',
34+
pathname: '/hello/world',
35+
complete: false,
36+
apiToken: 'api_token_foo',
37+
canonicalUrl: 'https://example.com/docs/foo/hello/world',
38+
})
39+
).toBe('/foo/');
40+
});
41+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { withLeadingSlash, withTrailingSlash } from '@/lib/paths';
2+
import type { PublishedSiteContent } from '@gitbook/api';
3+
import { getProxyRequestIdentifier, isProxyRequest } from './proxy';
4+
5+
/**
6+
* Get the appropriate base path for the visitor authentication cookie.
7+
*/
8+
export function getVisitorAuthBasePath(
9+
siteRequestURL: URL,
10+
siteURLData: PublishedSiteContent
11+
): string {
12+
// The siteRequestURL for proxy requests is of the form `https://proxy.gitbook.com/site/siteId/...`
13+
// In such cases, we should not use the resolved siteBasePath for the cookie because for subsequent requests
14+
// we will not have the siteBasePath in the request URL in order to retrieve the cookie. So we use the
15+
// proxy identifier instead.
16+
return isProxyRequest(siteRequestURL)
17+
? withLeadingSlash(withTrailingSlash(getProxyRequestIdentifier(siteRequestURL)))
18+
: siteURLData.siteBasePath;
19+
}

packages/gitbook-v2/src/lib/images/getImageResizingContextId.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1+
import { getProxyRequestIdentifier, isProxyRequest } from '../data';
2+
13
/**
24
* Get the site identifier to use for image resizing for an incoming request.
35
* This identifier can be obtained before resolving the request URL.
46
*/
57
export function getImageResizingContextId(url: URL): string {
6-
if (url.host === 'proxy.gitbook.site' || url.host === 'proxy.gitbook-staging.site') {
7-
// For proxy requests, we extract the site ID from the pathname
8-
// e.g. https://proxy.gitbook.site/site/siteId/...
9-
const pathname = url.pathname.slice(1).split('/');
10-
return pathname.slice(0, 2).join('/');
8+
if (isProxyRequest(url)) {
9+
return getProxyRequestIdentifier(url);
1110
}
1211

1312
return url.host;

packages/gitbook-v2/src/middleware.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import { serveResizedImage } from '@/routes/image';
1717
import {
1818
DataFetcherError,
1919
getPublishedContentByURL,
20+
getVisitorAuthBasePath,
2021
normalizeURL,
2122
throwIfDataError,
2223
} from '@v2/lib/data';
2324
import { isGitBookAssetsHostURL, isGitBookHostURL } from '@v2/lib/env';
2425
import { getImageResizingContextId } from '@v2/lib/images';
2526
import { MiddlewareHeaders } from '@v2/lib/middleware';
2627
import type { SiteURLData } from './lib/context';
27-
2828
export const config = {
2929
matcher: [
3030
'/((?!_next/static|_next/image|~gitbook/static|~gitbook/revalidate|~gitbook/monitoring|~scalar/proxy).*)',
@@ -137,7 +137,12 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
137137
return NextResponse.redirect(siteURLData.redirect);
138138
}
139139

140-
cookies.push(...getResponseCookiesForVisitorAuth(siteURLData.siteBasePath, visitorToken));
140+
cookies.push(
141+
...getResponseCookiesForVisitorAuth(
142+
getVisitorAuthBasePath(siteRequestURL, siteURLData),
143+
visitorToken
144+
)
145+
);
141146

142147
// We use the host/origin from the canonical URL to ensure the links are
143148
// correctly generated when the site is proxied. e.g. https://proxy.gitbook.com/site/siteId/...

packages/gitbook/e2e/internal.spec.ts

+37
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,43 @@ const testCases: TestsCase[] = [
419419
},
420420
],
421421
},
422+
{
423+
name: 'Site subdirectory (proxy)',
424+
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
425+
contentBaseURL: 'https://nextjs-gbo-proxy.vercel.app/documentation/',
426+
tests: [
427+
{
428+
name: 'Main',
429+
url: '',
430+
fullPage: true,
431+
},
432+
],
433+
},
434+
{
435+
name: 'Site subdirectory (proxy) with VA',
436+
skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel',
437+
contentBaseURL: 'https://nextjs-gbo-proxy-va.vercel.app/va/docs/',
438+
tests: [
439+
{
440+
name: 'Main',
441+
url: () => {
442+
const privateKey =
443+
'rqSfA6x7eAKx1qDRCDq9aCXwivpUvQ8YkXeDdFvCCUa9QchIcM7pF1iJ4o7AGOU49spmOWjKoIPtX0pVUVQ81w==';
444+
const token = jwt.sign(
445+
{
446+
name: 'gitbook-open-tests',
447+
},
448+
privateKey,
449+
{
450+
expiresIn: '24h',
451+
}
452+
);
453+
return `?jwt_token=${token}`;
454+
},
455+
fullPage: true,
456+
},
457+
],
458+
},
422459
{
423460
name: 'Content tests',
424461
contentBaseURL: 'https://gitbook.gitbook.io/test-gitbook-open/',

0 commit comments

Comments
 (0)