Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] does not work behind proxy #3

Closed
MM-Lehmann opened this issue Jun 4, 2020 · 9 comments
Closed

[BUG] does not work behind proxy #3

MM-Lehmann opened this issue Jun 4, 2020 · 9 comments
Labels
bug Something isn't working help wanted help wanted

Comments

@MM-Lehmann
Copy link

MM-Lehmann commented Jun 4, 2020

I am running dash on a server which sits behind a reverse proxy. Thus, the flask server does not get the real client IP but the proxy IP instead.
Upload would let me select files and display the progress bar but is stuck at 0% and never progresses or actually send data.
Maybe this is a bug of resumable.js? -> 23/resumable.js#214

@fohrloop
Copy link
Owner

fohrloop commented Jun 4, 2020

Thanks for reporting @MM-Lehmann !

I have to say that at least from the top of my head I really don't know how to fix this. Is there any way I could reproduce the problem? I have yet no experience on proxies. If you have any ideas on how to reproduce the problem or try to narrow it down / fix it, please share or throw me with a pull request.

The first step would be to check if it is problem in the JS side or in Flask configuration. What kind of errors you get to the browser console? (F12) Do the XHR request look like going to the right address? (F12 -> Network tab).

If you can explain a little more about how the proxy works (with a diagram?), maybe we can find where it goes wrong.

@fohrloop fohrloop added bug Something isn't working help wanted help wanted labels Jun 4, 2020
@MM-Lehmann
Copy link
Author

The address issued by resumable.js is http://host/API/resumable?resumableChunkNumber=1&resumableChunkSize=1048576&resumableCurrentChunkSize=1048576&resumableTotalSize=4936708&resumableType=application%2Fvnd.ms-excel&resumableIdentifier=4936708-xxx&resumableFilename=xxx&resumableRelativePath=xxx&resumableTotalChunks=4&upload_id=xxx (replaced a few private strings)
My app actually runs on http://host/app buthost/APIdoes not exist due to the proxy setup. Basically, no subsites are allowed. everything has to go through http://host/app...
Also, the console throws the following:

fileError with resumable.js! (file, errorCount) d {opts: {…}, _prevProgress: 0.1450320334927648, resumableObj: n, file: File, getOpt: ƒ, …} <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Object not found!</title>
<link rev="made" href="mailto:%5bno%20address%20given%5d" />
<style type="text/css"><!--/*--><![CDATA[/*><!--*/ 
    body { color: #000000; background-color: #FFFFFF; }
    a:link { color: #0000CC; }
    p, address {margin-left: 3em;}
    span {font-size: smaller;}
/*]]>*/--></style>
</head>

<body>
<h1>Object not found!</h1>
<p>


    The requested URL was not found on this server.

  

    The link on the
    <a href="http://host/app">referring
    page</a> seems to be wrong or outdated. Please inform the author of
    <a href="http://host/app">that page</a>
    about the error.

  

</p>
<p>
If you think this is a server error, please contact
the <a href="mailto:%5bno%20address%20given%5d">webmaster</a>.

</p>

<h2>Error 404</h2>
<address>
  <a href="/">host</a><br />
  <span>Apache</span>
</address>
</body>
</html>

@fohrloop
Copy link
Owner

fohrloop commented Jun 4, 2020

I created an update (0.2.4) where user is able to configure the upload component API endpoint like this:

du.configure_upload(
    app,
    UPLOAD_FOLDER_ROOT,
    upload_api="/any/address/dash-upload", 
)

So, when running the dash app with

app.run_server(debug=True, host='127.0.0.1', port=8877)

the POST requests by the upload component are directed to

http://127.0.0.1:8877/any/address/dash-upload?resumableChunkNumber=1&....

so you can change the path any way you want (after the host). If I understood right, now you would like to use

du.configure_upload(
    app,
    UPLOAD_FOLDER_ROOT,
    upload_api="/app/api/dash-upload", 
)

Does this help you?

@MM-Lehmann
Copy link
Author

MM-Lehmann commented Jun 5, 2020

Wow, thanks for the instant reply and action! Your support is impressive.
Unfortunately, it doesn't work after all:

Request URL: http://host/app/dash-upload?resumableChunkNumber=1&resumableChunkSize=1048576&resumableCurrentChunkSize=1048576&resumableTotalSize=4936708&resumableType=application%2Fvnd.ms-excel&resumableIdentifier=4936708-xxx&resumableFilename=xxx.csv&resumableRelativePath=xxx.csv&resumableTotalChunks=4&upload_id=xxx
Request Method: POST
Status Code: 405 METHOD NOT ALLOWED
Remote Address: 160.46.81.41:80
Referrer Policy: no-referrer-when-downgrade

This is indeed a different message than before, so requests seem to go a bit farther than before but somehow get rejected by the server (host or waitress/flask?!).
I did experiment with the endpoint extension a bit (removing/changing dash-upload) but no change there. Maybe I need to define the endpoint as a flask route in my app first?

EDIT: The above is with waitress under linux. When running the flask dev server, the error becomes 502 (Bad Gateway)

@fohrloop
Copy link
Owner

fohrloop commented Jun 5, 2020

I'm happy to help! The code worked on my local machine, so I guess there is something with your server configuration what we are missing here.

The place where the configuration happens is in configure_upload.py, in the decorate_server. You can see that it actually defines the endpoint as a flask route in your app (this is one of the reasons you need to call du.configure_upload()):

@server.route(upload_api, methods=['POST'])

The server here is the flask.Flask instance of your dash app.

Perhaps next you could try to define any new route to your flask application that can handle POST requests and try to send a POST request there. See if you get that working without dash-uploader?

@MM-Lehmann
Copy link
Author

After much debugging, I've found a solution/workaround.
Construction a dummy route to test things actually helped a lot.
However, I hesitate to actually post a PR because I am not sure this is the right way to do it.

The problem was indeed somehow connected to my apache reverse proxy config (which I cannot administer). In order to make the app load at all, I have to set requests_pathname_prefix="/app" when calling the Dash constructor while explicitly leaving routes_pathname_prefix at default ("/").

This way, I think resumable.js gets confused because it expects a unified url_base_pathname as was the default per dash<0.18.3 & dash_renderer<0.9.0.
My fix was to introduce an additional prefix argument to configure_upload.py and change line 49 to
settings.upload_api = prefix + upload_api.
Now, I call du.configure_upload(self.app, USER_UPLOADS, upload_api="/dash-upload", prefix="/app") instead of prepending the prefix to upload_api.
This way I can inject my apache prefix and keep the routes intact.

I hope this is somewhat comprehensible.
If you think this is the right approach, I am happy to send you a pull request, although you might want to make things a bit prettier that I did. Also, I am not entirely sure if this really is a fix or another strange coincidence of my setup...

Thank you for your sublime support!

@fohrloop
Copy link
Owner

fohrloop commented Jun 6, 2020

Okay I think I got a grasp on what was happening there. So, to recap, you run the app at

http://myhost/myproxy

and therefore initiate the dash app instance with

app = dash.Dash(
    __name__,
    requests_pathname_prefix='myproxy',
)

To make the dash-uploader work (applies to other POST requests, too), you'll need to configure the route /api/dash-uploader with

@server.route('/api/dash-uploader', methods=['POST'])

and in the front-end, POST the requests to http://myhost/myproxy/api/dash-uploader.

I think that the Upload component should work out of the box with proxies, too. Thank you for the detailed info. Your proposed fix seems to be very accurate. However, since all the necessary information is already in the app that is passed to configure_upload, I would suggest that the "prefix" (requests_pathname_prefix) is read automagically. I guess that was what you would have assumed to happen 😊

I made a suggested solution to another branch:

https://github.com/np-8/dash-uploader/tree/issue3

Unfortunately, I do not have a proxy set up, so the dash app won't even start when using any requests_pathname_prefix (other than /). Would you be able to clone this, and install with

pip install -e <path_with_setup.py>

and verify that it works in your proxy setup out of the box?

@MM-Lehmann
Copy link
Author

Runs perfectly out of the box. I don't even to define the upload_api endpoint anymore.
I think you can safely merge this and release.

@fohrloop
Copy link
Owner

fohrloop commented Jun 6, 2020

I added the fix in 0.3.0. Thank you for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted help wanted
Projects
None yet
Development

No branches or pull requests

2 participants