Skip to content

Python: Fix header conversion to byte-pair on scope building#2125

Closed
morganabc wants to merge 2 commits intocloudflare:mainfrom
morganabc:patch-1
Closed

Python: Fix header conversion to byte-pair on scope building#2125
morganabc wants to merge 2 commits intocloudflare:mainfrom
morganabc:patch-1

Conversation

@morganabc
Copy link

@morganabc morganabc commented May 14, 2024

headers: These are byte strings of the exact byte sequences sent by the client/to be sent by the server. While modern HTTP standards say that headers should be ASCII, older ones did not and allowed a wider range of characters. Frameworks/applications should decode headers as they deem appropriate.
(https://asgi.readthedocs.io/en/latest/specs/www.html#wsgi-compatibility)

Workerd uses Pyodide JSProxy object when passing fetch(request, env) to Python, but JSProxy objects translates into string, not byte-string.

In order to match ASGI specification, it needs to be translated into byte-pair when building scopes.

@github-actions
Copy link

github-actions bot commented May 14, 2024

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@morganabc
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request May 14, 2024
@morganabc morganabc marked this pull request as ready for review May 14, 2024 23:21
@morganabc morganabc requested review from a team as code owners May 14, 2024 23:21
@morganabc morganabc requested review from hoodmane and jasnell May 14, 2024 23:21
Copy link
Contributor

@hoodmane hoodmane left a comment

Choose a reason for hiding this comment

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

Code change makes sense. Ideally we ought to add a test.

@morganabc morganabc requested a review from hoodmane May 29, 2024 08:57
from js import URL

headers = [tuple(x) for x in req.headers]
headers = [(k.lower().encode(), v.encode()) for k, v in req.headers]
Copy link
Contributor

Choose a reason for hiding this comment

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

In lieu of adding a test, can we add a comment here with an example that cares about this being bytes so that we can add a test ourselves later?

Copy link
Author

Choose a reason for hiding this comment

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

It assumes FastAPI so that would be a main concern, but its for ASGI spec though so should I add for FastAPI or just asserting for spec?

Like, anything for

@app.get("/example")
  async def example(request: Request):
    request.headers.get("content-type") # this will error if header is not bytes.

perhaps?

Copy link
Author

Choose a reason for hiding this comment

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

Also is there way to test this locally? I want to build miniflare with master workerd, but building workers-sdk doesnt works..

Copy link
Contributor

Choose a reason for hiding this comment

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

You can build workerd by itself and then set an environment variable to tell wrangler to use your custom build. @dom96 are there good instructions somewhere that we can point contributors to?

I guess I should check it out and test it myself...

Copy link
Contributor

Choose a reason for hiding this comment

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

May be easier to build and run workerd locally. These are probably the best instructions we have for doing this: https://github.com/cloudflare/workerd?tab=readme-ov-file#configuring-workerd.

We also have some sample Python worker configs in the repo already, for example https://github.com/cloudflare/workerd/tree/main/samples/pyodide-fastapi

Copy link
Author

@morganabc morganabc May 30, 2024

Choose a reason for hiding this comment

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

Oh, so I can use workerd directly! Thanks, now I can test few things.

@jasnell jasnell added the python Issues/PRs relating to Python Workers label May 30, 2024
@leobuskin
Copy link

That's not the only trouble w/ ASGI and binary strings. We also need to decode resulting headers in asgi.py, if I understand correctly:

async def send(got):
        ...
        if got['type'] == 'http.response.start':
            status = got['status']
            headers = [(k.decode(), v.decode()) for k, v in got['headers']]

@spikerwork
Copy link

That's not the only trouble w/ ASGI and binary strings. We also need to decode resulting headers in asgi.py, if I understand correctly:

async def send(got):
        ...
        if got['type'] == 'http.response.start':
            status = got['status']
            headers = [(k.decode(), v.decode()) for k, v in got['headers']]

Confirmed! This patch fixed the problem with b'' I've seen before

Screenshot from 2024-06-05 11-03-09

@morganabc
Copy link
Author

That's not the only trouble w/ ASGI and binary strings. We also need to decode resulting headers in asgi.py, if I understand correctly:

async def send(got):
        ...
        if got['type'] == 'http.response.start':
            status = got['status']
            headers = [(k.decode(), v.decode()) for k, v in got['headers']]

Yes, right. I fixed it. Thanks!

if got["type"] == "http.response.start":
status = got["status"]
headers = got["headers"]
headers = [(k.decode(), v.decode()) for k, v in got['headers']]

Choose a reason for hiding this comment

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

@morgan9e I'd use double quotes here to be compliant w/ folks' formatting standards

Copy link
Author

Choose a reason for hiding this comment

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

Working on it!

@hoodmane
Copy link
Contributor

hoodmane commented Jun 5, 2024

Since #2096 merged, we're actually running Python tests on workerd so it should be possible to add a test now.

@morganabc
Copy link
Author

@hoodmane: Since #2096 merged, we're actually running Python tests on workerd so it should be possible to add a test now.

Should I add test for ASGI only or if its working with Starlette? I think it might be better if we make asgi.py independant to FastAPI

@hoodmane
Copy link
Contributor

hoodmane commented Jun 6, 2024

Agreed we should make it independent. Currently the only thing in there that is dependent on fastapi is Env. Perhaps we can just delete that for now and we can figure out the appropriate place to add it later.

@dom96
Copy link
Contributor

dom96 commented Nov 18, 2024

Hey @morgan9e, would love to get this merged in, are you still planning to create a test for this?

@morganabc
Copy link
Author

Hey @morgan9e, would love to get this merged in, are you still planning to create a test for this?

Yep, thanks I'll do it ASAP.

@morganabc
Copy link
Author

Hey @morgan9e, would love to get this merged in, are you still planning to create a test for this?

Yep, thanks I'll do it ASAP.

I added the test, I wrote sample ASGI Application rather than using FastAPI because I thought it was better to seperate it from FastAPI.

@morganabc morganabc closed this Nov 20, 2024
@morganabc morganabc deleted the patch-1 branch November 20, 2024 07:38
@morganabc
Copy link
Author

morganabc commented Nov 20, 2024

Sorry, I accidently merged and removed upstream branch, I reopened it. #3142

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Issues/PRs relating to Python Workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants