Dart AppEngine Headers already sent when using Futures - google-app-engine

I am using AppEngine and wrote a server application in Dart. Since the gcloud API is using Futures for pretty much everything, I make use of them as well. The problem however, once I start using Futures, a BadStateException is thrown. See the two examples below.
Working server
runAppEngine((HttpRequest request) {
request.response
..writeln('Hello world')
..statusCode = HttpStatus.OK
..close();
}, port: port);
Output:
Hello world
Not working server: Headers already sent
Future _asyncTask(HttpRequest request) {
return new Future(() {
request.response
..writeln('Hello world')
..statusCode = HttpStatus.OK; // causes BadStateException
}).catchError((e) {
request.response.writeln(e.toString());
}).whenComplete(() {
request.response.close();
});
}
runAppEngine((HttpRequest request) {
return _asyncTask(request);
}, port: port);
Output:
Hello world Bad state: HTTP headers were already sent.
I would like to use Futures but for some reasons, I just cannot get rid of the BadStateException. Can someone point me to what I am doing wrong?

Oh well, seems like I found the bug. I was looking for a fix to this problem all day and now that I ask here, I find the problem five minutes later.
Anyhow, I hope this answer helps people with the same problem.
The problem is that with writing to the response, headers are set implicitly. Hence, once you call writeln(String) you cannot set any headers afterwards anymore.
The reason, that there is no error logged in the first server code is, that there is simply no logic to log errors, but it is as wrong as the second one.
Here is the correct way of doing it:
Future _asyncTask(HttpRequest request) {
return new Future(() {
request.response
..statusCode = HttpStatus.OK
..writeln('Hello world');
}).catchError((e) {
request.response.writeln(e.toString());
}).whenComplete(() {
request.response.close();
});
}
runAppEngine((HttpRequest request) {
return _asyncTask(request);
}, port: port);
As you can see, the problem is fixed by sending the headers first.

Related

Cypress testing and waiting for axios requests

Can anyone explain how or why my test isn't waiting for my data from an axios request before moving on? I'm completely new to this but have most simple stuff worked out but can't seem to navigate the docs to find where i'm going wrong.
Here's the relevant info..
cy.get('.day').eq(4).click() //Change the day
cy.route('/api/practice/available-slots').as('apiCheck') //Get available slots for that day
cy.wait('#apiCheck') //Wait for the days available slots to be returned
So you can see below I click the fourth day and my post URL is showing and getting data like it normally does but then my wait function throws that error. I like to think i'm close but as I said i'm new and not entirely sure what's going wrong. Thanks
For what it's worth here's the axios request:
axios
.post(this.props.reqProto + this.props.reqHost + '/api/practice/available-slots', {
startDate: this.state.appointmentSlotsDate,
})
.then((res) => {
....
}
})
Thanks Hiram,
That was one issue. Also the order of my code was incorrect. I need to allow cypress to anticipate the POST request instead of it trying to double back to it. This seems to work
cy.route({
method: 'POST',
url: '/api/practice/available-slots',
}).as('apiCheck')
cy.get('.day').eq(4).click()
cy.wait('#apiCheck')

Request Deferrer with Service Worker in PWA

I am making a PWA where users can answer the forms. I want it to make also offline, so when a user fills out a form and does not have the internet connection, the reply will be uploaded when he is back online. For this, I want to catch the requests and send them when online. I wanted to base it on the following tutorial:
https://serviceworke.rs/request-deferrer_service-worker_doc.html
I have managed to implement the localStorage and ServiceWorker, but it seems the post messages are not caught correctly.
Here is the core function:
function tryOrFallback(fakeResponse) {
// Return a handler that...
return function(req, res) {
// If offline, enqueue and answer with the fake response.
if (!navigator.onLine) {
console.log('No network availability, enqueuing');
return;
// return enqueue(req).then(function() {
// // As the fake response will be reused but Response objects
// // are one use only, we need to clone it each time we use it.
// return fakeResponse.clone();
// });
}
console.log("LET'S FLUSH");
// If online, flush the queue and answer from network.
console.log('Network available! Flushing queue.');
return flushQueue().then(function() {
return fetch(req);
});
};
}
I use it with:
worker.post("mypath/add", tryOrFallback(new Response(null, {
status: 212,
body: JSON.stringify({
message: "HELLO"
}),
})));
The path is correct. It detects when the actual post event happens. However, I can't access the actual request (the one displayed in try or fallback "req" is basically empty) and the response, when displayed, has the custom status, but does not contain the message (the body is empty). So somehow I can detect when the POST is happening, but I can't get the actual message.
How to fix it?
Thank you in advance,
Grzegorz
Regarding your sample code, the way you're constructing your new Response is incorrect; you're supplying null for the response body. If you change it to the following, you're more likely to see what you're expecting:
new Response(JSON.stringify({message: "HELLO"}), {
status: 212,
});
But, for the use case you describe, I think the best solution would be to use the Background Sync API inside of your service worker. It will automatically take care of retrying your failed POST periodically.
Background Sync is currently only available in Chrome, so if you're concerned about that, or if you would prefer not to write all the code for it by hand, you could use the background sync library provided as part of the Workbox project. It will automatically fall back to explicit retries whenever the real Background Sync API isn't available.

Cortana ran into an issue

I have created a javascript application (aka UWA) in order to play with my Belkin wemo and then turn on or turn off the ligth with Cortana. The following function is well called but Cortana ends up with an issue. If I remove the call to the HTTP call, the program works fine. Who can tell me what's wrong with the following function because no more details are exposed unfortunately (of course in the real program is replaced with the right URL):
function setWemo(status) {
WinJS.xhr({ url: "<url>" }).then(function () {
var userMessage = new voiceCommands.VoiceCommandUserMessage();
userMessage.spokenMessage = "Light is now turned " + status;
var statusContentTiles = [];
var statusTile = new voiceCommands.VoiceCommandContentTile();
statusTile.contentTileType = voiceCommands.VoiceCommandContentTileType.titleOnly;
statusTile.title = "Light is set to: " + status;
statusContentTiles.push(statusTile);
var response = voiceCommands.VoiceCommandResponse.createResponse(userMessage, statusContentTiles);
return voiceServiceConnection.reportSuccessAsync(response);
}).done();
}
Make sure that your background task has access to the WinJS namespace. For background tasks, since there isn't any default.html, base.js won't be getting imported automatically unless you explicitly do it.
I had to update winjs to version 4.2 from here (or the source repository on git), then add that to my project to update from the released version that comes with VS 2015. WinJS 4.0 has a bug where it complains about gamepad controls if you try to import it this way (see this MSDN forum post)
Then I added a line like
importScripts("/Microsoft.WinJS.4.0/js/base.js");
to the top of your script's starting code to import WinJS. Without this, you're probably getting an error like "WinJS is undefined" popping up in your debug console, but for some reason, whenever I hit that, I wasn't getting a debug break in visual studio. This was causing the Cortana session to just hang doing nothing, never sending a final response.
I'd also add that you should be handling errors and handling progress, so that you can periodically send progress reports to Cortana to ensure that it does not time you out (which is why it gives you the error, probably after around 5 seconds):
WinJS.xhr({ url: "http://urlhere/", responseType: "text" }).done(function completed(webResponse) {
... handle response here
},
function error(errorResponse) {
... error handling
},
function progress(requestProgress) {
... <some kind of check to see if it's been longer than a second or two here since the last progress report>
var userProgressMessage = new voiceCommands.VoiceCommandUserMessage();
userProgressMessage.DisplayMessage = "Still working on it!";
userProgressMessage.SpokenMessage = "Still working on it";
var response = voiceCommands.VoiceCommandResponse.createResponse(userProgressMessage);
return voiceServiceConnection.reportProgressAsync(response);
});

XSockets do not connect from Firefox

I need to use web sockets for some interactions with the user. I have pretty much copypasted solution from here - http://xsockets.net/blog/angular-js-xsocketsnet and got an issue with Firefox (27.0.1).
When I try to make this call (TwoWayBinding is my XSockets controller, I'm using .NET MVC on host side):
var connect = function (url) {
var deferred = $q.defer();
socket = new XSockets.WebSocket(url);
socket.on(XSockets.Events.open, function (conn) {
$rootScope.$apply(function () {
deferred.resolve(conn);
});
});
return deferred.promise;
};
connect("ws://localhost:49200/TwoWayBinding").then(function (ctx) {
isConnected = true;
queued.forEach(function (msg, i) {
publish(msg.t, msg.d);
});
queued = [];
});
I always get an error from Firebug:
Firefox can't establish a connection to the server at ws://localhost:49200/TwoWayBinding.
this.webSocket = new window.WebSocket(url, subprotocol || "XSocketsNET");
The same code works well in Chrome, it gets connected and I'm getting messages sent from host. Mentioned methods are wrapped into angular service, but this all works, I do not think this should be a problem.
One thing I was able to figure out from Fiddler was this:
Chrome:
Result Protocol Host URL Body Caching Content-Type Process Comments Custom
3 200 HTTP Tunnel to localhost:49200 0 chrome:3976
Result Protocol Host URL Body Caching Content-Type Process Comments Custom
6 101 HTTP localhost:49200 /TwoWayBinding?XSocketsClientStorageGuid=5cf5c99aafd141d1b247ed70107659e0 0 chrome:3976
Firefox:
Result Protocol Host URL Body Caching Content-Type Process Comments Custom
1740 200 HTTP Tunnel to localhost:49200 0 firefox:1420
Result Protocol Host URL Body Caching Content-Type Process Comments Custom
1741 - HTTP localhost:49200 /TwoWayBinding -1 firefox:1420
Simply said - there is some additional parameter XSocketsClientStorageGuid in the response for Chrome which does not occur in the respose to FF. I'm not sure if that has any impact or if I'm completely wrong but will appreciate any advice if somebody experiences same issue.
Update:
It looks like the critical line is this one
socket = new XSockets.WebSocket(url);
as the socket is not created properly in Firefox. But I still not have the cause of this.
What version are you running on , did you make a new installation of XSockets.NET using the Nuget Package or did you use the git example mentioned in the quesion above?
I just did a test on FF 26.0 and 27.0.1 , and it did go well using this pice of example;
http://xsockets.github.io/XSockets.JavaScript.API/test/index.html
I will have a look at the old Angular example asap and makre sure it is fixed of there is a problem!
Kind regards
Magnus

Weird behavior with Restlet and GAE

I have the following piece of code with Restlet in Google AppEngine from an Android client.
ClientResource clientResource = new ClientResource(RESTLET_TEST_URL);
ProductResource productResource = clientResource.wrap(ProductResource.class);
productResource.store(mProduct);
Status status = clientResource.getResponse().getStatus();
Toast.makeText(this, "Status: "+ status.getDescription(), Toast.LENGTH_SHORT).show();
clientResource.release();
The .store() method is analogous to a PUT request. The weird thing is, this works fine when I connect to the development server but on the actual AppEngine site, nothing happens. I just get Status: OK indicating that the request went through.
I can't troubleshoot cause I can only do that in the Dev Server and that is working fine.
Any ideas on what the problem may be or how to approach this ?
For reference, the code at the server end is :
if (product != null ) {
if (new DataStore().putToDataStore(product) ) {
log.warning("Product written to datastore");
} else {
log.warning("Product not found in datastore");
}
}
This is just a simple write to the datastore using Objectify.
Turns out this is a known issue. See here
The solution is to use clientResource.setEntityBuffering(true);. However, please note that this method is only available in the Release Candidate for Android Client and not in the stable release.

Resources