Skip to content

Commit 88fbb08

Browse files
committed
Allow to use relative URLs for fetch
1 parent 9cb1268 commit 88fbb08

File tree

11 files changed

+132
-14
lines changed

11 files changed

+132
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
function initialize(instance) {
2+
let { request } = instance.lookup('service:fastboot');
3+
fetch.__fastbootRequest = request;
4+
}
5+
6+
export default {
7+
name: 'fastboot:fetch', // `ember-fetch` addon registers as `fetch`
8+
initialize,
9+
};

Diff for: packages/fastboot/src/sandbox.js

+68-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ const chalk = require('chalk');
44
const vm = require('vm');
55
const sourceMapSupport = require('source-map-support');
66

7+
const httpRegex = /^https?:\/\//;
8+
const protocolRelativeRegex = /^\/\//;
9+
710
module.exports = class Sandbox {
811
constructor(globals) {
912
this.globals = globals;
@@ -56,27 +59,82 @@ module.exports = class Sandbox {
5659
}
5760

5861
buildFetch() {
62+
let globals;
63+
5964
if (globalThis.fetch) {
60-
return {
65+
globals = {
6166
fetch: globalThis.fetch,
6267
Request: globalThis.Request,
6368
Response: globalThis.Response,
6469
Headers: globalThis.Headers,
6570
AbortController: globalThis.AbortController,
6671
};
72+
} else {
73+
let nodeFetch = require('node-fetch');
74+
let {
75+
AbortController,
76+
abortableFetch,
77+
} = require('abortcontroller-polyfill/dist/cjs-ponyfill');
78+
let { fetch, Request } = abortableFetch({
79+
fetch: nodeFetch,
80+
Request: nodeFetch.Request,
81+
});
82+
83+
globals = {
84+
fetch,
85+
Request,
86+
Response: nodeFetch.Response,
87+
Headers: nodeFetch.Headers,
88+
AbortController,
89+
};
6790
}
6891

69-
let nodeFetch = require('node-fetch');
70-
let { AbortController, abortableFetch } = require('abortcontroller-polyfill/dist/cjs-ponyfill');
71-
let { fetch, Request } = abortableFetch({ fetch: nodeFetch, Request: nodeFetch.Request });
92+
let originalFetch = globals.fetch;
93+
globals.fetch = function __fastbootFetch(input, init) {
94+
if (input && input.href) {
95+
input.url = globals.fetch.__fastbootBuildAbsoluteURL(input.href);
96+
} else if (typeof input === 'string') {
97+
input = globals.fetch.__fastbootBuildAbsoluteURL(input);
98+
}
99+
return originalFetch(input, init);
100+
};
72101

73-
return {
74-
fetch,
75-
Request,
76-
Response: nodeFetch.Response,
77-
Headers: nodeFetch.Headers,
78-
AbortController,
102+
globals.fetch.__fastbootBuildAbsoluteURL = function __fastbootBuildAbsoluteURL(url) {
103+
if (protocolRelativeRegex.test(url)) {
104+
let [host] = globals.fetch.__fastbootParseRequest(url, fetch.__fastbootRequest);
105+
url = `${host}${url}`;
106+
} else if (!httpRegex.test(url)) {
107+
let [host, protocol] = globals.fetch.__fastbootParseRequest(url, fetch.__fastbootRequest);
108+
url = `${protocol}//${host}${url}`;
109+
}
110+
return url;
79111
};
112+
113+
globals.fetch.__fastbootParseRequest = function __fastbootParseRequest(url, request) {
114+
if (!request) {
115+
throw new Error(
116+
`Using fetch with relative URL ${url}, but application instance has not been initialized yet.`
117+
);
118+
}
119+
// Old Prember version is not sending protocol
120+
const protocol = request.protocol === 'undefined:' ? 'http:' : request.protocol;
121+
return [request.host, protocol];
122+
};
123+
124+
let OriginalRequest = globals.Request;
125+
globals.Request = class __FastBootRequest extends OriginalRequest {
126+
constructor(input, init) {
127+
if (typeof input === 'string') {
128+
input = globals.fetch.__fastbootBuildAbsoluteURL(input);
129+
} else if (input && input.href) {
130+
// WHATWG URL or Node.js Url Object
131+
input = globals.fetch.__fastbootBuildAbsoluteURL(input.href);
132+
}
133+
super(input, init);
134+
}
135+
};
136+
137+
return globals;
80138
}
81139

82140
runScript(script) {

Diff for: test-packages/basic-app/app/routes/fetch.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Route from '@ember/routing/route';
22
import { assert } from '@ember/debug';
3+
import { hash } from 'rsvp';
34

45
export default class FetchRoute extends Route {
56
beforeModel() {
@@ -11,7 +12,29 @@ export default class FetchRoute extends Route {
1112
}
1213

1314
async model() {
14-
let response = await fetch('https://api.github.com/users/tomster');
15-
return response.json();
15+
let [
16+
absoluteURL,
17+
absoluteRequest,
18+
protocolURL,
19+
protocolRequest,
20+
relativeURL,
21+
relativeRequest,
22+
] = await Promise.all([
23+
fetch('http://localhost:45678/absolute-url.json'),
24+
fetch(new Request('http://localhost:45678/absolute-request.json')),
25+
fetch('//localhost:45678/assets/protocol-url.json'),
26+
fetch(new Request('//localhost:45678/assets/protocol-request.json')),
27+
fetch('/assets/relative-url.json'),
28+
fetch(new Request('/assets/relative-request.json')),
29+
]);
30+
31+
return hash({
32+
absoluteURL: absoluteURL.json(),
33+
absoluteRequest: absoluteRequest.json(),
34+
protocolURL: protocolURL.json(),
35+
protocolRequest: protocolRequest.json(),
36+
relativeURL: relativeURL.json(),
37+
relativeRequest: relativeRequest.json(),
38+
});
1639
}
1740
}

Diff for: test-packages/basic-app/app/templates/fetch.hbs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
{{@model.login}}
1+
{{@model.absoluteURL.response}}
2+
{{@model.absoluteRequest.response}}
3+
{{@model.protocolURL.response}}
4+
{{@model.protocolRequest.response}}
5+
{{@model.relativeURL.response}}
6+
{{@model.relativeRequest.response}}

Diff for: test-packages/basic-app/public/absolute-request.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "absolute-request"
3+
}

Diff for: test-packages/basic-app/public/absolute-url.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "absolute-url"
3+
}

Diff for: test-packages/basic-app/public/protocol-request.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "protocol-request"
3+
}

Diff for: test-packages/basic-app/public/protocol-url.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "protocol-url"
3+
}

Diff for: test-packages/basic-app/public/relative-request.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "relative-request"
3+
}

Diff for: test-packages/basic-app/public/relative-url.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"response": "relative-url"
3+
}

Diff for: test-packages/basic-app/test/fetch-test.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ describe('fetch', function() {
2626

2727
expect(response.statusCode).to.equal(200);
2828
expect(response.headers['content-type']).to.equalIgnoreCase('text/html; charset=utf-8');
29-
expect(response.body).to.contain('tomster');
29+
expect(response.body).to.contain('absolute-url');
30+
expect(response.body).to.contain('absolute-request');
31+
expect(response.body).to.contain('protocol-url');
32+
expect(response.body).to.contain('protocol-request');
33+
expect(response.body).to.contain('relative-url');
34+
expect(response.body).to.contain('relative-request');
3035
});
3136
});

0 commit comments

Comments
 (0)