Skip to content

Commit

Permalink
fix(gatsby): Use anonymous requests when fetching resources (gatsbyjs…
Browse files Browse the repository at this point in the history
…#14443)

* fix(gatsby): Make requests not send credentials.

The [tests that I ran](gatsbyjs#14293 (comment)) suggest that this is no longer needed, and it fixes the [issues I was experiencing](gatsbyjs#14293 (comment)).

Fixes gatsbyjs#14293.

* Updated docs

* Removed withCredentials that was previously missed.

* Updated snapshots.

* adjust test for asset-prefix

* Ensure manifest is always loaded from content server

* Fix test
  • Loading branch information
Cameron Martin authored and GatsbyJS Bot committed Aug 14, 2019
1 parent c21b207 commit cdd800f
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 26 deletions.
10 changes: 1 addition & 9 deletions docs/docs/asset-prefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,7 @@ This feature works seamlessly with `pathPrefix`. Build out your application with

When using a custom asset prefix with `gatsby-plugin-offline`, your assets can still be cached offline. However, to ensure the plugin works correctly, there are a few things you need to do.

1. Your asset server needs to have the following headers set:

```
Access-Control-Allow-Origin: <site origin>
Access-Control-Allow-Credentials: true
```

Note that the origin needs to be specific, rather than using `*` to allow all origins. This is because Gatsby makes requests to fetch resources with `withCredentials` set to `true`, which disallows using `*` to match all origins. This is also why the second header is required. For local testing, use `http://localhost:9000` as the origin.

1. Your asset server needs to have the `Access-Control-Allow-Origin` header set either to `*` or your site's origin.
2. Certain essential resources need to be available on your content server (i.e. the one used to serve pages). This includes `sw.js`, as well as resources to precache: the Webpack bundle, the app bundle, the manifest (and any icons referenced), and the resources for the offline plugin app shell.

You can find most of these by looking for the `self.__precacheManifest` variable in your generated `sw.js`. Remember to also include `sw.js` itself, and any icons referenced in your `manifest.webmanifest` if you have one. To check your service worker is functioning as expected, look in Application → Service Workers in your browser dev tools, and check for any failed resources in the Console/Network tabs.
6 changes: 4 additions & 2 deletions e2e-tests/path-prefix/cypress/integration/asset-prefix.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ describe(`assetPrefix`, () => {
})

describe(`gatsby-plugin-manifest`, () => {
it(`prefixes manifest`, () => {
assetPrefixMatcher(cy.get(`head link[rel="manifest"]`))
it(`doesn’t prefix manifest`, () => {
cy.get(`head link[rel="manifest"]`)
.should(`have.attr`, `href`)
.and(`not.matches`, assetPrefixExpression)
})

it(`prefixes shortcut icon`, () => {
Expand Down
6 changes: 1 addition & 5 deletions e2e-tests/path-prefix/scripts/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ const server = http.createServer((request, response) =>
headers: [
{
key: `Access-Control-Allow-Origin`,
value: `http://localhost:9000`,
},
{
key: `Access-Control-Allow-Credentials`,
value: true,
value: `*`,
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ exports[`develop-static-entry onPreRenderHTML can be used to replace postBodyCom
exports[`develop-static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><script src=\\"/socket.io/socket.io.js\\"></script></head><body><div> div3 </div><div> div2 </div><div> div1 </div><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"></div><script src=\\"/commons.js\\"></script></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"use-credentials\\"/><style> .style3 </style><style> .style2 </style><style> .style1 </style><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace headComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/><style> .style3 </style><style> .style2 </style><style> .style1 </style><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"use-credentials\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><div> div3 </div><div> div2 </div><div> div1 </div></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace postBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/></head><body><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><div> div3 </div><div> div2 </div><div> div1 </div></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"use-credentials\\"/></head><body><div> div3 </div><div> div2 </div><div> div1 </div><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
exports[`static-entry onPreRenderHTML can be used to replace preBodyComponents 1`] = `"<!DOCTYPE html><html><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"ie=edge\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1, shrink-to-fit=no\\"/><meta name=\\"generator\\" content=\\"Gatsby 2.0.0\\"/><link as=\\"fetch\\" rel=\\"preload\\" href=\\"/page-data/about/page-data.json\\" crossorigin=\\"anonymous\\"/></head><body><div> div3 </div><div> div2 </div><div> div1 </div><noscript id=\\"gatsby-noscript\\">This app works best with JavaScript enabled.</noscript><div id=\\"___gatsby\\"><div style=\\"outline:none\\" tabindex=\\"-1\\" role=\\"group\\" id=\\"gatsby-focus-wrapper\\"></div></div><script id=\\"gatsby-script-loader\\">/*<![CDATA[*/window.pagePath=\\"/about/\\";window.webpackCompilationHash=\\"1234567890abcdef1234\\";/*]]>*/</script><script id=\\"gatsby-chunk-mapping\\">/*<![CDATA[*/window.___chunkMapping={};/*]]>*/</script></body></html>"`;
39 changes: 39 additions & 0 deletions packages/gatsby/cache-dir/__tests__/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const checkSanitized = components => {
expect(
components.find(val => Array.isArray(val) && val.length === 0)
).toBeFalsy()
expect(components.find(val => Array.isArray(val))).toBeFalsy()
}

const checkNonEmptyHeadersPlugin = {
Expand Down Expand Up @@ -171,6 +172,7 @@ describe(`static-entry sanity checks`, () => {
beforeEach(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
global.__ASSET_PREFIX__ = ``
})

const methodsToCheck = [
Expand Down Expand Up @@ -218,6 +220,20 @@ describe(`static-entry sanity checks`, () => {
done()
})
})

test(`${methodName} can flatten arrays`, done => {
const plugin = injectValuePlugin(`onPreRenderHTML`, methodName, [
<style key="style1"> .style1 {} </style>,
<style key="style2"> .style2 {} </style>,
<style key="style3"> .style3 {} </style>,
[<style key="style4"> .style3 {} </style>],
])
global.plugins = [plugin, checkNonEmptyHeadersPlugin]

StaticEntry(`/about/`, (_, html) => {
done()
})
})
})
})

Expand Down Expand Up @@ -260,3 +276,26 @@ describe(`static-entry`, () => {
})
})
})

describe(`sanitizeComponents`, () => {
let sanitizeComponents

beforeEach(() => {
fs.readFileSync.mockImplementation(file => MOCK_FILE_INFO[file])
sanitizeComponents = require(`../static-entry`).sanitizeComponents
})

it(`strips assetPrefix for manifest link`, () => {
global.__PATH_PREFIX__ = `https://gatsbyjs.org/blog`
global.__BASE_PATH__ = `/blog`
global.__ASSET_PREFIX__ = `https://gatsbyjs.org`

const sanitizedComponents = sanitizeComponents([
<link
rel="manifest"
href="https://gatsbyjs.org/blog/manifest.webmanifest"
/>,
])
expect(sanitizedComponents[0].props.href).toBe(`/blog/manifest.webmanifest`)
})
})
1 change: 0 additions & 1 deletion packages/gatsby/cache-dir/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ const doFetch = (url, method = `GET`) =>
new Promise((resolve, reject) => {
const req = new XMLHttpRequest()
req.open(method, url, true)
req.withCredentials = true
req.onreadystatechange = () => {
if (req.readyState == 4) {
resolve(req)
Expand Down
1 change: 0 additions & 1 deletion packages/gatsby/cache-dir/prefetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const xhrPrefetchStrategy = function(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest()
req.open(`GET`, url, true)
req.withCredentials = true

req.onload = () => {
if (req.status === 200) {
Expand Down
34 changes: 29 additions & 5 deletions packages/gatsby/cache-dir/static-entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ const fs = require(`fs`)
const { join } = require(`path`)
const { renderToString, renderToStaticMarkup } = require(`react-dom/server`)
const { ServerLocation, Router, isRedirect } = require(`@reach/router`)
const { get, merge, isObject, flatten, uniqBy } = require(`lodash`)
const {
get,
merge,
isObject,
flatten,
uniqBy,
flattenDeep,
replace,
} = require(`lodash`)

const apiRunner = require(`./api-runner-ssr`)
const syncRequires = require(`./sync-requires`)
Expand Down Expand Up @@ -69,10 +77,26 @@ const loadPageDataSync = pagePath => {

const createElement = React.createElement

const sanitizeComponents = components => {
export const sanitizeComponents = components => {
const componentsArray = ensureArray(components)
return componentsArray.map(component => {
// Ensure manifest is always loaded from content server
// And not asset server when an assetPrefix is used
if (__ASSET_PREFIX__ && component.props.rel === `manifest`) {
return React.cloneElement(component, {
href: replace(component.props.href, __ASSET_PREFIX__, ``),
})
}
return component
})
}

const ensureArray = components => {
if (Array.isArray(components)) {
// remove falsy items
return components.filter(val => (Array.isArray(val) ? val.length > 0 : val))
// remove falsy items and flatten
return flattenDeep(
components.filter(val => (Array.isArray(val) ? val.length > 0 : val))
)
} else {
// we also accept single components, so we need to handle this case as well
return components ? [components] : []
Expand Down Expand Up @@ -305,7 +329,7 @@ export default (pagePath, callback) => {
rel="preload"
key={pageDataUrl}
href={pageDataUrl}
crossOrigin="use-credentials"
crossOrigin="anonymous"
/>
)
}
Expand Down

0 comments on commit cdd800f

Please sign in to comment.