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

fix: upgrade is not websocket and dependencies are installed, should not warning #2360

Merged
merged 17 commits into from
Aug 13, 2024

Conversation

vvanglro
Copy link
Contributor

@vvanglro vvanglro commented Jun 6, 2024

Summary

When I upgraded uvicorn to standard, I found that I encountered a lot of Unsupported upgrade request. prompts in the logs, so I debugged locally and found that h11 and httptools handle upgrades with different logic. I think I should keep the same logic.
If the upgrade field is not equal to websocket in h11, the self._should_upgrade_to_ws() method will not run, while httptools is different.

Checklist

  • I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
  • I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
  • I've updated the documentation accordingly.

@vvanglro
Copy link
Contributor Author

@Kludex Hey, would you look at that?

@Kludex Kludex self-assigned this Jul 20, 2024
@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

Can you create a table of expected behavior you see?

Also, while checking this... I've noticed that this is printed multiple times with httptools:

WARNING:  Unsupported upgrade request.
WARNING:  No supported WebSocket library detected. Please use "pip install 'uvicorn[standard]'", or install 'websockets' or 'wsproto' manually.
WARNING:  Unsupported upgrade request.
WARNING:  No supported WebSocket library detected. Please use "pip install 'uvicorn[standard]'", or install 'websockets' or 'wsproto' manually.
WARNING:  Unsupported upgrade request.
WARNING:  No supported WebSocket library detected. Please use "pip install 'uvicorn[standard]'", or install 'websockets' or 'wsproto' manually.

Does this also happens for you?

@Kludex Kludex assigned vvanglro and unassigned Kludex Jul 20, 2024
@vvanglro
Copy link
Contributor Author

I have created a table to describe the expected behavior and the actual behavior I observed:

Protocol Expected Behavior Actual Behavior
h11 Ignore upgrade requests not equal to websocket Works as expected, no warnings
httptools Ignore upgrade requests not equal to websocket Multiple warnings: "Unsupported upgrade request"

yes, I also encountered the warnings multiple times with httptools.

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

Is this PR fixing the multiple warnings? I tried it, and it didn't change that. I may have done something wrong?

@vvanglro
Copy link
Contributor Author

vvanglro commented Jul 20, 2024

How did you test it?
Run the code below without this PR, then you will see multiple warnings when httptools is selected.

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello, World!"}


if __name__ == '__main__':
    import uvicorn
    # uvicorn.run(app, http="h11")
    uvicorn.run(app, http="httptools")
import httpx


async def a():
    headers = {"Connection": "upgrade", "Upgrade": "123"}
    async with httpx.AsyncClient() as client:
        response = await client.get('http://0.0.0.0:8000', headers=headers)
        print(response.text)


import asyncio
asyncio.run(a())

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

I'll get back to you in some hours. I'm away.

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

I'm using wscat.

I'd like to see this working against:

from starlette.applications import Starlette
from starlette.routing import WebSocketRoute
from starlette.websockets import WebSocket


async def route(websocket: WebSocket) -> None:
    await websocket.accept()
    await websocket.send_text("Hello, world!")
    await websocket.close()


app = Starlette(routes=[WebSocketRoute("/ws", route)])

Running with uvicorn main:app --http httptools.

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

Ah, got it.

The _should_upgrade is called multiple times in the httptools one.

@vvanglro
Copy link
Contributor Author

vvanglro commented Jul 20, 2024

If you are using the standard ws client to request it, no warning will be generated.
If you use http and bring the upgrade upgrade protocol field in the header. Then a warning will be generated.

image


def _should_upgrade(self) -> bool:
upgrade = self._get_upgrade()
return self._should_upgrade_to_ws(upgrade)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The biggest difference between h11 and httptools is that h11 strictly determines that upgrade is equal to websocket, whereas httptools does not, which leads to warnings when upgrade is not a websocket.

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 20, 2024

Are you saying that your PR just doesn't solve the multiple warnings on the scenario which should really warn?

@vvanglro
Copy link
Contributor Author

vvanglro commented Jul 20, 2024

There seems to be two issues now, there should only be one warning prompt if a user receives a request to upgrade ws when they have not chosen to install a websocket dependency.

When a user installs a websocket dependency and receives a request with an incorrect value in the upgrade field, multiple warnings are generated, which is incorrect because I already have the dependency installed, this PR is to fix that.

@vvanglro vvanglro changed the title fix: upgrade is not websocket, handle httptools with the same logic as h11 fix: upgrade is not websocket and dependencies are installed, httptools should not warning Jul 20, 2024
@vvanglro
Copy link
Contributor Author

vvanglro commented Jul 22, 2024

Only one warning is generated now.

And the warnings are friendly, if the dependency is installed and the upgrade is not supported, then only prompted the upgrade is not supported. If the dependency is not installed and the upgrade is not supported, then both are prompted.

@urec56
Copy link

urec56 commented Jul 27, 2024

Hey. What the solution of this problem now, guys? I have it on my fastapi app for some time.
image

And it starts like

CMD ["gunicorn", "app.main:app", "--workers", "1", "--worker-class", "uvicorn.workers.UvicornWorker", \
 	"--bind=0.0.0.0:8000", "--max-requests", "100", "--max-requests-jitter", "50"]

My libs:

h11==0.14.0
httptools==0.6.1
uvicorn==0.26.0
uvloop==0.19.0
websockets==12.0

@vvanglro
Copy link
Contributor Author

vvanglro commented Jul 27, 2024

@urec56 This comment is the solution to this PR: #2360 (comment)

If you can reproduce the problem, you can try installing this PR to verify that it solves the problem.

pip install git+https://github.com/vvanglro/uvicorn.git

The expected result is that you will only receive: [WARNING] Unsupported upgrade request.

@urec56
Copy link

urec56 commented Jul 27, 2024

Ok, I'll try it with this fix and reply here in a few days

@urec56
Copy link

urec56 commented Jul 30, 2024

So, I tried this fix and it looks like it solved the problem. Thank you, guys

@Kludex
Copy link
Sponsor Member

Kludex commented Jul 31, 2024

I'll check this over the weekend. Sorry the delay.

@Kludex Kludex assigned Kludex and unassigned vvanglro Jul 31, 2024
@vvanglro vvanglro changed the title fix: upgrade is not websocket and dependencies are installed, httptools should not warning fix: upgrade is not websocket and dependencies are installed, should not warning Aug 13, 2024
@vvanglro
Copy link
Contributor Author

@Kludex Maybe there should be a timeout for the test action?

jobs:
  tests:
    name: "Python ${{ matrix.python-version }} ${{ matrix.os }}"
    runs-on: "${{ matrix.os }}"
+   timeout-minutes: 30

@Kludex
Copy link
Sponsor Member

Kludex commented Aug 13, 2024

Yes. Some process is hanging. Do you want to create a PR for it?

@vvanglro
Copy link
Contributor Author

Yes. Some process is hanging. Do you want to create a PR for it?

I'll do.

Copy link
Sponsor Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @vvanglro 🙏

uvicorn/protocols/http/httptools_impl.py Outdated Show resolved Hide resolved
uvicorn/protocols/http/httptools_impl.py Outdated Show resolved Hide resolved
@urec56
Copy link

urec56 commented Aug 13, 2024

In what release of uvicorn we can download this fix?

@Kludex Kludex merged commit 587a1cc into encode:master Aug 13, 2024
15 checks passed
@Kludex
Copy link
Sponsor Member

Kludex commented Aug 13, 2024

In what release of uvicorn we can download this fix?

0.30.6

@urec56
Copy link

urec56 commented Aug 13, 2024

Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants