I am setting up a new VueJS-Project and implemented a workbox-backgroundSync in my progressive web application. To test this workbox plugin i need to disconnect my device (chrome) from the internet and fire a POST-request. Under chrome this request got a state of pending for couple of minutes, even with no internet connection. So my application is waiting to the request response.
My POST-request:
axios
.post('/createrecipe', recipe)
.then(response => {
commit('createRecipes', recipes);
commit('setSnackbar', { text: "Recipe was created. ( " + response.data.status + response.status + " )", color:'success', snack:true });
console.log("Response from Server for create Recipe: ", response);
})
.catch(error => {
commit('setSnackbar', { text: "Your Post will be stored and send by Internet-Connection", color: 'warning', snack: true });
console.log(error)
});
My ServiceWorker:
const matchCb = ({url, event}) => {
return (url.pathname === '/api/createrecipe');
};
const bgSyncPlugin = new workbox.backgroundSync.Plugin('createdRecipes-post-storage-offline', {
maxRetentionTime: 24 * 60, // Retry for max of 24 Hours
});
workbox.routing.registerRoute(
matchCb,
workbox.strategies.networkOnly({
plugins: [bgSyncPlugin]
}),
'POST'
);
I interrupted the network in Chrome-devtool under the network tab and the background process went through successfully. But this is not the best solution for the users, because they dont know how to control the devtool.
I expect that the request will be canceled, while chrome has no internet connection.
Here is a screenshot of the request in a state of pending instead of failed:
enter image description here
Side Note: My web application runs under localhost.
Not the best solution but my mate Ilja KO found a workaround. I just set a timeout with the instance of axios to abort the requests, even if chrome got a internet-connection or not.
axios.defaults.timeout = 5000;
Related
I'm trying to make socket.io-client work in a svelte front end app to talk to an existing API server that already uses socket.io. After a number of challenges, I managed to make this work but I can only get this to work with sveltekit's preview and not in dev mode. Wondered if someone with some knowledge of those could explain why or suggest what I need to do to get it connecting in dev?
svelte 3.34.0
sveltekit next-169
socket.io(-client) 4.2.0
basic code as follows, currently within a file $lib/db.js where I define a few stores that are pulled into the layout for general use..
import { io } from "socket.io-client";
import { browser } from '$app/env';
const initSocket = async () => {
console.log('creating socket...');
let socket = io('http://192.168.1.5:4000', { 'connect timeout': 5000 });
socket.on("connect", () => {
// always works in preview...
console.log('socket created with ID:', socket.id);
});
socket.on("connect_error", (error) => {
// permanently fired in dev...
console.error('Failed to connect', error);
});
socket.on("error", (error) => {
console.error('Error on socket', error);
});
socket.on("foo", data => {
// works in preview when server emits a message of type 'foo'..
console.log("FOO:", data);
});
};
if (browser) {
initSocket();
}
// stores setup and exports omitted..
with svelte-kit preview --host I see the socket creation log message with the socket ID and the same can be seen on the api server where it logs the same ID. The socket works and data is received as expected.
with svelte-kit dev --host however, the log message from socket.on("connect").. is never output and I just see an endless stream of error messages in the browser console from the socket.on("connect_error").. call..
Failed to connect Error: xhr poll error
at XHR.onError (transport.js:31)
at Request.<anonymous> (polling-xhr.js:93)
at Request.Emitter.emit (index.js:145)
at Request.onError (polling-xhr.js:242)
at polling-xhr.js:205
Importantly, there is no attempt to actually contact the server at all. The server never receives a connection request and wireshark/tcpdump confirm that no packet is ever transmitted to 192.168.1.5:4000
Obviously having to rebuild and re-run preview mode on each code change makes development pretty painful, does anyone have insight as to what the issue is here or suggestions on how to proceed?
I've had a similar problem, I solved it by adding this code to svelte.config.js:
const config = {
kit: {
vite: {
resolve: {
alias: {
"xmlhttprequest-ssl": "./node_modules/engine.io-client/lib/xmlhttprequest.js",
},
},
},
},
};
The solution was provided by this comment from the vite issues.
My app generates a custom product page on a Shopify store. I use Vue3 for the frontend. There are other apps running js on the page, e.g. live chat, push notification pop-up, GDPR cookie bar, etc. They are injected by the platform and I can't remove them. (Btw, these js are minified and hard to read)
My app has an add bundle to cart button on the floating footer to send a POST request to my server with Fetch API. But it's blocked by these irrelevant apps. I think these apps are monitoring if a POST / GET request is sent. They assume they are working on standard product pages but not custom one like mine.
I tried to implement a block list with yett. But this passive way is not good enough. It's just a fix after the issue happens. Any way I can protect my fetch request without interfering by other js scripts?
let request = new Request('/create_new_product/', {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
});
let vm1 = this;
fetch(request)
.then(response => response.json())
.then(data => {
console.log('Success creating variant:', data);
console.log('variant_id:', data.variant_id);
// stopped here by other apps :-(
if (data.variant_id) {
vm1.addNewVariantToCart(this.variants, data.variant_id);
vm1.$emit('clearall');
setTimeout(function(){ vm1.isLoading = false; }, 2000);
}
else if (data.Error) {
alert(data.Error);
vm1.isLoading = false;
}
})
.catch((error) => {
console.error('Error:', error);
vm1.isLoading = false;
});
I'm attempting to store a session cookie after a successful firebase-authentication login. All the firebase functions appear to be executing correctly. I'm able to retrieve my idToken after logging. The Post correctly sends this idToken to the server where it is validated. However, I'm getting stuck on the last step which is to store the session cookie in my success function, which doesn't execute.
To troubleshoot this I go to the Chrome Dev Tools and access the network tab and turn on preserve log. Here I can see my post request. When I click on response it says "Failed to load response data". This at least tells me that the post is partially working. The client is at a minimum sending the idToken, but I can't verify that the response is being sent from the server. Additionally, when I navigate to the Application tab of the chrome dev tools and click refresh I don't see my session cookie there.
Here is the client side code:
btnLogin.addEventListener('click', e => {
// Get eamil and pass
// TODO: Check for real email
const email = txtEmail.value;
const pass = txtPassword.value;
// As httpOnly cookies are to be used, do not persist any state client side.
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
// Sign in
firebase.auth().signInWithEmailAndPassword(email,pass).then((userCredential) => {
// Get the user's ID token as it is needed to exchange for a session cookie.
userCredential.user.getIdToken().then((idToken) => {
// Session login endpoint is queried and the session cookie is set.
// TODO: Add CSRF protection);
$.ajax({
type: 'post',
//accepts: 'json',
data: {idToken: idToken},
//dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
success: function(data) {
console.log('MADE IT TO THE SUCCESS FUNCTION');
//document.cookie = data;
firebase.auth().signOut();
$('#loginForm').before(' \
<div class="alert alert-secondary" role="alert"> \
The login was succesful! \
</div> \
');
//setTimeout(window.location.assign('/admin-manage'), 5000);
},
error: function() {
console.log('post error');
$('#loginForm').before(' \
<div class="alert alert-danager" role="alert"> \
The idToken post failed! \
</div> \
');
},
timeout: 5000
});
});
});
});
Here is the server side code:
/* POST FOR SESSION INITIALIZATION */
app.post('/admin-login', (req, res) => {
// Get the ID token passed and the CSRF token.
var idToken = req.body.idToken.toString();
// TODO: CSRF TOKEN
// Set session expiration to 2 days.
const expiresIn = 60 * 60 * 24 * 2 * 1000;
// Create the session cookie. This will also verify the ID token in the process.
// The session cookie will have the same claims as the ID token.
// We could also choose to enforce that the ID token auth_time is recent.
adminApp.auth().verifyIdToken(idToken).then((decodedIdToken) => {
console.log('ID TOKEN: ' + idToken);
// Only process if the user just signed in the last 5 minutes
if (new Date().getTime() / 1000 - decodedIdToken.auth_time < 5 * 60) {
// Create session cookie and store it serverside
return adminApp.auth().createSessionCookie(idToken, {expiresIn});
}
else {
throw new error('The IDToken has expired');
}
})
// eslint-disable-next-line promise/always-return
.then((sessionCookie) => {
console.log(sessionCookie);
//Set session cookie on client side
const options = {maxAge: expiresIn, httpOnly: false, secure: false};
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
})
.catch((error) => {
console.log(error);
res.status(401).send();
});
});
EDIT
Here is an image of the network view from me running chrom dev tools.
CHROME DEV TOOLS APPLICATION VIEW
As you can see the post script gets canceled.
Here is a detailed view of the request headers:
enter image description here
EDIT #2
I've made so more changes to the server-side code and still haven't seen any change in results. Here is the new server-side code:
/* POST FOR SESSION INITIALIZATION */
app.post('/admin-login', (req, res, next) => {
// Get the ID token passed and the CSRF token.
var idToken = req.body.idToken.toString();
// TODO: CSRF TOKEN
// Set session expiration to 2 days.
const expiresIn = 60 * 60 * 24 * 2 * 1000;
// Create the session cookie. This will also verify the ID token in the process.
// The session cookie will have the same claims as the ID token.
// We could also choose to enforce that the ID token auth_time is recent.
adminApp.auth().verifyIdToken(idToken).then((decodedClaims) => {
console.log('DECODED CLAIMS AUD: ' + decodedClaims.uid );
// Only process if the user just signed in the last 5 minutes
if (new Date().getTime() / 1000 - decodedClaims.auth_time < 5 * 60) {
// Create session cookie and store it serverside
// eslint-disable-next-line promise/no-nesting
adminApp.auth().createSessionCookie(idToken, {expiresIn}).then((sessionCookie) => {
console.log(sessionCookie);
//Set session cookie on client side
const options = {
maxAge: expiresIn,
httpOnly: true,
secure: false
};
res.set('Content-Type', 'application/json');
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
});
}
else {
throw new error('The IDToken has expired');
}
}).catch((error) => {
console.log(error);
res.status(401).send('UNAUTHROIZED REQUEST!');
});
});
I also took a wireshark capture on my loopback adapter so that I could capture the localhost traffic. I can see the HTTP response with the set-cookie tag, but not sure what the problem is. See the image below:
HTTP Post Response Wireshark Capture
EDIT #3
I've finally made a little progress. I noticed the URI in my wireshark capture wasn't "samesite" so I added the "sameSite: 'None'" option to my set-cookie options.
So my new options are:
const options = {
maxAge: expiresIn,
httpOnly: true,
secure: false,
sameSite: 'None'
};
This fixed the issue on my localhost development environment. However, I changed the secure option to true then did a firebase deploy. After the deployment I logged into the production environment and found that the cookie would not generate. So the only question left is why it won't work in the production environment.
EDIT #4
Alright, I'm losing my mind. I changed the options back to how I had them in EDIT #3 so that I could continue working the app, and the session cookie is no longer being set again. I'm at a complete loss as to why it won't work now. I even went into chrome to clear all browsing history in hopes this would resolve the issue, but no luck. If anyone can shed light on what is happening here I would greatly appreciate it.
I'm using service worker for push notifications, following this article. Everything is working with Chrome but with Firefox (v.44.0.2) I have a weird issue.
On successful login to my app, I register the service worker which does nothing but waiting for push events; I see that is correctly registered (from some logging and from about:serviceworkers). Now, if I refresh the page (CTRL+R) all my POST have CORS issues (missing Access-Control-Allow-Origin header) due to this service worker and the user is redirected to login page; from here on all POSTs do not work for the same reason.
Conversely, if I login, unregister the service worker and then refresh, there are no problems at all. Any idea of what's going on? Again my service worker just handles push events, no caching no other processing done and it perfectly works on Chrome.
Here's my service worker code ( SOME_API_URL points to a real API which is not needed for testing purpose cause the issue happens after the service worker registers, no push events needed)
self.addEventListener('push', function(event) {
// Since there is no payload data with the first version
// of push messages, we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(SOME_API_URL).then(function(response) {
if (response.status !== 200) {
// Either show a message to the user explaining the error
// or enter a generic message and handle the
// onnotificationclick event to direct the user to a web page
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var title = data.notification.title;
var message = data.notification.message;
var icon = data.notification.icon;
var notificationTag = data.notification.tag;
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this push message';
var notificationTag = 'notification-error';
return self.registration.showNotification(title, {
body: message,
tag: notificationTag
});
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
// Android doesn't close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(
clients.matchAll({
type: 'window'
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});
Firefox 44 has bug 1243453, which causes the Origin header of cross-origin requests to get dropped if the service worker doesn't listen for fetch events.
The bug has been fixed in Firefox 45, which will be released the week of March 8, 2016 (next week, as of the time of this writing). In the meantime, and for users who don't immediately upgrade to the latest Firefox release, you can work around the problem by adding this code to the service worker:
addEventListener('fetch', function(evt) {
evt.respondWith(fetch(evt.request));
});
I'm going crazy trying to figure out what's wrong with my system that it is unable to send websocket messages. I've tried a ton of things:
Chrome 32, firefox 27, ie 10
various websocket server libraries: ws, websocket, nodejs-websocket
running the server on windows and centos
reinstalling node.js (to version 0.10.26)
reinstalling firefox
Turning off my firewall
The behavior is always the same: both client and server say they get a connection, but when the client tries to send a message, the other one simply doesn't get it. Sometimes i get "Error: read ECONNRESET" on the server - but not all the time. About 50% of the time it just fails silently. Eventually the connection closes.
If I add code to send a message from the server on connection, it always gets "Error: read ECONNRESET".
What does work?
the exact same code and setup on my laptop
any websocket code online, for example this: http://www.websocket.org/echo.html
So what could cause just my local websocket connection to silently drop messages?
Server:
var
ws = require('websocket').server
var http = require('http')
var server = http.createServer(function(request, response) {
console.log("connection received for "+request.url)
response.writeHead(404)
response.end()
})
server.listen(8080, function() {
console.log("now listening")
})
wsServer = new ws({httpServer:server, autoAcceptConnections: false})
wsServer.on('request', function(request) {
console.log("got request")
var connection = request.accept('a', request.origin)
console.log("got connection")
connection.on('message', function(message) {
console.log("got message: "+message)
})
connection.on("close", function() {
console.log("closed")
})
connection.on('error', function(e) {
console.log('Error: '+e)
})
})
Output:
got connection
closed
Client:
<script>
var ws = new WebSocket('ws://localhost:8080/', 'a')
ws.onopen = function() {
console.log('open')
ws.send('something2')
}
ws.onerror = function(e) {
console.log(e)
}
ws.onmessage = function(m) {
console.log(m)
}
ws.onclose = function() {
console.log('closed')
}
</script>
Output
open
closed
This is driving me crazy.