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

HTTP2 Trailers not being sent #254

Open
adambudziak opened this issue Aug 30, 2024 · 2 comments
Open

HTTP2 Trailers not being sent #254

adambudziak opened this issue Aug 30, 2024 · 2 comments

Comments

@adambudziak
Copy link

adambudziak commented Aug 30, 2024

Hi, I know that it's probably a stretch since it's likely covered by a bunch of tests, but I tried everything and I can't get it to work. So I'm playing with connecting gRPC to ASGI using hypercorn, and I made this very simple app:

import asyncio
from hypercorn.config import Config
from hypercorn.asyncio import serve

async def grpc_echo(scope, receive, send):
    if scope['type'] == 'http' and scope['method'] == 'POST':
        message = await receive()
        request_body = message.get('body', b'')

        # Send headers
        await send({
            'type': 'http.response.start',
            'status': 200,
            'headers': [
                (b'content-type', b'application/grpc'),
            ],
            'trailers': True
        })

        await send({
            'type': 'http.response.body',
            'body': request_body,
        })

        await send({
            'type': 'http.response.trailers',
            'headers': [
                (b'grpc-status', b'0'),
                (b'grpc-message', b'OK'),
            ],
        })
    else:
        raise NotImplementedError


async def main():
    config = Config()
    config.bind = ["localhost:50051"]
    await serve(grpc_echo, config)

if __name__ == "__main__":
    asyncio.run(main())

here's the proto (though I doubt it matters)

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloRequest) {}
}

message HelloRequest {
  string name = 1;
}

I run the client by
grpcurl -proto helloworld.proto -plaintext -d '{"name": "World"}' localhost:50051 helloworld.Greeter/SayHello

and the error I get is

  Code: Internal
  Message: server closed the stream without sending trailers

This area is completely new to me so there might be some rookie mistake (maybe not related to trailers at all), but I checked the ASGI spec, and my trailers look ok, I ran it through debugger and it looks like it's being sent, however I don't see them in Wireshark and the clients (I tried Postman and grpcurl) both fail.

In the debugger I saw that the response body is sent (I see the "World" string), I also see some stuff being sent afterwards, but nothing looking like the trailers that I set.

Any help is appreciated, and congrats for an amazing lib, it's been a long time since I saw one that's so readable inside.

@adambudziak
Copy link
Author

adambudziak commented Aug 31, 2024

Ok, so I did more digging, and it looks like some issue with end_stream value in H2Stream.send_headers.

https://github.com/python-hyper/h2/blob/master/src/h2/stream.py#L863the function taken from the map here sets trailers_sent in the state machine to true.

Then this check https://github.com/python-hyper/h2/blob/master/src/h2/stream.py#L877
always raises an exception because trailers_sent is True, but end_stream is always False (hypercorn doesn't set it).

(it looks correct since according to the spec "The HEADERS frame starting the trailers header block has the END_STREAM flag set.")

I tried setting end_stream to True, but then I get the following error in grpcurl.

Error invoking method "helloworld.Greeter/SayHello": grpc call for "helloworld.Greeter.SayHello" failed: EOF

however it works better in Postman (I see the status code, but no response body).

I'm gonna keep investigating, it's likely that it's a different issue.

@adambudziak
Copy link
Author

It looks like because trailers now set end_stream=True, the response body isn't sent. I made a "fix" (quotes because I really have no idea what I'm doing). I'll check if tox passes and if so, I'll make a PR.

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

No branches or pull requests

1 participant