I have been working on an application using SailsJS. Everything has been working fine until a couple of hours ago when I wasn't getting responses from socket.get(). I checked the network tab in chrome console and found that the last request is still pending.
Uri for request:
ws://localhost:1337/socket.io/1/websocket/FBcw-Q4H3PLFXcIlb0GB
Status code:
101 Switching Protocols
I have searched around and some people have been saying it is due to firewalls. So I disabled them but this didn't help. Some others have suggested defaulting to xhr polling so I did that but the same thing happens, a request to
http://localhost:1337/socket.io/1/xhr-polling/4_gAWYQJtKdXhDFEcxHe?t=1395536371612
just stays pending forever.
In response to the comment here is the request event:
socket.get('/session/create', {
username: username,
password: password
}, function (response) {
console.log(response) // null
});
The response is never returned and is always null in the callback. I even tried sending a response straight away and that isn't returned either. Example below:
// Session controller
module.exports = {
create: function (req, res) {
return res.json({foo: 'bar'}, 200);
}
}
Sorry, I have done some fiddling and I have found the issue (probably a silly mistake on my part).
I have shortcut urls enabled which in SailsJS means a request to /foo/bar will map to the FooController.bar
This worked fine until I added a wildcard route like the below example:
get /:foo/:bar
This is what seems to have broken the request. Since I was sending a request to /session/create and relied on shortcut routes without specifying an explicit route it was interpreted by the :foo/:bar route.
Lesson learned: Disable shortcut URLs and use proper routing.
Related
I'm working on a react-native app with spotify integration. I've set up the oAuth flow w/ auth code grant where I can get the authorization code. I've then set up cloud function on firebase to proxy the actual token exchange (I don't want to reveal my secret to the client!). I've added logs and can see that the function is correctly completing the exchange with the spotify token endpoint, and receiving a refresh and access token.
const tokenRequeset = functions.https.onCall(async (data, context) => {
// spotify network request, error handling, etc here ....
// I want to emphasize that this network request completes
// properly - my log statement below verifies in server logs
// that I'm getting the expected value.
const resp = await axios.post(
"https://accounts.spotify.com/api/token",
QueryString.stringify({
grant_type: "authorization_code",
code: code,
redirect_uri: redirectURI,
}),
{
headers: {
"Authorization": `Basic ${BEARER_TOKEN}`,
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
console.log(resp.data.access_token);
return { status: "success", token: resp.data.access_token };
});
export default tokenRequest
resp.data.access_token is the JWT access token used to hit the spotify API - it's a string value according to the API. (I'd provide an example one, but it is an auth token)
However, when I try to use the firebase/functions package to call my function from my app, I will sometimes get a 'FirebaseError: Response is not valid JSON object.'
What makes this extra fun is that it's inconsistent - yesterday I had the issue, and then it went away (without changing my code!). I was able to hit both the local emulator function and then the deployed function no problem, but today the 'FirebaseError: Response is not valid JSON object.' error is back.
I have checked the logs for the failed invocations both locally and on the deployed function, and in both cases the spotify API call is working - I'm getting all the expected behavior right up until the return (which isn't working for some reason).
On the client side, I'm configuring firebase like so:
const firebaseConfig = {
// Shhhhhh
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const functions = getFunctions(app);
// Uncomment to run locally
connectFunctionsEmulator(functions, "localhost", 5001);
export { app, functions };
And then exposing and calling it like so:
const SpotifyAuth = httpsCallable(functions, "spotify-auth");
const resp = await SpotifyAuth(code, redirectURI)
(I know this isn't full code - I grabbed the relevant portions. Happy to provide more if needed).
I tried looking up this error, and I found results from ~2018/2020 with the old style of firebase/functions, but they seem to be related to region and I'm deployed in the default us-central1 - according to the SDK documentation that means I shouldn't touch it.
The existing solutions to the problem also seem to be based on the old style of function calls, rather than the more recent httpsCallable() and getFunctions(app).
I'm going insane trying to figure out why sometimes I'm getting this error
EDIT:
More information on the error - I ran my code again today and didn't see the error locally, but I DID see it when I hit the deployed function.
Again, I want to emphasize that I think the error is in the firebase network response - if you look at the network request I receive a 200 but the response is empty.
Did an additional full mockup of a function to see what would happen:
const test = functions.https.onCall((data, context) => {
console.log("function call");
return { status: "success", token: "asdfasdfasdfasdfasfs" };
});
export default test;
I'm getting the same error.
UPDATE:
I've given up on using the sdk and onCall method for firebase cloud functions - all of my testing thus far indicates that this is a bug or error on the google cloud function side, and there's nothing I can do from my side.
The good news is the onRequest approach seems to not have this issue - it's behaving properly and reliably.
I really hope that I've messed up along the way and there's a solution I've missed - the SDK seems fantastic and I like the integration it (is supposed to) offer, but as far as I'm aware right now unless there's a bug fix (or update to the documentation if I'm doing something wrong) it seems like it simply won't work.
I'm still planning on using firebase, but from my experience thus far I'd advise anyone early in their server work to consider using another offering (at least if you need to use the functions - I was able to get storage working).
I have encountered baffling behavior when running an Ajax request as part of my Flask application. I have written a handler receive a div click and then send an Ajax request with certain data to a specific route specified in my app.py. The data is then inserted into a database. While this approach worked fine when running my Flask app on my own machine, upon moving my app to another hosting service (Pythonanywhere), every time I click the div, the request is being sent twice, as evidenced by the data being inserted twice into the database.
Similar variants of this question have been asked before (here and here, for instance), but those questions all deal with POST requests, while mine is using a GET. Additionally, those questions generally involved an HTML form that was being submitted alongside the POST request, hence the additional request. However, my code does not have any forms.
My code sample (simplified, but the same in essence to my current efforts):
In frontend.html:
<div class='wrapper'>
<div class='submit_stamp' data-timestamp='2019-8-2'>Submit</div>
</div>
In frontend.js:
$('.wrapper').on('click', '.submit_stamp', function(){
$.ajax({
url: "/submit_time",
type: "get",
data: {time: $(this).data('timestamp')},
success: function(response) {
$('.wrapper').append(response.html);
},
});
});
In app.py:
#app.route('/submit_time')
def submit_time():
db_manager.submit_stamp(flask.request.args.get('time'))
return flask.jsonify({'html':'<p>Added timestamp</p>'})
As such, whenever I click the submit_stamp element, the Ajax request fires twice, the timestamp is inserted twice into my database, and "Added timestamp" is appended twice to .wrapper. Some things I have done to fix this include:
Adding an event.stopPropagation() in the handler
Using a boolean flag system where a variable is set to true just after the click, and reset to false in the success handler of the .ajax. I wrapped the $.ajax with this boolean in a conditional statement.
None of these patches worked. What confuses me, however, is why $.ajax is called once when running on my machine, but is called twice when running on the hosting service. Does it have to do with the cache? How can I resolve this issue? Thank you very much!
Edit:
Strangely, the duplicate requests occur infrequently. Sometimes, only one request is made, other times, the requests are duplicated. However, I have checked Network XHR output in Chrome and it is only displaying the single request header.
The access log output (with IPs removed):
<IP1> - - [05/Aug/2019:16:35:03 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 76 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1" "<IP>" response-time=0.217
<IP2> - - [05/Aug/2019:16:35:05 +0000] "GET /submit_time?payload=.... HTTP/1.1" 200 71 "http://www.katchup.work/create" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" "<IP2>" response-time=0.198
With your latest update, I'd have to say this isn't a duplicate request. With your log saying one request was from Mozilla on a Windows based machine, and the other request coming from Chrome on a Mac, it's simply 2 different requests coming from two different locations that happen to be close to each other in time. Even if it was a test from a virtual machine, it shouldn't record the multiple OSs or browsers, as VM will take care of all translations, preventing confusion like this.
You don't include IP addresses, but if they are public addresses (as in something other than 127.x.x.x, 10.x.x.x, or 192.x.x.x) then they are definitely two different users that happen to be using your software at the same time.
If you are tracking that it's the same user, it might simply be them using your software on 2 different devices (such as a desktop vs mobile phone). If that's not allowed, then make sure their access reflects this. If it can be tracked through DNS to different geographical locations, you might have a compromised account to lock down until the real user can confirm their identity.
However you slice it, with the new data, I don't think it's actually your software, unless you can reproduce it through testing even somewhat reliably. Take time to consider that it might just Not be a bug, but something else. Software devs are conditioned to think everything is a bug and their fault, when it could be something benign or a malicious attack that might not have been previously considered.
Good luck and hopefully I gave you something to think about!
Thank you to everyone who responded. Ultimately, I was able to resolve this issue with two different solutions:
1) First, I was able to block the offending request by checking the IP in the backend:
#app.route('/submit_time')
def submit_time():
_ip = flask.request.environ.get('HTTP_X_REAL_IP', flask.request.remote_addr)
if _ip == '128.177.108.218':
return flask.jsonify({'route':'UNDEFINED-RESULT'.lower()})
return flask.jsonify({"html":'<p>Added timestamp</p>'})
The above is really more of a temporary hack, as there is no guarantee the target IP will stay the same.
2) However, I discovered that running on HTTPS also removed the duplicate request. Originally, I was loading my app from the Pythonanywhere dashboard, resulting in http://www.testsite.com. However, once I installed a proper SSL certificate, refreshed the page, and ran the request again, I found that the desired result was produced.
I am awarding the bounty to #computercarguy as his post prompted me to think about the external/network related reasons why my original attempt was failing.
Very unusual solution, but it should work (If not, I think the problem can't be solved with js.)
EDITED: Check the sent ID in the ajax request! (So check on server side!) This is sure will be a unique id, so, you can test with this #computercarguy has right or not.
let ids = []
function generateId(elem) {
let r = Math.random().toString(36).substring(7)
while ($.inArray(r, ids) !== -1) {
r = Math.random().toString(36).substring(7)
}
ids.push(r)
elem.attr("id", r)
}
$(document).ready(function() {
$(".wrapper").find(".submit_stamp").each(function() {
generateId($(this))
})
console.log(ids)
});
function ajaxHandler(stampElem, usedId) {
let testData = new FormData()
testData.append("time", stampElem.data('timestamp'))
testData.append("ID", usedId)
$.ajax({
url: "/submit_time",
type: "get",
data: testData,
success: function(response) {
$('.wrapper').append(response.html);
generateId(stampElem);
if (stampElem.attr("id").length) {
console.log("new id:"+stampElem.attr("id"));
}
},
});
}
$(".wrapper").on("click", ".submit_stamp", function(ev) {
ev.preventDefault()
ev.stopImmediatePropagation()
if ($(this).attr("id").length) {
let id = $(this).attr("id")
$("#"+id).one("click", $.proxy(ajaxHandler, null, $(this), id))
$(this).attr("id", "")
}
});
So first of all I would use below syntax as a personal preference
$('.wrapper').click(function (event) {
event.stopImmediatePropagation();
$.ajax({
url: "/submit_time",
type: "get",
data: {
time: $(this).data('timestamp')
},
success: function (response) {
$('.wrapper').append(response.html);
},
});
});
Also as I said, you need to make sure when you refer to two concurrent request, they are indeed from same IP+client, else you may be confusing between parallel request from different places to be repeated as such
Little change in your js file.
$('.wrapper').on('click', '.submit_stamp', function(event){
event.preventDefault();
event.stopImmediatePropagation();
$.ajax({
url: "/submit_time",
type: "get",
data: {time: $(this).data('timestamp')},
success: function(response) {
$('.wrapper').append(response.html);
},
});
});
I'm using the npm request library and am running into an issue where the request is never sent if I call express's res.send() after calling request. I realize the request callback won't fire if I close the connection, but I'm not even seeing the request being sent in the first place.
This code is being executed on RunKit (formerly TonicDev), an online code editor that allows code execution via endpoints. I'm not seeing this issue on my local machine, so it seems like it may have to do with RunKit. Anyone have any ideas as to what's going on here or how I might work around this?
You can execute the code yourself by going to:
https://runkit.com/gragland/58056bc6e9d9ed00130c84d5 and clicking the endpoint link at the top.
// Helper to return a RunKit compatible express app (runkit.com/tonic/express-endpoint)
var tonicExpress = require("#runkit/tonic/express-endpoint/1.0.0")
// Provide the exports object to the tonicExpress helper
var app = tonicExpress(module.exports)
var request = require('request')
app.get("/", function(req, res){
var request_number = 9
request({
// To see if request is sent go to: https://requestb.in/1coqbqn1?inspect
url: 'http://requestb.in/1coqbqn1',
method: 'POST',
json: {
request_number: request_number,
message: 'hello'
}
})
// The line below has to be commented out for the above request to be sent
// I don't care about the request callback() firing, I just want the request to be sent
res.send('Done')
})
I have an error reporting beacon I created using Google Apps script and it is published to run as myself and to be accessible to "anyone, even anonymous," which should mean that X-domain requests to GAS are allowed.
However, my browsers are now indicating there is no Access-Control-Allow-Origin header on the response after the code posts to the beacon.
Am I missing something here? This used to work as recently as two months ago. So long as the GAS was published for public access, then it was setting the Access-Control-Allow-Origin header.
In Google Apps Script:
Code.gs
function doPost(data){
if(data){
//Do Something
}
return ContentService.createTextOutput("{status:'okay'}", ContentService.MimeType.JSON);
}
Client Side:
script.js
$.post(beacon_url, data, null, "json");
When making calls to a contentservice script I always have sent a callback for JSONP. Since GAS does not support CORS this is the only reliable way to ensure your app doesn't break when x-domain issues arrive.
Making a call in jQuery just add "&callback=?". It will figure everything else out.
var url = "https://script.google.com/macros/s/{YourProjectId}/exec?offset="+offset+"&baseDate="+baseDate+"&callback=?";
$.getJSON( url,function( returnValue ){...});
On the server side
function doGet(e){
var callback = e.parameter.callback;
//do stuff ...
return ContentService.createTextOutput(callback+'('+ JSON.stringify(returnValue)+')').setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I've lost a couple of hours with the same issue. The solution was trivial.
When you deploy the script as webapp, you get two URLs: the /dev one and the /exec one. You should use /exec one to make cross domain POST requests. The /dev one is always private: it requires to be authorized and doesn't set *Allow-Origin header.
PS.: The /exec one seems to be frozen — it doesn't reflect any changes of code until you manually deploy it with a new version string (dropdown list in deploy dialog). To debug the most recent version of the script with the /dev URL just install an alternative browser and disable it's web-security features (--disable-web-security in GoogleChrome).
Just to make it simpler for those who are only interested in a POST request like me:
function doPost(e){
//do stuff ...
var MyResponse = "It Works!";
return ContentService.createTextOutput(MyResponse).setMimeType(ContentService.MimeType.JAVASCRIPT);
}
I stumbled upon the same issue:
calling /exec-urls from the browser went fine when running a webpage on localhost
throws crossorigin-error when called from a https-domain
I was trying to avoid refactoring my POST JSON-clientcode into JSONP (I was skeptical, since things always worked before).
Possible Fix #1
Luckily, after I did one non-CORS request (fetch() in the browser from a https-domain, using mode: no-cors), the usual CORS-requests worked fine again.
last thoughts
A last explanation might be: every new appscript-deployment needs a bit of time/usage before its configuration actually settled down at server-level.
Following solution works for me
In Google Apps Script
function doPost(e) {
return ContentService.createTextOutput(JSON.stringify({status: "success", "data": "my-data"})).setMimeType(ContentService.MimeType.JSON);
}
In JavaScript
fetch(URL, {
redirect: "follow",
method: "POST",
body: JSON.stringify(DATA),
headers: {
"Content-Type": "text/plain;charset=utf-8",
},
})
Notice the attribute redirect: "follow" which is very very important. Without that, it doesn't work for me.
I faced a similar issue of CORS policy error when I tried to integrate the app script application with another Vue application.
Please be careful with the following configurations:
Project version should be NEW for every deployment.
Execute the app as me in case you want to give access to all.
Who has access to the app to anyone, anonymous.
Hope this works for you.
in your calling application, just set the content-type to text/plain, and you will be able to parse the returned JSON from GAS as a valid json object.
Here is my JSON object in my google script doPost function
var result = {
status: 200,
error: 'None',
rowID: rowID
};
ws.appendRow(rowContents);
return ContentService.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
and here I am calling my app script API from node js
const requestOptions = {
method: 'POST',
headers: {'Content-Type': 'text/plain'},
body: JSON.stringify({param1: value, param2:value})
};
const response = await fetch(server_URL, requestOptions);
const data = await response.json();
console.log(data);
console.log(data.status);
My case is different, I'm facing the CORS error in a very weird way.
My code works normally and no CORS errors, only until I added a constant:
const MY_CONST = "...";
It seems that Google Apps Script (GAS) won't allow 'const' keyword, GAS is based on ES3 or before ES5 or that kind of thing. The error on 'const' redirect to an error page URL with no CORS.
Reference:
https://stackoverflow.com/a/54413892/5581893
In case this helps all any of those people like me:
I have a .js file which contains all my utility functions, including ones which call a GAS. I keep forgetting to clear my cache when I go to test updates, so I'll often get this kind of error because the cached code is using the /dev link instead of the /exec one.
I need to do a POST method inside a firefox add-on to another server, I have been trying to use different ways, and after googling I found out that I should use the Request module from the SDK inside my main.js.
I am using firefox v 23
I tried using the chrome module
var xmlhttp = chrome.Cc["#mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(chrome.Ci.nsIXMLHttpRequest);
but I got NS_ERROR_FAILURE. I even added the permissions in the package.json
"permissions": {
"cross-domain-content": ["https:[some url]"]
}
But it still gives the same error.
I then used the Request module but didn't work so far. I tried a GET method with the Request module and it works fine. But the POST method always returns a 0 status and an empty response.
I tried doing the same request via a browser http client and it worked fine!! But through the code inside the add-on it always returns a 0.
The request sets headers and of course has a payload.
var contentObject = {[Valid JSON Object]};
var myRequest = Request({
url: "https://[some url]",
headers: {
"pragma": "no-cache"
},
content: contentObject,
contentType: "application/json",
onComplete: function (response) {
console.log("Status: " + response.status);
console.log("Response json: " + JSON.stringify(response));
}
}).post();
Your support is highly appreciated. There are very few resources I found over the internet about this issue and non of them solved my problem.
I guess the server script expects a JSON string representation of the contentObject. But this is not how objects are treated by the request module, they are turned to key/value pairs.
So change
content: contentObject
to
content: JSON.stringify(contentObject)
the POST method always returns a 0 status and an empty response
This might not be direct answer, but I had the same problem last couple of days. A friend who was connected to network via different provider tried the same code and it worked fine. Also, if I remember correctly, I could connect to the port 80 but not to the port where I was sending POST request so that port might be blocked on the network you are connected.