There were a lot of really good questions after my OSCON talk on Dart. I think I was able to answer most of the questions statisfactorily and accurately, but to one I gave the wrong answer.
The question was how to make an
HttpRequest
to a different origin site. I thought that once the CORS headers were set properly on the server, there was a setting in HttpRequest
that was all that was required. I followed up afterwards and found that the questioner had solved the problem in the meantime—in this case, it was hitting the wrong server (one without CORS headers set).But it occurs to me that I have not tried this myself. In fact, I have specifically side-stepped the problem in the Hipster MVC test suite by only running my browser-based tests from
content_shell
. This utility which evaluates and analyzes a browser page also doubles as test harness by loading a test context page and sending the console output to STDOUT on the command line.What is side-stepping my CORS question is that
content_shell
is able to make localhost connections to a test server. When I run the same test suite accessing the same test server from the Dartium browser, I get a bunch of errors like:OPTIONS http://localhost:31337/widgets/ALL Origin null is not allowed by Access-Control-Allow-Origin. localhost:31337/widgets/ALL:1 XMLHttpRequest cannot load http://localhost:31337/widgets/ALL. Origin null is not allowed by Access-Control-Allow-Origin. index.html:1 OPTIONS http://localhost:31337/widgets/ALL Origin null is not allowed by Access-Control-Allow-Origin. ...I am unsure how an OPTIONS request for cross origin request checking should respond in this case. Also, I am on a plane and too cheap to pay the $20 just to look up the answer, so I am going to start by replying with a simple 200 with no response body. In my Dart test server I do this as:
HttpServer.bind('127.0.0.1', 31337).then((app) {
app.listen((HttpRequest req) {
if (req.method == 'OPTIONS') {
HttpResponse res = req.response;
res.statusCode = HttpStatus.OK;
res.close();
return;
}
// Handle other requests...
});
});
Nothing too fancy there—it reads quite well. If the current request is an OPTIONS, then I grab the response property from the incoming request object, set a 200/OK status code on it, and close the response, signalling that processing of this response is complete. I then return
from the request stream so that no other handling is attemped.But after restarting the server and re-running my tests, I still get the same errors in the Dartium console. So next I add the header that is being complained about,
Access-Control-Allow-Origin
to the response headers:
HttpServer.bind('127.0.0.1', port).then((app) {
app.listen((HttpRequest req) {
HttpResponse res = req.response;
res.headers
..add('Access-Control-Allow-Origin', 'null');
if (req.method == 'OPTIONS') { /* ... */ }
// Handle other requests...
});
});
I am fairly sure that a value of *
would work here as well (confirmed later), but I start with the most restricted value that can possibly work—the “null” value currently supplied by Dartium when running a file://
test context page.With that change applied to my test server, my tests still fail. But now with a new error message. Instead of complaing about the
Access-Control-Allow-Origin
header, Dart is complaining about Access-Control-Allow-Headers
:OPTIONS http://localhost:31337/widgets Request header field Content-type is not allowed by Access-Control-Allow-Headers. localhost:31337/widgets:1Phew! So at least I am on the right track. It is never fun to try a theory and see no change at all.
To change this error, I add the apparently required header to my test server:
HttpServer.bind('127.0.0.1', port).then((app) {
app.listen((HttpRequest req) {
HttpResponse res = req.response;
res.headers
..add('Access-Control-Allow-Origin', 'null')
..add('Access-Control-Allow-Headers', 'Content-Type');
if (req.method == 'OPTIONS') { /* ... */ }
// Handle other requests...
});
});
I then get a similar (but different) warning:OPTIONS http://localhost:31337/widgets/ALL Method DELETE is not allowed by Access-Control-Allow-Methods.I fix that with year another header:
HttpServer.bind('127.0.0.1', port).then((app) {
app.listen((HttpRequest req) {
HttpResponse res = req.response;
res.headers
..add('Access-Control-Allow-Origin', 'null')
..add('Access-Control-Allow-Headers', 'Content-Type')
..add('Access-Control-Allow-Methods', 'DELETE,PUT');
if (req.method == 'OPTIONS') { /* ... */ }
// Handle other requests...
});
});
(the PUT comes from another message that I see from one of the tests)With that, I have eliminated all of my errors and have my test suite passing—now from both the browser and content shell:
➜ test git:(http-test-server) ✗ content_shell --dump-render-tree index.html CONSOLE MESSAGE: unittest-suite-wait-for-done CONSOLE MESSAGE: PASS: unsupported remove CONSOLE MESSAGE: PASS: Hipster Sync can parse regular JSON CONSOLE MESSAGE: PASS: Hipster Sync can parse empty responses CONSOLE MESSAGE: PASS: Hipster Sync HTTP get it can parse responses CONSOLE MESSAGE: PASS: Hipster Sync HTTP post it can POST new records CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP PUT: can update existing records CONSOLE MESSAGE: PASS: Hipster Sync (w/ a pre-existing record) HTTP DELETE: can remove the record from the store CONSOLE MESSAGE: PASS: Hipster Sync (w/ multiple pre-existing records) can retrieve a collection of records CONSOLE MESSAGE: CONSOLE MESSAGE: All 8 tests passed. CONSOLE MESSAGE: unittest-suite-successIn the end, my response at the OSCON session was misleading (sorry about that). I had indicated that only a single parameter need be set in the
HttpRequest
constructor. I think I was likely thinking of the withCredentials
option, which will send cookies and similar authorization data to the CORS server. The answer is that nothing else is needed. As long as the server sets the appropriate headers, which I can do now in my Dart server, the the HttpRequest
object from dart:html
will just work.Day #824
No comments:
Post a Comment