oboe.js icon indicating copy to clipboard operation
oboe.js copied to clipboard

When using file:// payload "error" is always called with code 0

Open jessegit opened this issue 10 years ago • 16 comments

When loading json with relative path from file:// url, fail(undefined,0,"",undefined) will always be called at the end.

jessegit avatar Nov 07 '14 21:11 jessegit

the last argument is undefined if the payload has multiple json objects; the last argument is the json object if the payload has a single json object.

jessegit avatar Nov 07 '14 21:11 jessegit

If you could post a really small code sample, I could use it to make sure I'm understanding your use case 100% and use it to write a test, but I think the problem is the typical fact that file:// protocol tends to behave very strangely when dealing with AJAX. You can see a good example of how to property handle xhr.status checks here and it will need to be changed here

This should fix it, unless I am misunderstanding: https://github.com/netpoetica/oboe.js/commit/bb5772b80a019d03621234b34e356947898888f7

If you could try to use my code in dist/ (master branch) and see if that stops the error, let me know

netpoetica avatar Nov 08 '14 05:11 netpoetica

@jessegit - do you know if @netpoetica 's fork fixes it? I haven't tested very much with file URLs but see no reason not to incorporate support for them if it can be done cleanly.

One issue is I'm not sure how to write an integration test for file:// URLs - I guess the test would have to know the location that it was checked out to so that it can load some test JSON from the local filesystem.

My advice in general would be to use a webserver, but I'm guessing there's some use case where that isn't possible?

jimhigson avatar Jan 03 '15 15:01 jimhigson

@jimhigson In terms of a use case, the way I discovered a solution to this problem was when working with some custom Phonegap-like native app frameworks. HTML/CSS/JS was all being loaded in a UIWebView via the file protocol, and on the file protocol, you will not get an HTTP response code. I also believe the correct thing to do is use a webserver and I believe this is what most native app frameworks do, but I would say that if you don't acknowledge the file system, you are sort of "forcing" an error onto the user.

I would think that to be able to test this, you could just AJAX load JSON from the local filesystem which should give you a status of 0, failing on your master branch but will "succeed" on my fork.

netpoetica avatar Jan 03 '15 16:01 netpoetica

Ah yes, I can see then why you'd want to load from a file:// url.

I agree with the general approach that you mention. I'm not sure how a test running under Karma in the browser can know the location of the checked-out code in the local filesystem. I guess there could be a special endpoint that the test hits over http to get the file:// URL. Think of anything simpler?

jimhigson avatar Jan 03 '15 17:01 jimhigson

Ok, this should be pretty simple. I can make a connect endpoint like this:

function serveCheckedOutLocation(req, res) {
    res.send({fileUrl:'file://' + __dirname});
}

Which replies like this:

{
  "fileUrl": "file:///Users/jimhigson/Sites/oboe.js/test"
}

Then the test can add onto that file URL to make the file:// AJAX request.

jimhigson avatar Jan 03 '15 17:01 jimhigson

(currently failing) integration test in a branch set up for this...

https://github.com/jimhigson/oboe.js/blob/fileUrls/test/specs/oboe.integration.spec.js#L209

jimhigson avatar Jan 03 '15 18:01 jimhigson

@netpoetica - in your patch you check window.location.protocol === 'file:' - actually this is rather more difficult to test than I thought. Of course, Karma running over http can't simply send a request for a file:// url because it has not greater privileges than any other web page.

I don't know how to get Karma running over a file:// URL. I suspect a Karma test like this this may not be possible.

jimhigson avatar Jan 03 '15 18:01 jimhigson

Oi, I see what you mean. I am not super familiar with Karma. I'm thinking about the possibility of putting some kind of bash script in place that wraps a tests for this alongside the code that starts karma, but that will make the results kind of ugly and probably break Travis integration or future plans for automation. The following suggestions get kind of hairy and I imagine will not be incredibly exciting...

The problem is, the HTML page that makes the AJAX request also needs to be opened from within the file:// protocol. So you have to be on the file:// protocol in order to make and AJAX request on the file:// protocol.

I think there would need to be a full HTML including the oboe.js lib and a request to the file system, file that can be opened in a browser in order to really test this.

Is Karma actually a headless browser like Phantomjs? It doesn't appear to be so, from what I can tell it's more like some sort of wrapper around your command-line testing suite to serve as an integrator with CI. Is it possible to have Karma do this somehow, or boot a headless browser like phantomjs to do this? I do not have a clear picture of how this might work, but I am thinking that there might be a way.

Chrome has an option via flags that will let you disable web security and allow these AJAX requests - maybe this Karma chrome launcher will allow for such a thing?

I will keep thinking about this, but yah I definitely see the testability issue

netpoetica avatar Jan 03 '15 20:01 netpoetica

Karma uses actual browsers, but the browser can be headless.

It is probably ok to do this as a unit test by stubbing window.location.protocol (or probably, by stubbing something wrapping this - if we try to change the property itself the URL will change and the test will stop!)

Is it correct to check the protocol of the window and not the URL that was requested? For example, can a page served over file:// make requests to resources not on file:// ?

jimhigson avatar Jan 05 '15 10:01 jimhigson

@jimhigson The behavior is different in different browsers. I haven't been doing front-end much recently but here is how it stands as I remember it:

  • on Firefox, an cross-protocol AJAX request will be allowed (file:// requesting http://)
  • on Chrome, you must specifically run Chrome with the disabled web security flag
  • on Safari, it will be allowed
  • in Phantomjs, there is a flag that you must specify if I recall

I think the stub idea is a very good one! It will help avoid some pretty head-achey situations

netpoetica avatar Jan 06 '15 00:01 netpoetica

Ok I think the only safe way is to look at the protocol of the URL being requested, not the URL of the page (although that would default to the protocol of the page if not explicitly given). That should work anywhere where the browser supports file://

On Tuesday, January 6, 2015, Keith Rosenberg [email protected] wrote:

@jimhigson https://github.com/jimhigson The behavior is different in different browsers. I haven't been doing front-end much recently but here is how it stands as I remember it:

  • on Firefox, an cross-protocol AJAX request will be allowed (file:// requesting http://)
  • on Chrome, you must specifically run Chrome with the disabled web security flag
  • on Safari, it will be allowed
  • in Phantomjs, there is a flag that you must specify if I recall

I think the stub idea is a very good one! It will help avoid some pretty head-achey situations

— Reply to this email directly or view it on GitHub https://github.com/jimhigson/oboe.js/issues/50#issuecomment-68808592.

jimhigson avatar Jan 06 '15 09:01 jimhigson

Any news about that? I am using oboe in my Apache Cordova app. There I make AJAX / oboe requests to my local files that are get downloaded to my internal storage. A typical file request looks like something like this

window.resolveLocalFileSystemURL(
  'cdvfile://localhost/persistent/my-data.json',
  fileEntry => {
    // file:///data/user/0/my.package.for.my.app/files/files/my-data.json
    const url = fileEntry.toURL();
    oboe(url)
      .done(...)
      .fail(...)
  },
  error => console.error(error)
);

Both done() and fail() events are fired.

screendriver avatar Oct 15 '15 06:10 screendriver

Both done() and fail() events are fired.

+1

brody4hire avatar Nov 18 '15 12:11 brody4hire

For all that are struggling with that: my current, slightly ugly, workaround is

let doneCalled = false;

window.resolveLocalFileSystemURL(
  'cdvfile://localhost/persistent/my-data.json',
  fileEntry => {
    // file:///data/user/0/my.package.for.my.app/files/files/my-data.json
    const url = fileEntry.toURL();
    oboe(url)
      .done(() => {
        doneCalled = true;
      })
      .fail(error => {
        if (!doneCalled) {
          // It's a real error...
        }
      })
  },
  error => console.error(error)
);

screendriver avatar Nov 18 '15 13:11 screendriver

Hmm my post above is not a real solution. If something really fails, then I never know if it was a real error or a .fail() that's always get called 😕

screendriver avatar May 02 '16 09:05 screendriver