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

New grpcadapter package to transcode connect and gRPC-Web #527

Closed
wants to merge 401 commits into from

Conversation

emcfarlane
Copy link
Contributor

@emcfarlane emcfarlane commented Jun 21, 2023

Creates a new package grpcadapter (github.com/connect-go/grpcadapter). Create a handler grpcadapter.NewHandler that can transcode connect and gRPC-web protocols to gRPC handlers. Allows gRPC go servers to use connect by wrapping grpc.Server.

	grpcServer := grpc.NewServer()
	mux := grpcadapter.NewHandler(grpcServer,
		grpcadapter.WithReadMaxBuffer(1024*1024),
		grpcadapter.WithWriteMaxBuffer(1024*1024),
	)
	http.ListenAndServe(":8080", h2c.NewHandler(mux, &http2.Server{}))

Unary connect requests are buffered and obey the limits set via connect options. For streams and gRPC-web these options are ignored and control is passed to the underlying gRPC handler.

Connect-crosstest is used to add more coverage: connectrpc/conformance#478

Closes https://github.com/bufbuild/connect-go/issues/334

bufdev and others added 30 commits April 9, 2022 11:08
* Add test for errors marshaling protobuf Status

If a user-supplied Codec errors when marshaling a protobuf Status
message (which we use internally when converting errors to HTTP
trailers), we currently drop all the error information on the floor and
return "Unknown: EOF".

This commit adds a reproduction as a test case. Relates to #197.

* Use CodeInternal for errors marshaling proto Status

If we can't marshal a protobuf Status message, send CodeInternal to the
client with some details.

* Fix error-wrapping lint
So that we can share code between our implementations of the Connect and
gRPC protocols, factor enveloping (aka length-prefixed messages) and
compression out of the gRPC-specific code.
* Add test for unary RPC with zero-byte messages

* Add failing test for handler timeout handling

We're parsing timeouts, but not properly propagating them into user
code. Thanks, crosstests!

* Fix timeout handling

Since we know the shape of the Connect protocol, we can simplify the
protocol interfaces and move some shared utility functions into
`protocol.go`. This also fixes server-side timeout handling.

* Keep Accept-Post string manipulation shorter

We're only doing this at startup, so it's okay to make it slow. The code
doesn't get much shorter, but it's arguably more readable.

* Add indirection to constant limit

Move the literal for our discard limit into a constant.
…nable to be sent (#211)

Co-authored-by: Steve Ayers <sayers@uber.com>
In preparation for factoring out some of the complicated HTTP stuff
happening in the gRPC client stream implementation, move timeout
encoding to a less-convoluted portion of the code.
The most complex portion of the client-side gRPC sender and receiver is
the HTTP layer, where we use `io.Pipe` to create a streaming request
body. So that we can reuse this code for the Connect protocol, factor it
out of the gRPC-specific code. This has the side effect of simplifying
the gRPC implementation and more clearly separating the gRPC and
gRPC-Web protocols.
The params structs for protocols are somewhat proven now, so we can stop
hand-copying them into protocol-specific structs and embed instead.
Make all the gRPC headers constants and prefix a few unexported utility
functions that snuck through.
If we're not using the receiver, leave it unnamed.
Use Connect's canonical string representation for error codes.
We use sender and receiver wrappers to make sure that all public APIs
return *Errors. Factor these out of the gRPC code, since they're equally
applicable to Connect.
@emcfarlane emcfarlane changed the title Transcode Connect and gRPC-Web to gRPC handlers New grpcadapter package to transcode connect and gRPC-Web Jun 26, 2023
// binary Protobuf format, even if the messages in the request/response stream
// use a different codec. Consequently, this function needs a Protobuf codec to
// unmarshal error information in the headers.
func grpcErrorFromTrailer(trailer http.Header) *connect.Error {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copied from protocol_grpc.go

return base64.NewDecoder(base64.URLEncoding, stringReader)
}

func queryValueReader(data string, base64Encoded bool) io.Reader {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copied from protocol_connect.go.

emcfarlane and others added 5 commits June 27, 2023 09:22
Use the `MarshalAppend` methods introduced in v1.31 of the protobuf
runtime to more aggressively reuse buffers from Connect's internal pool
(for both binary and JSON). This reduces allocations and substantially
improves throughput:

```
BenchmarkConnect/unary-12                   8054           1318347 ns/op        14414394 B/op        251 allocs/op
BenchmarkConnect/unary-12                 10448           1153636 ns/op         6115680 B/op        236 allocs/op
```

---------

Co-authored-by: Akshay Shah <akshay@akshayshah.org>
Capture unexpected io.EOF errors as io.ErrUnexpectedEOF in client streams. Now raises an error when the stream doesn't end with an end response message.

Fixes https://github.com/bufbuild/connect-go/issues/397
Copy link
Member

@akshayjshah akshayjshah left a comment

Choose a reason for hiding this comment

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

I really like the direction here - the user-facing API is super nice and clean! It's also much more decoupled from the internals of connect-go than I expected, which is great - I'd love to keep this PR open until we've got github.com/connectrpc ready to go, and then put this code into a separate repository.

A few notes on the code:

  • Please follow the Go style guide. (Search Notion for "Code Standards.")
  • Can we use the content-length header to buffer less when handling unary Connect RPCs?

connect_ext_test.go Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
connect_ext_test.go Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
grpcadapter/adapter.go Outdated Show resolved Hide resolved
protocol.go Outdated Show resolved Hide resolved
protocol_connect.go Outdated Show resolved Hide resolved
emcfarlane and others added 4 commits June 30, 2023 10:36
Cover the gRPC and gRPC-Web protocols in the tests for
`io.ErrUnexpectedEOF`, and ensure that error codes and observable
`errors.Is` behavior is consistent for all 3 supported RPC protocols.
@timruffles
Copy link

What's the status of this feature, and any indication on which version it'll appear in?

@akshayjshah
Copy link
Member

We're splitting it out into a separate project, which we'll release under the https://github.com/connectrpc organization. It's being actively worked on by @emcfarlane and @jhump, so it's coming as soon as we get something really nice ready!

@timruffles
Copy link

That's great. Do you know if it will support existing unary server interceptors? We have a lot of existing interceptors we'd like to avoid having to rewrite or maintain 2x copies of.

@akshayjshah
Copy link
Member

As things are shaping up now, the new package we're developing works with any http.Handler. Since grpc.Server implements http.Handler, you'll have full support for unary and streaming gRPC interceptors.

@akshayjshah akshayjshah deleted the edward/transcode-handler branch September 15, 2023 23:51
@timruffles
Copy link

Is there somewhere we could follow along the development of the forthcoming package? We're getting close to an internal decision point about technology, and having this available would really help. The number of internal existing golang gRPC services we have implemented means it wouldn't make sense to use connect without something like this.

@timruffles
Copy link

Also - we'd be very happy to beta-test! 🥳

@emcfarlane
Copy link
Contributor Author

Hey @timruffles, beta testing would be great! Will get something to you soon.

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.