Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upCross-origin URL redirection workaround #909
Conversation
|
I spent little while learning about the browser issue here (it boils down to Other than the inline comments I made, another suggestion I have is that the Note to self: you can reproduce the browser issue by running this in the console at https://example.com: fetch('https://httpbin.org/redirect-to?url=https%3A%2F%2Fhttpbin.org%2F', {
headers: {
Range: 'bytes=0-'
}
}) |
| // expose the final URL. | ||
| // fetch() or XMLHttpRequest.responseUrl are both candidates. | ||
| if (typeof window !== 'undefined' && typeof window.fetch === 'function' && !url.startsWith(window.location.origin + '/')) { | ||
| window.fetch(url, { method: 'HEAD' }).then(function (res) { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
| if (typeof window !== 'undefined' && typeof window.fetch === 'function' && !url.startsWith(window.location.origin + '/')) { | ||
| window.fetch(url, { method: 'HEAD' }).then(function (res) { | ||
| if (hasError) return | ||
| if (res.url !== url) { |
This comment has been minimized.
This comment has been minimized.
josephfrazier
Oct 7, 2016
Member
I think res.ok should be checked as well:
if (res.ok && res.url !== url) {
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| hasError = true | ||
| return cb(err) |
This comment has been minimized.
This comment has been minimized.
josephfrazier
Oct 7, 2016
Member
Actually, it might be better to handle the error case with an early exit. So, lines 165-180 could be replaced by:
if (!res.ok || res.url === url) {
hasError = true
return cb(err)
}
opts.url = res.url
get.concat(opts, function (err, res, data) {
if (hasError) return
if (err) {
hasError = true
return cb(err)
}
onResponse(res, data)
})
This comment has been minimized.
This comment has been minimized.
| } | ||
|
|
||
| hasError = true | ||
| return cb(err) |
This comment has been minimized.
This comment has been minimized.
josephfrazier
Oct 7, 2016
Member
I'm tempted to say this should be an early-exit as well (at line 162):
if (typeof window === 'undefined' || typeof window.fetch !== 'function' || url.startsWith(window.location.origin + '/')) {
hasError = true
return cb(err)
}
// (the code at line 163)
This comment has been minimized.
This comment has been minimized.
| // never falsely positive. | ||
| // | ||
| // We can't use simple-get here because http-browserify doesn't | ||
| // expose the final URL. |
This comment has been minimized.
This comment has been minimized.
josephfrazier
Oct 8, 2016
•
Member
I came back to this, and I now think we actually can use simple-get here. browserify replaced http-browserify with stream-http (see the list of shims here), which does expose res.url (see jhiesey/stream-http#40).
Here's a proof of concept that you can browserify and run in the console at https://example.com:
var get = require('simple-get')
function getPreflightRedirectConcat(opts, cb) {
return get.concat(opts, function (err, res, data) {
if (!err || opts.url.startsWith(window.location.origin + '/')) {
return cb(err, res, data)
}
return get.head(opts.url, function (errHead, resHead) {
// If the HEAD didn't work or didn't redirect, return the original error
if (errHead || resHead.url == opts.url) {
return cb(err, res, data)
}
var opts2 = Object.assign({}, opts)
opts2.url = resHead.url
return get.concat(opts2, cb)
})
})
}
var opts = {
url: 'https://httpbin.org/redirect-to?url=https%3A%2F%2Fhttpbin.org%2F',
headers: {
Range: 'bytes=0-'
}
}
getPreflightRedirectConcat(opts, function (err, res, data) {
if (err) {
console.error(err)
}
else {
console.log("final url: " + res.url)
console.log(data.toString())
}
window.close()
})
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Thanks a lot for reviewing this! |
This comment has been minimized.
This comment has been minimized.
|
You're welcome! I think it looks pretty good now. It could still be broken into its own function (like the one I posted above), but that can happen later if needed. We don't exactly have any tests for this part of the code, so can you confirm that it still fixes your use case? |
This comment has been minimized.
This comment has been minimized.
|
Excellent! I checked manually and confirm it still resolves the issue. |
This comment has been minimized.
This comment has been minimized.
|
Merged, thanks! |
This comment has been minimized.
This comment has been minimized.
|
Thank you! |
This comment has been minimized.
This comment has been minimized.
|
LGTM! |
This comment has been minimized.
This comment has been minimized.
lock
bot
commented
May 4, 2018
|
This thread has been automatically locked because it has not had recent activity. To discuss futher, please open a new issue. |
jablko commentedSep 13, 2016
Browsers allow HTTP redirects for simple cross-origin requests but not for requests that require preflight.
Because they set the Range header, this includes all web seed requests (if they're cross-origin).
So currently, web seeds can be cross-origin or they can make redirects, but not both at the same time.
What do you think of the following workaround for this?
If a request fails, maybe it's because the web seed sent a redirect and the browser objected, so try a simple request.
Make a simple request to unwind any redirects and get the final URL. (I used a HEAD request for this.)
If the final URL doesn't match the original URL, retry our original request with the new URL.
I make a feeble attempt to check that this is a cross-origin request.
It catches all cross-origin cases, but matches a few same-origin cases too. I figure that's okay in the interest of simplicity, since the check is merely an optimization anyway.