Skip to content

Index reflection symbols and fix README reflection mounts - #453

Merged
stefanvanburen merged 4 commits into
mainfrom
svanburen/grpcreflect-symbol-index
Nov 10, 2026
Merged

stefanvanburen merged 4 commits into
mainfrom
svanburen/grpcreflect-symbol-index

Conversation

@stefanvanburen

@stefanvanburen stefanvanburen commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

This builds the symbol and extension-number lookups once up front instead of scanning the registry per request, and gives each not-found case its own message. The speedup is small in practice (about 10µs → 3µs for all_extension_numbers_of_type on a registry with descriptor.proto and friends; file responses are dominated by serializing the descriptor anyway), so this is mostly about deleting _find_file_for_symbol.

The README example only mounted v1, but we've listed v1alpha since #407, so a v1alpha client got a 404. Also noted which servers reflection works on: I checked grpcurl and buf curl against uvicorn (fails, no HTTP/2), granian (fails for gRPC since it lacks ASGI trailers), and pyvoy (works).

@stefanvanburen
stefanvanburen force-pushed the svanburen/grpcreflect-symbol-index branch 2 times, most recently from bf7f3a9 to 61452c7 Compare November 10, 2026 00:32
`_Index` builds maps from fully qualified symbol names to files and
from message names to extension numbers once, at construction, instead
of scanning the registry on each `file_containing_symbol` and
`all_extension_numbers_of_type` request. This replaces
`_find_file_for_symbol` and the registry setup repeated in each of the
four services.

Not-found errors now name what was missing (`file not found`,
`extension not found`, `type not found`) instead of always saying
`symbol not found`.

Signed-off-by: Stefan VanBuren <stefan@vanburen.xyz>
`list_services` lists `grpc.reflection.v1alpha.ServerReflection` since
#407, but the README example only mounted v1, so a client calling
v1alpha got `unimplemented: HTTP status 404 Not Found`.

The README also now says which servers reflection works on. Against
Uvicorn (HTTP/1.1 only), `grpcurl list` fails with `context deadline
exceeded` and `buf curl --list-services` with `the RPC protocol or
method requires HTTP/2`, for every `--protocol`. Granian serves HTTP/2
but lacks the ASGI trailers extension, so `grpcurl` and `buf curl
--protocol grpc` fail with `ASGI server does not support ASGI trailers
extension`. pyvoy passes `grpcurl list`/`describe` and `buf curl
--list-services` for every `--protocol` against both v1 and v1alpha.

Signed-off-by: Stefan VanBuren <stefan@vanburen.xyz>
@stefanvanburen
stefanvanburen force-pushed the svanburen/grpcreflect-symbol-index branch from 61452c7 to de06443 Compare November 10, 2026 00:45
@stefanvanburen
stefanvanburen marked this pull request as ready for review November 10, 2026 00:50
)
```

Mount both `grpc.reflection.v1` and `grpc.reflection.v1alpha`. Both are

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I didn't notice the list_services implication before. Should we go ahead and combine them into one app since the path dispatch should be simple?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think I'd leave that for a follow-up, if that works for you? one app can't really have a single path covering both (/grpc.reflection.v1.ServerReflection and /grpc.reflection.v1alpha.ServerReflection don't share a segment, so Mount(app.path, app) doesn't work), and I think connect-go keeps them as separate handlers too. we could add a helper that returns both routes though; happy to explore that separately.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

put up #460 for this; it adds reflection_apps (+ reflection_apps_sync) that returns both the v1 and v1alpha apps, since a single combined app still can't be mounted at one path.

Comment thread connectrpc-grpcreflect/README.md Outdated
Comment on lines +48 to +52
Reflection is a bidirectional streaming RPC, so the server needs HTTP/2,
and `grpcurl` speaks only the gRPC protocol, which needs the ASGI trailers
extension. Serve the app with [pyvoy](https://github.com/curioswitch/pyvoy),
which supports both; Uvicorn (HTTP/1.1 only) and Granian (no trailers)
don't.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Reflection is a bidirectional streaming RPC, so the server needs HTTP/2,
and `grpcurl` speaks only the gRPC protocol, which needs the ASGI trailers
extension. Serve the app with [pyvoy](https://github.com/curioswitch/pyvoy),
which supports both; Uvicorn (HTTP/1.1 only) and Granian (no trailers)
don't.
Reflection uses the gRPC protocol which requires a server that supports HTTP/2 trailers such as [pyvoy](https://pyvoy.dev).

stefanvanburen and others added 2 commits November 10, 2026 06:35
Co-authored-by: Anuraag (Rag) Agrawal <198344+anuraaga@users.noreply.github.com>
Signed-off-by: Stefan VanBuren <stefan@vanburen.xyz>
Signed-off-by: Stefan VanBuren <stefan@vanburen.xyz>
@stefanvanburen
stefanvanburen merged commit 74ed53b into main Nov 10, 2026
25 checks passed
@stefanvanburen
stefanvanburen deleted the svanburen/grpcreflect-symbol-index branch November 10, 2026 18:17
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.

2 participants