Hi I have simple service worker, but my Range header is not being send even thought i can log it on request object.
self.addEventListener('fetch', async function(event) {
if (event.request.headers.get("range")) {
const response = await fetch(event.request.clone());
return event.respondWith(this.getPartialResponse(event.request, response));
}
return event.respondWith(fetch(event.request));
}
async getPartialResponse(req, res) {
const pos = Number(/^bytes\=(\d+)\-$/g.exec(req.headers.get("range"))[1]);
const ab = await res.arrayBuffer();
const headers = new Headers(res.headers);
headers.append("Content-Range", `bytes ${pos}-${ab.byteLength - 1}/${ab.byteLength}`);
headers.append("Content-Length", ab.byteLength - pos + 1);
return new Response(ab.slice(pos), {
status: 206,
statusText: "Partial Content",
headers
});
}
Here you can see request one catched by service worker and the second one send to the api where you can notice the Range header is missing. Why ? My browser: Chrome/59.0.3071.104
caveat, I don't know what a range header is, so bear with me.
I know some headers are not available to the service worker for security reasons. Basically you cannot tamper with the request and response. For example you do not have access to Cache-Control in response objects from 3rd party domains, that way you cannot adjust the HTTP caching logic. This might be your issue, but I can't say for sure.
Look up rules concerning opaque request & responses.
Related
I have two servers, frontend (Next.js) and backend (express.js api server).
Frontend server is running without any additions. But I have an nginx proxy for backend.
So far everything is good because they are not connected yet.
Frontend: is working as it should be.
Backend: I can make calls directly from the backend itself (by self origin).
When I make a fetch get call from my frontend server to the backend server, it normally gives a cors error because the origins are different.
For this, I set the backend server with cors:
// /src/middlewares/cors.ts
import cors from 'cors';
const whitelist = new Set(['http://192.168.1.106:3000', 'https://192.168.148.132']);
// frontend: http://192.168.1.106:3000
// backend: https://192.168.148.132
const corsOptions = {
optionsSuccessStatus: 200,
origin: (origin: any, callback: any) => {
console.log('origin: ' + origin);
if (whitelist.has(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
// credentials: true,
};
export default cors(corsOptions);
and
// /app.ts
import cors from './middlewares/system/cors.js';
.
.
// setup cors
app.options('*', cors);
app.use(cors);
.
.
After doing this, I reach my main goal. The frontend server can make call to the backend server.
output:
But this time I notice a problem. I can't send self request to backend anymore (by self origin).
When dealing with this I looked at the origins that came to the /src/middlewares/cors.ts file that I showed above.
for frontend:
for backend:
I am using self signed ssl in nginx for back server.
And there is not any cors relevant headers in conf.
How can i solve this situation?
(If I'm missing something, you can point it out in the comments.)
The Origin header is only set in cross-origin requests. If you call your backend directly, the Javascript value is undefined, and in this case you must not restrict anything. You could, for example, write
if (!origin || whitelist.has(origin)) {
callback(null, true);
}
I am trying to add a parameter to the body of a POST request in a service worker but the original body is send. I use the following code
let token = '';
self.addEventListener('message', function (event) {
if (event.data && event.data.type === 'SET_TOKEN') {
token = event.data.token;
}
});
self.addEventListener('fetch', function (event) {
const destURL = new URL(event.request.url);
const headers = new Headers(event.request.headers);
if (token) headers.append('Authorization', token);
if (destURL.pathname === '/logout/') {
const promiseChain = event.request.json().then((originalBody) => {
return fetch(event.request.url, {
method: event.request.method,
headers,
// this body is not send to the server but only the original body
body: JSON.stringify(Object.assign(originalBody, { token })),
});
});
event.respondWith(promiseChain);
return;
}
const authReq = new Request(event.request, {
headers,
mode: 'cors',
});
event.respondWith(fetch(authReq));
});
Generally speaking, that should work. Here's a very similar live example that you can run and confirm:
https://glitch.com/edit/#!/materialistic-meadow-rowboat?path=sw.js%3A18%3A7
It will just POST to https://httpbin.org/#/Anything/post_anything, which will in turn echo back the request body and headers.
If your code isn't working, I would suggest using that basic sample as a starting point and slowing customizing it with your own logic. Additionally, it would be a good idea to confirm that your service worker is properly in control of the client page when its makes that request. Using Chrome DevTool's debugger interface, you should be able to put breakpoints in your service worker's fetch event handler and confirm that everything is running as expected.
Taking a step back, you should make sure that your web app isn't coded in such a way that it requires the service worker to be in control in order to go things like expire auth tokens. It's fine to have special logic in the service worker to account for auth, but make sure your code paths work similarly when the service worker doesn't intercept requests, as might be the case when a user force-reloads a web page by holding down the Shift key.
I have been debugging this for a few days, and I am hopeful that one of you fine people have run into something similar before, and have some ideas for us.
MacOS Big Sur,
Client and API are both JavaScript,
React/Node.js,
Hapi/Joi,
The problem is as follows: Some PDFs/images will fail to upload to our REST APIs. Never in Safari, sometimes in Firefox, always in Chrome and Edge.
The APIs are deployed to AWS and when running our local instances we cannot reproduce this issue(the uploads never fail locally of course, no CORS invoked. Local environment is identical to what is deployed).
The upload request will fail consistently when using some files, and will work consistently when using other files, as if the files themselves are corrupt. For example, PDF-A will always fail (unless being sent locally, then it will work. We get no failures or errors in the API code locally), PDF-B will never fail.
We receive this classic error(consistent in Edge and Chrome, intermittent in Firefox, not present in Safari):
Access to fetch at 'https://apiUrl/relevant-endpoint' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
We handle CORS in our APIs, by dynamically returning the preflight request info:
if (request.method === 'options') {
response.statusCode = 200;
response.headers['access-control-expose-headers'] = 'content-type, content-length, etag';
response.headers['access-control-max-age'] = 60 * 10; // 10 minutes
// dynamically set allowed headers & method
if (request.headers['access-control-request-headers']) {
response.headers['access-control-allow-headers'] = request.headers['access-control-request-headers'];
}
if (request.headers['access-control-request-method']) {
response.headers['access-control-allow-methods'] = request.headers['access-control-request-method'];
}
return h.continue;
}
It has been in production for 2 years, and already had handled a few thousand successful file uploads, 10s of thousands of requests.
Our client side request is as follows:
function request(req) {
return new Promise((resolve, reject) => {
const endpoint = `${ apiUrl }${ req.endpoint }`;
const options = {
method: req.method.toUpperCase()
};
fetch(endpoint, options)
.then((res) => res.json())
.then((res) => {
if (res.status === 'Fail') {
reject(res);
} else {
resolve(res.data);
}
})
.catch((err) => {
reject(err);
});
});
function submit(body) {
const options = {
endpoint: '/relevant-endpoint',
method: 'POST',
body
};
// Body : FormData { ... }
return new Promise((resolve, reject) => {
request(options)
.then(resolve)
.catch(reject);
});
}
const form = new FormData();
form.append('string A', this.state.stringA);
form.append('string B', this.state.stringB);
form.append('string C', this.state.stringC);
form.append('int A', this.state.intA);
form.append('file A', this.state.fileA); // the culprit
form.append('date A', this.state.dateA));
submit(form);
If the files that cause errors are optimized, in the case of a jpeg/png, or edited (i.e., fonts removed in Adobe) in the case of PDFs, they will upload successfully. I have not been able to discover exactly what characteristic is causing the files to fail. We are able to successfully submit larger and smaller files, and as mentioned before, some with identical content.
I can add some additional server side logic if necessary, I am not sure that the problem is related to that however.
For anyone reading this in the future, we made some progress with our implementation today, but we are no closer to understanding the bug.
We had another file upload section that would send similar files (new Formdata(), form.append()) as an array. We discovered that when we send our file that typically triggers the CORS error, in the second position, it won't trigger the error.
So now we send [ null, file ]. If we send [ file ] or [ file, null ] it will still trigger the error. I hope this helps anyone, or if you have further insight for us, I look forward to understanding what exactly is happening here. Thanks!
I want to get the response headers of a cached response inside a service worker. The purpose of this is so that I can read a custom header called 'Modified' to see if it is necessary to fetch a new copy of the data by comparing it to the response headers of a 'HEAD' fetch for the same URL.
On install of the service worker, I populate a cache called v1::fundamentals with some responses. I then register a fetch listener which looks for the request in the cache and if its there, serves it. I then want to async update the cache with non-stale content but only if the 'Modified' header contains a newer timestamp than the one in the cache. In the simplified code below, I try to access the headers with headers.get() but I always get a null in return. Why is this?
When I look at the cache in Chrome devtools, the headers are very much there, I just can't get to them from within the service worker.
self.addEventListener('fetch', event => {
console.log('%c[SW] Fetch caught: ', 'color: #42d9f4', event.request.url);
// Let the browser do its default thing for non-GET requests.
if (event.request.method != 'GET') {
return;
} else {
// Prevent the default, and handle the request ourselves.
event.respondWith(async function() {
// Try to get the response from a cache.
const cache = await caches.open('v1::fundamentals');
const cachedResponse = await cache.match(event.request);
if (cachedResponse) {
// Try to get the headers
var cacheDate = cachedResponse.headers.get('Modified');
// Print header, returns 'null'
console.log(cacheDate);
event.waitUntil(cache.add(event.request));
return cachedResponse;
}
return fetch(event.request);
}());
}
});
I have a website which I don't want to make people create accounts. It is a news feed with each news article categorized. I want to allow people to tag the categories they are interested in so that next time they go to the site it only shows news for the categories that are tagged.
I'm saving the tags in an indexedDB which I understand is available in a service worker.
Hence in my service worker I want to "intercept" requests to www.my-url.com, check the indexDB for what categories this person is interested in, and add some headers like 'x-my-customer-header': 'technology,physics,sports' so that my server can respond with a dynamic html of those categories only.
However I'm struggling to get the service worker to properly cache my root response. In my serviceworker.js, I console log every event.request for the onFetch handler. There are no requests that are related to my root url. I'm testing right now on my localhost, but I only see fetch requests to css & js files.
Here is my onFetch:
function onFetch(event) {
console.log('onFetch',event.request.url);
event.request.headers["X-my-custom-header"] = "technology,sports";
event.respondWith(
// try to return untouched request from network first
fetch(event.request).catch(function() {
// if it fails, try to return request from the cache
caches.match(event.request).then(function(response) {
if (response) {
return response;
}
// if not found in cache, return default offline content for navigate requests
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
return caches.match('/offline.html');
}
})
})
);
}
I'm using rails so there is no index.html that exists to be cached, when a user hits my url, the page is dynamically served from my news#controller.
I'm actually using the gem serviceworker-rails
What am I doing wrong? How can I have my service worker save a root file and intercept the request to add headers? Is this even possible?
Credit here goes to Jeff Posnick for his answer on constructing a new Request. You'll need to respond with a fetch that creates a new Request to which you can add headers:
self.addEventListener('fetch', event => {
event.respondWith(customHeaderRequestFetch(event))
})
function customHeaderRequestFetch(event) {
// decide for yourself which values you provide to mode and credentials
// Copy existing headers
const headers = new Headers(event.request.headers);
// Set a new header
headers.set('x-my-custom-header', 'The Most Amazing Header Ever');
// Delete a header
headers.delete('x-request');
const newRequest = new Request(event.request, {
mode: 'cors',
credentials: 'omit',
headers: headers
})
return fetch(newRequest)
}