Skip to content

Commit

Permalink
🎨 Koenig - Added support for shortened URLs in embed card (TryGhost#9781
Browse files Browse the repository at this point in the history
)

refs TryGhost#9724

- perform a HEAD request on a url if we don't find a matching provider, following any redirects until we hit success response before looking up providers for the resulting url
  • Loading branch information
kevinansfield authored and kirrg001 committed Aug 12, 2018
1 parent 860718f commit cfd9ff3
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 23 deletions.
78 changes: 55 additions & 23 deletions core/server/api/oembed.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
const common = require('../lib/common');
const {extract, hasProvider} = require('oembed-parser');
const Promise = require('bluebird');
const request = require('../lib/request');

const findUrlWithProvider = function findUrlWithProvider(url) {
let provider;

// build up a list of URL variations to test against because the oembed
// providers list is not always up to date with scheme or www vs non-www
let baseUrl = url.replace(/^\/\/|^https?:\/\/(?:www\.)?/, '');
let testUrls = [
`http://${baseUrl}`,
`https://${baseUrl}`,
`http://www.${baseUrl}`,
`https://www.${baseUrl}`
];

for (let testUrl of testUrls) {
provider = hasProvider(testUrl);
if (provider) {
url = testUrl;
break;
}
}

return {url, provider};
};

let oembed = {
read(options) {
Expand All @@ -12,34 +37,41 @@ let oembed = {
}));
}

// build up a list of URL variations to test against because the oembed
// providers list is not always up to date with scheme or www vs non-www
let base = url.replace(/^\/\/|^https?:\/\/(?:www\.)?/, '');
let testUrls = [
`http://${base}`,
`https://${base}`,
`http://www.${base}`,
`https://www.${base}`
];
let provider;
for (let testUrl of testUrls) {
provider = hasProvider(testUrl);
if (provider) {
url = testUrl;
break;
}
}

if (!provider) {
function unknownProvider() {
return Promise.reject(new common.errors.ValidationError({
message: common.i18n.t('errors.api.oembed.unknownProvider')
}));
}

return extract(url).catch((err) => {
return Promise.reject(new common.errors.InternalServerError({
message: err.message
}));
function knownProvider(url) {
return extract(url).catch((err) => {
return Promise.reject(new common.errors.InternalServerError({
message: err.message
}));
});
}

let provider;
({url, provider} = findUrlWithProvider(url));

if (provider) {
return knownProvider(url);
}

// see if the URL is a redirect to cater for shortened urls
return request(url, {
method: 'HEAD',
timeout: 2 * 1000,
followRedirect: true
}).then((response) => {
if (response.url !== url) {
({url, provider} = findUrlWithProvider(response.url));
return provider ? knownProvider(url) : unknownProvider();
}

return unknownProvider();
}).catch(() => {
return unknownProvider();
});
}
};
Expand Down
36 changes: 36 additions & 0 deletions core/test/unit/api/oembed_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,38 @@ describe('API: oembed', function () {
}).catch(done);
});

it('follows redirects to get base url', function (done) {
let redirectMock = nock('https://youtu.be')
.intercept('/yHohwmrxrto', 'HEAD')
.reply(302, undefined, {
// eslint-disable-next-line
'Location': 'https://www.youtube.com/watch?v=yHohwmrxrto&feature=youtu.be'
});

let videoMock = nock('https://www.youtube.com')
.intercept('/watch', 'HEAD')
.query({v: 'yHohwmrxrto', feature: 'youtu.be'})
.reply(200);

let requestMock = nock('https://www.youtube.com')
.get('/oembed')
.query(true)
.reply(200, {
html: 'test'
});

OembedAPI.read({url: 'https://youtu.be/yHohwmrxrto'})
.then((results) => {
redirectMock.isDone().should.be.true;
videoMock.isDone().should.be.true;
requestMock.isDone().should.be.true;
should.exist(results);
should.exist(results.html);
results.html.should.eql('test');
done();
}).catch(done);
});

it('returns error for missing url', function (done) {
OembedAPI.read({url: ''})
.then(() => {
Expand All @@ -50,6 +82,10 @@ describe('API: oembed', function () {
});

it('returns error for unsupported provider', function (done) {
nock('http://example.com')
.intercept('/unknown', 'HEAD')
.reply(200);

OembedAPI.read({url: 'http://example.com/unknown'})
.then(() => {
done(new Error('Fetch oembed with unknown url provider should error'));
Expand Down

0 comments on commit cfd9ff3

Please sign in to comment.