How to control the XMLHttpRequest object on an HTML5 Web Worker?












1














I have a page which will normally overrides window.XMLHttpRequest with a wrapper that does a few extra things like inserting in headers on certain requests.



I have some functionality in a 3rd party library that uses HTML5 Worker, and we are seeing that this request does not use the XMLHttpRequest wrapper object. So any request that this library makes is missing the required headers, and so the request will fail.



Is there a way to control the XMLHttpRequest that any Worker the current thread creates?



This 3rd party library code looks like this:



        function createWorker(url) {
var worker = new Worker(url);
worker.onmessage = function (e) {
if (e.data.status) {
onprogress(e.data.status);
} else if (e.data.error) {
onerror(e.data.error);
} else {
exportUtils.saveFile(new Blob([e.data]), params.fileName);
onfinish();
}
};
worker.postMessage(params); // window.location.origin +
return worker;
}


The Javascript that is returned by the URL variable above contains code like this:



        return new Promise(function(t, r) {
var n = new XMLHttpRequest
, a = "batch_" + o()
, u = e.dataUrl.split(e.serviceUrl)[1]
, c = ;
n.onload = function() {
for (var e = this.responseText, n = this.responseText.split("rn"), o = 0, a = n.length, i = a - 1; o < a && "{" !== n[o].slice(0, 1); )
o++;
for (; i > 0 && "}" !== n[i].slice(-1); )
i--;
n = n.slice(o, i + 1),
e = n.join("rn");
try {
var u = JSON.parse(e);
t(u)
} catch (t) {
r(s + e)
}
}
,
n.onerror = function() {
r(i)
}
,
n.onabort = function() {
r(i)
}
,
n.open("POST", e.serviceUrl + "$batch", !0),
n.setRequestHeader("Accept", "multipart/mixed"),
n.setRequestHeader("Content-Type", "multipart/mixed;boundary=" + a);
for (var p in e.headers)
"accept" != p.toLowerCase() && n.setRequestHeader(p, e.headers[p]);
c.push("--" + a),
c.push("Content-Type: application/http"),
c.push("Content-Transfer-Encoding: binary"),
c.push(""),
c.push("GET " + u + " HTTP/1.1");
for (var p in e.headers)
c.push(p + ":" + e.headers[p]);
c.push(""),
c.push(""),
c.push("--" + a + "--"),
c.push(""),
c = c.join("rn"),
n.send(c)
}
)









share|improve this question
























  • Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
    – Zirak
    Nov 20 at 21:11










  • @Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
    – codefactor
    Nov 20 at 21:23










  • I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
    – Tex
    Nov 20 at 21:39


















1














I have a page which will normally overrides window.XMLHttpRequest with a wrapper that does a few extra things like inserting in headers on certain requests.



I have some functionality in a 3rd party library that uses HTML5 Worker, and we are seeing that this request does not use the XMLHttpRequest wrapper object. So any request that this library makes is missing the required headers, and so the request will fail.



Is there a way to control the XMLHttpRequest that any Worker the current thread creates?



This 3rd party library code looks like this:



        function createWorker(url) {
var worker = new Worker(url);
worker.onmessage = function (e) {
if (e.data.status) {
onprogress(e.data.status);
} else if (e.data.error) {
onerror(e.data.error);
} else {
exportUtils.saveFile(new Blob([e.data]), params.fileName);
onfinish();
}
};
worker.postMessage(params); // window.location.origin +
return worker;
}


The Javascript that is returned by the URL variable above contains code like this:



        return new Promise(function(t, r) {
var n = new XMLHttpRequest
, a = "batch_" + o()
, u = e.dataUrl.split(e.serviceUrl)[1]
, c = ;
n.onload = function() {
for (var e = this.responseText, n = this.responseText.split("rn"), o = 0, a = n.length, i = a - 1; o < a && "{" !== n[o].slice(0, 1); )
o++;
for (; i > 0 && "}" !== n[i].slice(-1); )
i--;
n = n.slice(o, i + 1),
e = n.join("rn");
try {
var u = JSON.parse(e);
t(u)
} catch (t) {
r(s + e)
}
}
,
n.onerror = function() {
r(i)
}
,
n.onabort = function() {
r(i)
}
,
n.open("POST", e.serviceUrl + "$batch", !0),
n.setRequestHeader("Accept", "multipart/mixed"),
n.setRequestHeader("Content-Type", "multipart/mixed;boundary=" + a);
for (var p in e.headers)
"accept" != p.toLowerCase() && n.setRequestHeader(p, e.headers[p]);
c.push("--" + a),
c.push("Content-Type: application/http"),
c.push("Content-Transfer-Encoding: binary"),
c.push(""),
c.push("GET " + u + " HTTP/1.1");
for (var p in e.headers)
c.push(p + ":" + e.headers[p]);
c.push(""),
c.push(""),
c.push("--" + a + "--"),
c.push(""),
c = c.join("rn"),
n.send(c)
}
)









share|improve this question
























  • Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
    – Zirak
    Nov 20 at 21:11










  • @Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
    – codefactor
    Nov 20 at 21:23










  • I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
    – Tex
    Nov 20 at 21:39
















1












1








1







I have a page which will normally overrides window.XMLHttpRequest with a wrapper that does a few extra things like inserting in headers on certain requests.



I have some functionality in a 3rd party library that uses HTML5 Worker, and we are seeing that this request does not use the XMLHttpRequest wrapper object. So any request that this library makes is missing the required headers, and so the request will fail.



Is there a way to control the XMLHttpRequest that any Worker the current thread creates?



This 3rd party library code looks like this:



        function createWorker(url) {
var worker = new Worker(url);
worker.onmessage = function (e) {
if (e.data.status) {
onprogress(e.data.status);
} else if (e.data.error) {
onerror(e.data.error);
} else {
exportUtils.saveFile(new Blob([e.data]), params.fileName);
onfinish();
}
};
worker.postMessage(params); // window.location.origin +
return worker;
}


The Javascript that is returned by the URL variable above contains code like this:



        return new Promise(function(t, r) {
var n = new XMLHttpRequest
, a = "batch_" + o()
, u = e.dataUrl.split(e.serviceUrl)[1]
, c = ;
n.onload = function() {
for (var e = this.responseText, n = this.responseText.split("rn"), o = 0, a = n.length, i = a - 1; o < a && "{" !== n[o].slice(0, 1); )
o++;
for (; i > 0 && "}" !== n[i].slice(-1); )
i--;
n = n.slice(o, i + 1),
e = n.join("rn");
try {
var u = JSON.parse(e);
t(u)
} catch (t) {
r(s + e)
}
}
,
n.onerror = function() {
r(i)
}
,
n.onabort = function() {
r(i)
}
,
n.open("POST", e.serviceUrl + "$batch", !0),
n.setRequestHeader("Accept", "multipart/mixed"),
n.setRequestHeader("Content-Type", "multipart/mixed;boundary=" + a);
for (var p in e.headers)
"accept" != p.toLowerCase() && n.setRequestHeader(p, e.headers[p]);
c.push("--" + a),
c.push("Content-Type: application/http"),
c.push("Content-Transfer-Encoding: binary"),
c.push(""),
c.push("GET " + u + " HTTP/1.1");
for (var p in e.headers)
c.push(p + ":" + e.headers[p]);
c.push(""),
c.push(""),
c.push("--" + a + "--"),
c.push(""),
c = c.join("rn"),
n.send(c)
}
)









share|improve this question















I have a page which will normally overrides window.XMLHttpRequest with a wrapper that does a few extra things like inserting in headers on certain requests.



I have some functionality in a 3rd party library that uses HTML5 Worker, and we are seeing that this request does not use the XMLHttpRequest wrapper object. So any request that this library makes is missing the required headers, and so the request will fail.



Is there a way to control the XMLHttpRequest that any Worker the current thread creates?



This 3rd party library code looks like this:



        function createWorker(url) {
var worker = new Worker(url);
worker.onmessage = function (e) {
if (e.data.status) {
onprogress(e.data.status);
} else if (e.data.error) {
onerror(e.data.error);
} else {
exportUtils.saveFile(new Blob([e.data]), params.fileName);
onfinish();
}
};
worker.postMessage(params); // window.location.origin +
return worker;
}


The Javascript that is returned by the URL variable above contains code like this:



        return new Promise(function(t, r) {
var n = new XMLHttpRequest
, a = "batch_" + o()
, u = e.dataUrl.split(e.serviceUrl)[1]
, c = ;
n.onload = function() {
for (var e = this.responseText, n = this.responseText.split("rn"), o = 0, a = n.length, i = a - 1; o < a && "{" !== n[o].slice(0, 1); )
o++;
for (; i > 0 && "}" !== n[i].slice(-1); )
i--;
n = n.slice(o, i + 1),
e = n.join("rn");
try {
var u = JSON.parse(e);
t(u)
} catch (t) {
r(s + e)
}
}
,
n.onerror = function() {
r(i)
}
,
n.onabort = function() {
r(i)
}
,
n.open("POST", e.serviceUrl + "$batch", !0),
n.setRequestHeader("Accept", "multipart/mixed"),
n.setRequestHeader("Content-Type", "multipart/mixed;boundary=" + a);
for (var p in e.headers)
"accept" != p.toLowerCase() && n.setRequestHeader(p, e.headers[p]);
c.push("--" + a),
c.push("Content-Type: application/http"),
c.push("Content-Transfer-Encoding: binary"),
c.push(""),
c.push("GET " + u + " HTTP/1.1");
for (var p in e.headers)
c.push(p + ":" + e.headers[p]);
c.push(""),
c.push(""),
c.push("--" + a + "--"),
c.push(""),
c = c.join("rn"),
n.send(c)
}
)






javascript web-worker






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 at 21:27









Zirak

26.3k116687




26.3k116687










asked Nov 20 at 21:06









codefactor

9072930




9072930












  • Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
    – Zirak
    Nov 20 at 21:11










  • @Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
    – codefactor
    Nov 20 at 21:23










  • I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
    – Tex
    Nov 20 at 21:39




















  • Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
    – Zirak
    Nov 20 at 21:11










  • @Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
    – codefactor
    Nov 20 at 21:23










  • I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
    – Tex
    Nov 20 at 21:39


















Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
– Zirak
Nov 20 at 21:11




Is this a service worker you're using, or a regular web-worker? If it's a regular web-worker, you can actually use service workers to manipulate all outgoing requests
– Zirak
Nov 20 at 21:11












@Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
– codefactor
Nov 20 at 21:23




@Zirak I don't know the difference between "service worker" and "regular web-worker" so I posted some code fragments into the question which should clarify.
– codefactor
Nov 20 at 21:23












I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
– Tex
Nov 20 at 21:39






I don't think this is possible. Workers run in a separate global context. That is, if you've overridden window.XMLHttpRequest, the Worker won't be aware if that - it will get its own context with its own XMLHttpRequest.
– Tex
Nov 20 at 21:39














1 Answer
1






active

oldest

votes


















3














The answer is both a soft "no" and an eventual "yes".



When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1).



What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api.



However, there's a relatively new kid in block called Service Workers which can help you quite a bit!



Service workers



Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. They are also global to your entire domain, so any request made from your site will be passed through them.



Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses.



Let's see a small example (note, run these from a local webserver):



// index.html
<script>
navigator.serviceWorker.register('sw.js')
.then(console.log.bind(console, 'SW registered!'))
.catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
})


Here we're registering a service worker and then requesting a page every 5 seconds. In the service worker, we're simple logging each network event, which can be caught in the fetch event.



On first load, you should see the service worker being registered. SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. I advise you to play around with the event properties before reading on so things will be clearer.



Cool! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. In an ideal world, we could access event.request.headers and add our own headers! Dreamy, isn't it!?



Unfortunately, request/response headers are guarded and immutable. Fortunately, we are a stubborn bunch and can simply re-construct the request:



// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
// extract our request
const { request } = event;

// clone the current headers
const newHeaders = new Headers();
for (const [key, val] of request.headers) {
newHeaders.append(key, val);
}
// ...and add one of our own
newHeaders.append('Say-What', 'You heard me!');

// clone the request, but override the headers with our own
const superDuperReq = new Request(request, {
headers: newHeaders
});

// now instead of the original request, our new request will take precedence!
return fetch(superDuperReq);
});


This is a few different concepts at play so it's okay if it takes more than once to get. Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! Hurray!



enter image description here



The Bad



Now, to some of the downsides:




  • Since we're hijacking every single request, we can accidentally change requests we didn't mean to and potentially destroy the entire universe!

  • Upgrading SWs is a huge pain. SW lifecycle is complex, debugging it on your users is difficult. I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description

  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles

  • Because they are so powerful, SWs can only be served over https. You should already be using https anyway, but this is still a hindrance

  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity




(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed.






share|improve this answer





















  • Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
    – codefactor
    Nov 20 at 22:40










  • You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
    – Zirak
    Nov 20 at 23:27











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401528%2fhow-to-control-the-xmlhttprequest-object-on-an-html5-web-worker%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









3














The answer is both a soft "no" and an eventual "yes".



When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1).



What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api.



However, there's a relatively new kid in block called Service Workers which can help you quite a bit!



Service workers



Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. They are also global to your entire domain, so any request made from your site will be passed through them.



Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses.



Let's see a small example (note, run these from a local webserver):



// index.html
<script>
navigator.serviceWorker.register('sw.js')
.then(console.log.bind(console, 'SW registered!'))
.catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
})


Here we're registering a service worker and then requesting a page every 5 seconds. In the service worker, we're simple logging each network event, which can be caught in the fetch event.



On first load, you should see the service worker being registered. SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. I advise you to play around with the event properties before reading on so things will be clearer.



Cool! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. In an ideal world, we could access event.request.headers and add our own headers! Dreamy, isn't it!?



Unfortunately, request/response headers are guarded and immutable. Fortunately, we are a stubborn bunch and can simply re-construct the request:



// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
// extract our request
const { request } = event;

// clone the current headers
const newHeaders = new Headers();
for (const [key, val] of request.headers) {
newHeaders.append(key, val);
}
// ...and add one of our own
newHeaders.append('Say-What', 'You heard me!');

// clone the request, but override the headers with our own
const superDuperReq = new Request(request, {
headers: newHeaders
});

// now instead of the original request, our new request will take precedence!
return fetch(superDuperReq);
});


This is a few different concepts at play so it's okay if it takes more than once to get. Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! Hurray!



enter image description here



The Bad



Now, to some of the downsides:




  • Since we're hijacking every single request, we can accidentally change requests we didn't mean to and potentially destroy the entire universe!

  • Upgrading SWs is a huge pain. SW lifecycle is complex, debugging it on your users is difficult. I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description

  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles

  • Because they are so powerful, SWs can only be served over https. You should already be using https anyway, but this is still a hindrance

  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity




(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed.






share|improve this answer





















  • Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
    – codefactor
    Nov 20 at 22:40










  • You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
    – Zirak
    Nov 20 at 23:27
















3














The answer is both a soft "no" and an eventual "yes".



When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1).



What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api.



However, there's a relatively new kid in block called Service Workers which can help you quite a bit!



Service workers



Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. They are also global to your entire domain, so any request made from your site will be passed through them.



Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses.



Let's see a small example (note, run these from a local webserver):



// index.html
<script>
navigator.serviceWorker.register('sw.js')
.then(console.log.bind(console, 'SW registered!'))
.catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
})


Here we're registering a service worker and then requesting a page every 5 seconds. In the service worker, we're simple logging each network event, which can be caught in the fetch event.



On first load, you should see the service worker being registered. SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. I advise you to play around with the event properties before reading on so things will be clearer.



Cool! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. In an ideal world, we could access event.request.headers and add our own headers! Dreamy, isn't it!?



Unfortunately, request/response headers are guarded and immutable. Fortunately, we are a stubborn bunch and can simply re-construct the request:



// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
// extract our request
const { request } = event;

// clone the current headers
const newHeaders = new Headers();
for (const [key, val] of request.headers) {
newHeaders.append(key, val);
}
// ...and add one of our own
newHeaders.append('Say-What', 'You heard me!');

// clone the request, but override the headers with our own
const superDuperReq = new Request(request, {
headers: newHeaders
});

// now instead of the original request, our new request will take precedence!
return fetch(superDuperReq);
});


This is a few different concepts at play so it's okay if it takes more than once to get. Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! Hurray!



enter image description here



The Bad



Now, to some of the downsides:




  • Since we're hijacking every single request, we can accidentally change requests we didn't mean to and potentially destroy the entire universe!

  • Upgrading SWs is a huge pain. SW lifecycle is complex, debugging it on your users is difficult. I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description

  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles

  • Because they are so powerful, SWs can only be served over https. You should already be using https anyway, but this is still a hindrance

  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity




(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed.






share|improve this answer





















  • Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
    – codefactor
    Nov 20 at 22:40










  • You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
    – Zirak
    Nov 20 at 23:27














3












3








3






The answer is both a soft "no" and an eventual "yes".



When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1).



What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api.



However, there's a relatively new kid in block called Service Workers which can help you quite a bit!



Service workers



Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. They are also global to your entire domain, so any request made from your site will be passed through them.



Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses.



Let's see a small example (note, run these from a local webserver):



// index.html
<script>
navigator.serviceWorker.register('sw.js')
.then(console.log.bind(console, 'SW registered!'))
.catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
})


Here we're registering a service worker and then requesting a page every 5 seconds. In the service worker, we're simple logging each network event, which can be caught in the fetch event.



On first load, you should see the service worker being registered. SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. I advise you to play around with the event properties before reading on so things will be clearer.



Cool! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. In an ideal world, we could access event.request.headers and add our own headers! Dreamy, isn't it!?



Unfortunately, request/response headers are guarded and immutable. Fortunately, we are a stubborn bunch and can simply re-construct the request:



// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
// extract our request
const { request } = event;

// clone the current headers
const newHeaders = new Headers();
for (const [key, val] of request.headers) {
newHeaders.append(key, val);
}
// ...and add one of our own
newHeaders.append('Say-What', 'You heard me!');

// clone the request, but override the headers with our own
const superDuperReq = new Request(request, {
headers: newHeaders
});

// now instead of the original request, our new request will take precedence!
return fetch(superDuperReq);
});


This is a few different concepts at play so it's okay if it takes more than once to get. Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! Hurray!



enter image description here



The Bad



Now, to some of the downsides:




  • Since we're hijacking every single request, we can accidentally change requests we didn't mean to and potentially destroy the entire universe!

  • Upgrading SWs is a huge pain. SW lifecycle is complex, debugging it on your users is difficult. I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description

  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles

  • Because they are so powerful, SWs can only be served over https. You should already be using https anyway, but this is still a hindrance

  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity




(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed.






share|improve this answer












The answer is both a soft "no" and an eventual "yes".



When a piece of code runs in a different context (like a webworker or an iframe), you do not have direct control of its global object (1).



What's more, XMLHttpRequest isn't the only way to send out network requests - you have several other methods, chief among them the fetch api.



However, there's a relatively new kid in block called Service Workers which can help you quite a bit!



Service workers



Service workers (abbrev. SWs) are very much like the web workers you already know, but instead of only running in the current page, they continue to run in the background as long as your user stays in your domain. They are also global to your entire domain, so any request made from your site will be passed through them.



Their main purpose in life is reacting to network requests, usually used for caching purposes and offline content, serving push notifications, and several other niche uses.



Let's see a small example (note, run these from a local webserver):



// index.html
<script>
navigator.serviceWorker.register('sw.js')
.then(console.log.bind(console, 'SW registered!'))
.catch(console.error.bind(console, 'Oh nose!'));

setInterval(() => {
fetch('/hello/');
}, 5000);
</script>

// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
})


Here we're registering a service worker and then requesting a page every 5 seconds. In the service worker, we're simple logging each network event, which can be caught in the fetch event.



On first load, you should see the service worker being registered. SWs only begin intercepting requests from the first page after they were installed...so refresh the page to begin seeing the fetch events being logged. I advise you to play around with the event properties before reading on so things will be clearer.



Cool! We can see from poking around with the event in the console that event.request is the Request object our browser constructed. In an ideal world, we could access event.request.headers and add our own headers! Dreamy, isn't it!?



Unfortunately, request/response headers are guarded and immutable. Fortunately, we are a stubborn bunch and can simply re-construct the request:



// sw.js
console.log('Hello from a friendly service worker');

addEventListener('fetch', event => {
console.log('fetch!', event);
// extract our request
const { request } = event;

// clone the current headers
const newHeaders = new Headers();
for (const [key, val] of request.headers) {
newHeaders.append(key, val);
}
// ...and add one of our own
newHeaders.append('Say-What', 'You heard me!');

// clone the request, but override the headers with our own
const superDuperReq = new Request(request, {
headers: newHeaders
});

// now instead of the original request, our new request will take precedence!
return fetch(superDuperReq);
});


This is a few different concepts at play so it's okay if it takes more than once to get. Essentially though, we're creating a new request which will be sent in place of the original one, and setting a new header! Hurray!



enter image description here



The Bad



Now, to some of the downsides:




  • Since we're hijacking every single request, we can accidentally change requests we didn't mean to and potentially destroy the entire universe!

  • Upgrading SWs is a huge pain. SW lifecycle is complex, debugging it on your users is difficult. I've seen a good video on dealing with it, unfortunately can't find it right now, but mdn has a fairly good description

  • Debugging SWs is often a very annoying experience, especially when combined with their weird lifecycles

  • Because they are so powerful, SWs can only be served over https. You should already be using https anyway, but this is still a hindrance

  • This is a lot of things to do for a relatively small benefit, so maybe reconsider its necessity




(1) You can access the global object of an iframe in the same origin as you, but getting your code to run first to modify the global object is tricky indeed.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 20 at 22:10









Zirak

26.3k116687




26.3k116687












  • Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
    – codefactor
    Nov 20 at 22:40










  • You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
    – Zirak
    Nov 20 at 23:27


















  • Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
    – codefactor
    Nov 20 at 22:40










  • You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
    – Zirak
    Nov 20 at 23:27
















Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
– codefactor
Nov 20 at 22:40




Very interesting - in your experience is a service worker using this method to swap out the request going to work in all modern browsers that support "Worker" ?
– codefactor
Nov 20 at 22:40












You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
– Zirak
Nov 20 at 23:27




You can see the support percentages on caniuse. Basically, all non-IE, with Android 4 WebView support lacking.
– Zirak
Nov 20 at 23:27


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53401528%2fhow-to-control-the-xmlhttprequest-object-on-an-html5-web-worker%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Wiesbaden

Marschland

Dieringhausen