-
Notifications
You must be signed in to change notification settings - Fork 400
Add application redirects + vary headers #828
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,5 +19,5 @@ module.exports = { | |
}, | ||
}, | ||
|
||
redirectLangPrefix: false, | ||
enablePrefixMiddleware: false, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
|
||
export default class DetailPage extends React.Component { | ||
render() { | ||
return ( | ||
<div> | ||
<h1>Detail Page</h1> | ||
</div> | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,5 @@ | |
|
||
html, | ||
body { | ||
background: red; | ||
background: #fff; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { getClientApp, isValidClientApp } from 'core/utils'; | ||
import { getLanguage, isValidLang } from 'core/i18n/utils'; | ||
import config from 'config'; | ||
import log from 'core/logger'; | ||
|
||
|
||
export function prefixMiddleWare(req, res, next, { _config = config } = {}) { | ||
// Split on slashes after removing the leading slash. | ||
const URLParts = req.originalUrl.replace(/^\//, '').split('/'); | ||
log.debug(URLParts); | ||
|
||
// Get lang and app parts from the URL. At this stage they may be incorrect | ||
// or missing. | ||
const [langFromURL, appFromURL] = URLParts; | ||
|
||
// Get language from URL or fall-back to detecting it from accept-language header. | ||
const acceptLanguage = req.headers['accept-language']; | ||
const { lang, isLangFromHeader } = getLanguage({ lang: langFromURL, acceptLanguage }); | ||
|
||
const hasValidLang = isValidLang(langFromURL); | ||
const hasValidClientApp = isValidClientApp(appFromURL, { _config }); | ||
|
||
let prependedLang = false; | ||
|
||
if ((hasValidLang && langFromURL !== lang) || | ||
(!hasValidLang && hasValidClientApp)) { | ||
// Replace the first part of the URL if: | ||
// * It's valid and we've mapped it e.g: pt -> pt-PT. | ||
// * The lang is invalid but we have a valid application | ||
// e.g. /bogus/firefox/. | ||
log.info(`Replacing lang in URL ${URLParts[0]} -> ${lang}`); | ||
URLParts[0] = lang; | ||
} else if (!hasValidLang) { | ||
// If lang wasn't valid or was missing prepend one. | ||
log.info(`Prepending lang to URL: ${lang}`); | ||
URLParts.splice(0, 0, lang); | ||
prependedLang = true; | ||
} | ||
|
||
let isApplicationFromHeader = false; | ||
|
||
if (!hasValidClientApp && prependedLang && | ||
isValidClientApp(URLParts[1], { _config })) { | ||
// We skip prepending an app if we'd previously prepended a lang and the | ||
// 2nd part of the URL is now a valid app. | ||
log.info('Application in URL is valid following prepending a lang.'); | ||
} else if (!hasValidClientApp) { | ||
// If the app supplied is not valid we need to prepend one. | ||
const application = getClientApp(req.headers['user-agent']); | ||
log.info(`Prepending application to URL: ${application}`); | ||
URLParts.splice(1, 0, application); | ||
isApplicationFromHeader = true; | ||
} | ||
|
||
// Redirect to the new URL. | ||
// For safety we'll deny a redirect to a URL starting with '//' since | ||
// that will be treated as a protocol-free URL. | ||
const newURL = `/${URLParts.join('/')}`; | ||
if (newURL !== req.originalUrl && !newURL.startsWith('//')) { | ||
// Collect vary headers to apply to the redirect | ||
// so we can make it cacheable. | ||
// TODO: Make the redirects cacheable by adding expires headers. | ||
const varyHeaders = []; | ||
if (isLangFromHeader) { | ||
varyHeaders.push('accept-language'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought the point of redirecting to a lang or application URL is that we won't have to send a vary header at all. In other words, why would we need to vary the cache on complete There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's to be able to make the redirect cacheable not the resulting URL. I'm not setting expires yet until we've had a chance to see that this works right. Currently AMO caches its 301 redirs for a year [1] [1] https://github.com/mozilla/addons-server/blob/master/src/olympia/amo/middleware.py#L74 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I see. It might be helpful to put a comment in there saying that the vary headers are there to make the redirect response cachable. I guess now that you explained it it makes sense though. |
||
} | ||
if (isApplicationFromHeader) { | ||
varyHeaders.push('user-agent'); | ||
} | ||
res.set('vary', varyHeaders); | ||
return res.redirect(302, newURL); | ||
} | ||
|
||
// Add the data to res.locals to be utilised later. | ||
/* eslint-disable no-param-reassign */ | ||
const [newLang, newApp] = URLParts; | ||
res.locals.lang = newLang; | ||
res.locals.clientApp = newApp; | ||
/* eslint-enable no-param-reassign */ | ||
|
||
return next(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it might be helpful to get a
log.debug()
ofURLParts
here.