Skip to content

Commit

Permalink
Add fetch API globals
Browse files Browse the repository at this point in the history
  • Loading branch information
bobisjan committed Jul 8, 2022
1 parent 703e788 commit 24aba7b
Show file tree
Hide file tree
Showing 15 changed files with 220 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function initialize(instance) {
let { request } = instance.lookup('service:fastboot');
fetch.__fastbootRequest = request;
}

export default {
name: 'fastboot:fetch', // `ember-fetch` addon registers as `fetch`
initialize,
};
5 changes: 3 additions & 2 deletions packages/ember-cli-fastboot/test/request-details-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';

const path = require('path');
const chai = require('chai');
const expect = chai.expect;
const RSVP = require('rsvp');
Expand All @@ -11,8 +12,8 @@ function injectMiddlewareAddon(app) {
pkg.devDependencies['body-parser'] =
process.env.npm_package_devDependencies_body_parser;
pkg.dependencies = pkg.dependencies || {};
pkg.dependencies['fastboot-express-middleware'] =
process.env.npm_package_dependencies_fastboot_express_middleware;
pkg.dependencies['fastboot'] = `file:${path.resolve(__dirname, '../../fastboot')}`
pkg.dependencies['fastboot-express-middleware'] = `file:${path.resolve(__dirname, '../../fastboot-express-middleware')}`
pkg['ember-addon'] = {
paths: ['lib/post-middleware'],
};
Expand Down
4 changes: 3 additions & 1 deletion packages/fastboot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@
"postversion": "git push origin master --tags"
},
"dependencies": {
"abortcontroller-polyfill": "^1.7.3",
"chalk": "^4.1.2",
"cookie": "^0.4.1",
"debug": "^4.3.3",
"jsdom": "^19.0.0",
"node-fetch": "^2.6.7",
"resolve": "^1.22.0",
"simple-dom": "^1.4.0",
"source-map-support": "^0.5.21"
Expand Down Expand Up @@ -79,4 +81,4 @@
"tokenRef": "GITHUB_AUTH"
}
}
}
}
88 changes: 88 additions & 0 deletions packages/fastboot/src/sandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const chalk = require('chalk');
const vm = require('vm');
const sourceMapSupport = require('source-map-support');

const httpRegex = /^https?:\/\//;
const protocolRelativeRegex = /^\/\//;

module.exports = class Sandbox {
constructor(globals) {
this.globals = globals;
Expand All @@ -14,6 +17,7 @@ module.exports = class Sandbox {

buildSandbox() {
let console = this.buildWrappedConsole();
let fetch = this.buildFetch();
let URL = require('url');
let globals = this.globals;

Expand All @@ -28,6 +32,7 @@ module.exports = class Sandbox {
// Convince jQuery not to assume it's in a browser
module: { exports: {} },
},
fetch,
globals
);

Expand All @@ -53,6 +58,89 @@ module.exports = class Sandbox {
return wrappedConsole;
}

buildFetch() {
let globals;

if (globalThis.fetch) {
globals = {
fetch: globalThis.fetch,
Request: globalThis.Request,
Response: globalThis.Response,
Headers: globalThis.Headers,
AbortController: globalThis.AbortController,
};
} else {
let nodeFetch = require('node-fetch');
let {
AbortController,
abortableFetch,
} = require('abortcontroller-polyfill/dist/cjs-ponyfill');
let { fetch, Request } = abortableFetch({
fetch: nodeFetch,
Request: nodeFetch.Request,
});

globals = {
fetch,
Request,
Response: nodeFetch.Response,
Headers: nodeFetch.Headers,
AbortController,
};
}

let originalFetch = globals.fetch;
globals.fetch = function __fastbootFetch(input, init) {
input = globals.fetch.__fastbootBuildAbsoluteURL(input);
return originalFetch(input, init);
};

globals.fetch.__fastbootBuildAbsoluteURL = function __fastbootBuildAbsoluteURL(input) {
if (input && input.href) {
// WHATWG URL or Node.js Url Object
input = input.href;
}

if (typeof input !== 'string') {
return input;
}

if (protocolRelativeRegex.test(input)) {
let request = globals.fetch.__fastbootRequest;
let [protocol] = globals.fetch.__fastbootParseRequest(input, request);
input = `${protocol}//${input}`;
} else if (!httpRegex.test(input)) {
let request = globals.fetch.__fastbootRequest;
let [protocol, host] = globals.fetch.__fastbootParseRequest(input, request);
input = `${protocol}//${host}${input}`;
}

return input;
};

globals.fetch.__fastbootParseRequest = function __fastbootParseRequest(url, request) {
if (!request) {
throw new Error(
`Using fetch with relative URL ${url}, but application instance has not been initialized yet.`
);
}

// Old Prember version is not sending protocol
const protocol = request.protocol === 'undefined:' ? 'http:' : request.protocol;
return [protocol, request.host];
};

let OriginalRequest = globals.Request;
globals.Request = class __FastBootRequest extends OriginalRequest {
constructor(input, init) {
input = globals.fetch.__fastbootBuildAbsoluteURL(input);
super(input, init);
}
};

return globals;
}

runScript(script) {
script.runInContext(this.context);
}
Expand Down
1 change: 1 addition & 0 deletions test-packages/basic-app/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ Router.map(function() {
this.route('echo-request-headers');
this.route('return-status-code-418');
this.route('metadata');
this.route('fetch');
});
27 changes: 27 additions & 0 deletions test-packages/basic-app/app/routes/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Route from '@ember/routing/route';
import { assert } from '@ember/debug';

export default class FetchRoute extends Route {
beforeModel() {
assert('fetch is available', fetch);
assert('Request is available', Request);
assert('Response is available', Response);
assert('Headers is available', Headers);
assert('AbortController is available', AbortController);
}

async model() {
let responses = await Promise.all([
fetch('http://localhost:45678/absolute-url.json'),
fetch(new Request('http://localhost:45678/absolute-request.json')),
fetch('//localhost:45678/protocol-relative-url.json'),
fetch(new Request('//localhost:45678/protocol-relative-request.json')),
fetch('/path-relative-url.json'),
fetch(new Request('/path-relative-request.json')),
]);

responses = await Promise.all(responses.map((response) => response.json()));

return responses.map((response) => response.response).join('|');
}
}
1 change: 1 addition & 0 deletions test-packages/basic-app/app/templates/fetch.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{@model}}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/absolute-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "absolute-request"
}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/absolute-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "absolute-url"
}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/path-relative-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "path-relative-request"
}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/path-relative-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "path-relative-url"
}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/protocol-relative-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "protocol-relative-request"
}
3 changes: 3 additions & 0 deletions test-packages/basic-app/public/protocol-relative-url.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"response": "protocol-relative-url"
}
40 changes: 40 additions & 0 deletions test-packages/basic-app/test/fetch-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const RSVP = require('rsvp');
const request = RSVP.denodeify(require('request'));
const expect = require('chai').use(require('chai-string')).expect;
const { startServer, stopServer } = require('../../test-libs');

describe('fetch', function () {
this.timeout(120000);

before(function () {
return startServer();
});

after(function () {
return stopServer();
});

it('uses fetch', async () => {
const response = await request({
url: 'http://localhost:45678/fetch',
headers: {
Accept: 'text/html',
},
});

expect(response.statusCode).to.equal(200);
expect(response.headers['content-type']).to.equalIgnoreCase('text/html; charset=utf-8');
expect(response.body).to.contain(
[
'absolute-url',
'absolute-request',
'protocol-relative-url',
'protocol-relative-request',
'path-relative-url',
'path-relative-request',
].join('|')
);
});
});
30 changes: 30 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3621,6 +3621,11 @@ abortcontroller-polyfill@^1.4.0:
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.4.0.tgz#0d5eb58e522a461774af8086414f68e1dda7a6c4"
integrity sha512-3ZFfCRfDzx3GFjO6RAkYx81lPGpUS20ISxux9gLxuKnqafNcFQo59+IoZqpO2WvQlyc287B62HDnDdNYRmlvWA==

abortcontroller-polyfill@^1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5"
integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==

accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
Expand Down Expand Up @@ -13595,6 +13600,13 @@ node-fetch@^2.3.0, node-fetch@^2.6.0:
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==

node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"

node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
Expand Down Expand Up @@ -17074,6 +17086,11 @@ tr46@^3.0.0:
dependencies:
punycode "^2.1.1"

tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==

tree-sync@^1.2.2:
version "1.4.0"
resolved "https://registry.yarnpkg.com/tree-sync/-/tree-sync-1.4.0.tgz#314598d13abaf752547d9335b8f95d9a137100d6"
Expand Down Expand Up @@ -17653,6 +17670,11 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"

webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==

webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
Expand Down Expand Up @@ -17826,6 +17848,14 @@ whatwg-url@^10.0.0:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"

whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"

whatwg-url@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
Expand Down

0 comments on commit 24aba7b

Please sign in to comment.