Ajax post working on windows, not on android

I am trying to communicate with a couchdb hosted on a remote server.

I make a GET request and a POST request. I’m also using smileupps to host couchdb(they have free hosting). Here’s the CORS configuration:

credentials: false; headers:Accept, Authorization, Content-Type, Origin; methods: GET,POST,PUT,DELETE,OPTIONS,HEAD; origins: *

Everything works fine when launching ZapWorks Studio on windows. When scanning the zapcode with an android device, however, the post ajax call fails. Only the post. I am using basic authentication . I enforce that only the admin can manage the database on couchdb. I can access the host from both the desktop and the phone from a web browser to do everything manually.

I tried everything I could of to solve the problem: remove authentication, change the CORS configuration…nothing works. I thought it was an issue with CORS but everything works fine on windows and on the mobile just the POST fails…I keep getting a status code of 0. I’ve been trying to make this work for hours and hours. Here’s the zpp to show the logic. ajax.zpp (187.4 KB) .

What I want to do is basically fetch the documents from a database, then post another. Everything works on windows. On mobile the post refuses to work.

EDIT
New info, testing on apitester also works on the desktop and mobile.

EDIT
Tried with REST Api Client on my phone and it worked as well. This can only be a CORS issue or something with zapworks. Weird that it works on windows but not on the phone.

EDIT - I found out what the problem is, but not how to fix it. So I set a proxy to debug the requests made from zapworks studio following this tutorial. It seems that it does a preflight request but gets the response

"HTTP/1.1 405 Method Not Allowed"

even though the payload is

{"error":"method_not_allowed","reason":"Only DELETE,GET,HEAD,POST allowed"}.

Here’s the request:

OPTIONS /ranking HTTP/1.1
Host: somehost.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: null
User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; SM-G950U1 Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 Mobile Safari/537.36
Access-Control-Request-Headers: authorization,content-type,x-requested-with
Accept: /
Accept-Encoding: gzip, deflate
Accept-Language: en-US
X-Requested-With: com.zappar.Zappar

and the response:

HTTP/1.1 405 Method Not Allowed
Server: CouchDB/1.6.0 (Erlang OTP/R15B01)
Date: Mon, 18 Jun 2018 21:22:12 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 76
Cache-Control: must-revalidate
Allow: DELETE,GET,HEAD,POST
Access-Control-Expose-Headers: Cache-Control, Content-Type, Server
Access-Control-Allow-Origin: null
Connection: keep-alive

{“error”:“method_not_allowed”,“reason”:“Only DELETE,GET,HEAD,POST allowed”}

which clearly shows that POST is allowed…

On the windows side, there doesn’t seem to be a preflight request for some reason and my guess is that’s why it works. Now the question is how do I configure CORS on couchdb to work on android. These are the configurations available:

enable_cors: true
credentials: false
headers:Accept, Authorization, Content-Type, Origin
methods:GET,POST,PUT,DELETE,OPTIONS,HEAD
origins:*

This is the code:

const Open_SansRegular_ttf0 = symbol.nodes.Open_SansRegular_ttf0;

parent.on(“ready”, () => {
const Plane0 = symbol.nodes.Plane0;

let ajaxParameters : Z.Ajax.Parameters = {
    url: "https://something.smileupps.com/test/_all_docs?include_docs=true",
    headers: {"Authorization": "Basic my64encoding"},
    method: "GET",
    timeout: 3000
};

// Perform the AJAX request
Z.ajax(ajaxParameters, (statusCode, data, request) => {checkRequest(statusCode, data);});

ajaxParameters = {
    url: "https://something.smileupps.com/test",
    headers: {"Content-Type":"application/json", "Authorization": "Basic my64encoding"},
    method: "POST",
    body: '{"name" : "asdasd", "something": 234}',
    timeout: 3000
};

Z.ajax(ajaxParameters, (statusCode, data, request) => {checkRequest(statusCode, data);});

});

function checkRequest(statusCode, data) {
if (statusCode === 0) {
Open_SansRegular_ttf0.text(“Unable to connect - check network connection.”);
console.log(“Unable to connect - check network connection.”);
return;
}

if (statusCode < 200 || statusCode >= 300) {
    Open_SansRegular_ttf0.text("HTTP request failed: " + statusCode);
    console.log("HTTP request failed: " + statusCode);
    return;
}

// Attempt to parse the data returned from the AJAX request as JSON
let parsedData;
try {
    // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
    parsedData = JSON.parse(data);
} catch (e) {
    Open_SansRegular_ttf0.text("Unable to parse JSON: " + e);
    console.log("Unable to parse JSON: " + e);
    return;
}

return parsedData;

}

EDIT - Here’s the request on windows

Accept:/
Accept-Encoding:gzip, deflate
Accept-Language:en-US
Authorization:Basic mybase64encoding
Connection:keep-alive
Content-Length:37
Content-Type:application/json
Host:http://something.smileupps.com/test
Origin:file://
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ZapWorksStudio/4.0.4-stable Chrome/58.0.3029.110 Electron/1.7.9 Safari/537.36
X-DevTools-Request-Id:3680.9
X-Requested-With:XMLHttpRequest

and the response:

Access-Control-Allow-Origin:file://
Access-Control-Expose-Headers:Cache-Control, Content-Type, ETag, Server
Cache-Control:must-revalidate
Content-Length:95
Content-Type:text/plain; charset=utf-8
Date:Mon, 18 Jun 2018 21:36:22 GMT
ETag:“1-512f89feb3d0a88781119e772ec6fd7b”
Location:http://something.smileupps.com/test
Server:CouchDB/1.6.0 (Erlang OTP/R15B01)

No preflight.

1 Like

Hey guys I made a post on stackoverflow with 100 points bounty

1 Like

https://github.com/pouchdb/add-cors-to-couchdb this might be helpful :slight_smile:

1 Like

Hey Mark thanks. The couchdb on the remote server has CORS configurations. A person replied on the stackoverflow post:

Your problem is in the request: Origin: null is usually what you get when the Web page containing the xhr request is opened with the file: rather than the http or https protocol. You won’t get any successful CORS request with such an origin.

It seems the ajax call is getting messed up for some reason.

1 Like

There seems to be an issue with the way the origin header is handled by ZapWorks Studio:
Untitled

Is there any way to circumvent this? Any settings to change?

1 Like

Our platform team have reviewed this. Most server software allows you to specify that Origin: null is permitted but it looks like the combination of CouchDB and the Chrome implementation on Android don’t make this possible. We are looking at a modification to our platform that will give content experiences proper origins. It’s in our roadmap but we don’t have a timeframe at the moment. How quickly would you be looking to deploy this? The other workaround would be to ‘proxy’ CouchDB using something like nginx to allow all origins for the preflight request.

1 Like

Yea the proxy solution is what I’m going for for the time being. Glad to know implementing proper origins is on the roadmap. Thank you for your time!

1 Like

Thank you so much for this. I was into this issue and tired to tinker around to check if its possible but couldnt get it done. Now that i have seen the way you did it, thanks guys
with regards

2 Likes

Any ETA on this?

2 Likes

Hi @marks,

We don’t have any information to share on this specific functionality, unfortunately. Our team is still looking into its feasibility, and if/how it would affect the wider platform.

We’ll let you know once we have an update.

All the best,
Seb

1 Like

Necroing this thread. It won’t be done ever right?

@george is it not feasible, or is this in the backlog? Adjusting proper origins(currently null) and document.referrer(currently an empty string) would be great!

@Seb @George @Mark @Francesca @simon Can someone give me feedback on this? ;_;.

Hi @marks,

This isn’t a feature on the roadmap I’m afraid.

I wonder if you could try constructing the request yourself instead of using Z.ajax? Something like: Pure JavaScript Send POST Data Without a Form - Stack Overflow

George

That’s a shame. Is it that complicated? Come one now, it’s been 3 years T_T. Pretty please?

I have just read though the thread but wasn’t clear on the platform you’re using - is this with the native Android Zappar app or WebAR? The apps use a webview to run user javascript, but they set the HTML for that page just with a string provided from the host app - we don’t publish individual HTML pages for each published project, just the javascript.

So that means the webview running the user content likely has a null origin (as the page isn’t actually loaded from the origin at all). This is likely also the case on WebAR when page content is provided by a blob, which I suspect is how the sandboxed iframes that run the content are set up on WebAR.

It would likely be pretty complicated to fix; we’d either need to publish a dedicated HTML for every project already out there or add some sort of server-side (ideally at the edge) generation of that page, which adds complexity and latency across the board.

Seb and Mark are no longer at Zappar btw :slight_smile:

WebAR.

It would likely be pretty complicated to fix; we’d either need to publish a dedicated HTML for every project already out there or add some sort of server-side (ideally at the edge) generation of that page, which adds complexity and latency across the board.

I don’t fully understand what you’re saying, but I don’t see why a dedicated HTML for each project would be necessary. You could just add to the origin “web.zappar.com” or “beta.zappar.app” or something. Same with document.referrer . It doesn’t need to be the full path. That would solve the problem.

Unfortunately you can’t set the origin / host from Javascript - a lot of the web security model is built on trusting certain origins so JS isn’t allowed to change that. Normal iframes that are loaded from a blob will inherit the origin of the parent, but we use the “sandbox” attribute to explicitly disable that from happening - see the description here - specifically the allow-same-origin section.

That actually reminds me of the real reason we actually want user scripts to have a null origin - the Studio WebAR sites run third-party content from lots of different users, and if all those content scripts shared an origin then they would be able to read and write cookies and local storage used by content from other accounts.

Universal AR content hosted on Zapworks doesn’t have this restriction - rather each workspace is allocated its own subdomain so cross-workspace snooping isn’t possible but it does allow projects to share local storage and cookies if they want.

Were you able to work around the issue with a proxy?

That actually reminds me of the real reason we actually want user scripts to have a null origin - the Studio WebAR sites run third-party content from lots of different users, and if all those content scripts shared an origin then they would be able to read and write cookies and local storage used by content from other accounts.

Ok then what about the full URL? Something like Zappar for Web (v1.0.142) ? So each project would have an unique origin.

Were you able to work around the issue with a proxy?

Yes but it’s a pain. It’d be so much better if you could set the origin/document.referrer for Studio projects. Please consider it :pray:

We’d either need to publish a dedicated HTML for every project already out there or add some sort of server-side (ideally at the edge) generation of that page, which adds complexity and latency across the board.

I understand but this complexy and latency would only need to be done once per project. After it’s initially published, you wouldn’t need to touch the origin and document.referrer again.

The origin doesn’t include the path components - that’s just how the web security model works. A per-project subdomain might work, but isn’t straightforward to implement.

The latency I was referring to is for every end-user accessing the content, they now need to fetch the HTML from the network (even if it can be generated on the edge of the CDN) rather than just having it provided as a string from the “runtime” JS running in the main page.

If it was easily possible without security implications we’d do it. I have looked into it and I just don’t think it’s going to be feasible I’m afraid - effectively the Studio WebAR sites are runtimes for “untrusted” user code that runs in the sandboxed iframes. That setup does impose certain limitations on what the content code can do - ie it’s not visible on screen and can’t affect the layout and rendering of the main “runtime” page, and it seems having a null origin for the content JS-driven network requests is another one on that list.

1 Like