fadushin / esp8266

This repository contains source code for the ESP8266.
BSD 2-Clause "Simplified" License
73 stars 22 forks source link

Some modification to live with CORS #18

Open mvincm opened 6 years ago

mvincm commented 6 years ago

Hello,

First of all GREAT job, best http server form micropython! Thanks!

I'm using ESP32 and local laptop with chrome to do my project. I'm trying to write simple JS application to talk with ESP32. I'm using POST with "Content-type application/json" to send my JSON data do ESP32 (simple js script).

And the problem is that Chrome CORS blocking me to do my job ;)

So I made some changes in your code but I'm not sure it is right way to do this..

Step by step:

1) There must be two additional fields in header "Access-Control-Allow-Origin: *" and "Access-Control-Allow-Headers: Content-Type" so I add them to "uhttpd.py" in/near line 272. 2) There must be handler for OPTIONS method for preflight chrome request. So I add it to "http_api_handler.py" file near line 59 where is "if" statement for "verb" check and of course I must handle request for "options"method from Chrome in my API script (it could return simple "foo bar" dictionary) .

And the question is... Is it right way to to this?

Best regards, MvincM

fadushin commented 6 years ago

Sounds right, but could you either specify the version you are using (using the GIT SHA), use the latest version, or clone and submit a PR? I've recently made some changes to the source code, and uhttpd.py no longer exists!

mvincm commented 6 years ago

Thanks for replay ! You are 100% correct, I was using old version so I start to do my modification on current version (init.py 68c22840e763796cfe12099cd8404adf181bcbc2) but... after fresh installation of latest libs version - upip.install('micropython-uasyncio') and upip.install('micropython-logging') I have error like this:

import uhttpd import uhttpd.file_handler server = uhttpd.Server([('/', uhttpd.file_handler.Handler('/www'))]) server.run() INFO:None:uhttpd-master running... Traceback (most recent call last): File "", line 1, in File "uhttpd/init.py", line 70, in run File "uhttpd/init.py", line 407, in run File "/lib/uasyncio/core.py", line 138, in run_forever File "/lib/uasyncio/core.py", line 107, in run_forever AttributeError: 'generator' object has no attribute 'pend_throw'

Any ideas? I do not exactly where the problem is? In your great program or in uasyncio.

Best regards, MvincM

mvincm commented 6 years ago

Hello.

I tried myself to solve the issue but it seems that my Python skills are to low... Could you help? Thanks in advanced.

MvincM

fadushin commented 6 years ago

I was able to reproduce this issue using the latest uasyncio libraries. If I revert to uasyncio release 1.3 (f1fa3a7ff1a5c00be102e5d037fee0d556e4ca17), the problem goes away. I will look into what is wrong. For now, please do not use a version of uasyncio later that 1.3.

For reference, the commit that causes this to happen is:

https://github.com/micropython/micropython-lib/commit/203cc489c6c02ffba9b3dd3efb71d7067cc0836f#diff-3baf56914fffa5de6644c7a64f0fc296

fadushin commented 6 years ago

C.f., https://github.com/micropython/micropython-lib/issues/244

mvincm commented 6 years ago

Thanks for your attentions! I reverted to version 1.3 and it works. Waiting for any news from uasyncio devs.

Now I can push my CORS updates ;)

mvincm commented 6 years ago

My pull request

https://github.com/fadushin/esp8266/pull/20

but... those modification should be used (in my opinion) just for development. Normally CORS do the good job with security.

Best regards, MvincM

mvincm commented 6 years ago

Some news from field ;)

If someone want to use own JavaScript API like using e.g. XMLHttpRequest() and Basic Auth the http server scripts should be modified for this type of use case. I don't pull any new request to the code as @fadushin want to do more configurable all CORS actions. So I would describe below what should be done if we want to use Basic Auth and JS API. BTW... all my tests were done on Chrome.

1) If we are using Basic Auth our server side header Access-Control-Allow-Origin: * won't work... sorry... there must be some domain or IP address like this: Access-Control-Allow-Origin: http://192.168.43.129. So it is impossible to test JS from local disk. Chrome do not send any "Origin" if we run JS from local disk...

2) So if we have our JS scripts on some http server (in my case I don't want to put to many JS files on ESP32 - my http server on ESP32 is simple REST server) we should modified our response headers to looks similar to those:

response['headers'], {'Server': Server.server_name(),'Access-Control-Allow-Origin': 'http://192.168.43.129', 'Access-Control-Allow-Credentials' : 'true', 'Access-Control-Allow-Headers': 'Content-Type,Authorization,Credentials'}

3) Authorization header is never sent for OPTIONS (preflight) request. Never! So our server give 401 for preflight so chrome don't allow to send next e.g. POST request from your JS script. In this case small modification in 'init.py' file must be done. Line if self._config['require_auth']: should looks like this if self._config['require_auth'] and http_request['verb'] != 'options':

4) Simple JS script to send and receive pure JSON in POST looks like this:

<script> // Sending and receiving data in JSON format using POST method var xhr = new XMLHttpRequest(); var url = "http://192.168.43.129:8080/api/test2"; xhr.open("POST", url); xhr.setRequestHeader("Authorization", "Basic " + window.btoa("user" + ":" + "password")); xhr.setRequestHeader("Content-type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); console.log(json.email + ", " + json.password); } }; var data = JSON.stringify({"email": "foo@bar.com", "password": "passwd"}); xhr.send(data); </script>

5) As I can assume, @fadushin configurable options would be for some kind od "dev mode" but for security reason there should be options to explicite control Origins domains/IPs.

Best regards, MvincM

alexiade commented 5 years ago

So... There is a very easy way to get around CORS for development. Grab nginx, make it join them into one site with esp API under reverse proxy. Works like a charm :)