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

Rework streaming interceptors #316

Merged
merged 11 commits into from
Jul 12, 2022
Merged

Rework streaming interceptors #316

merged 11 commits into from
Jul 12, 2022

Conversation

akshayjshah
Copy link
Member

@akshayjshah akshayjshah commented Jul 9, 2022

Streaming interceptors aren't very useful in their current state. Rather
than wrapping user code, they wrap the the networking primitives used by
the user's code. This is a subtle distinction, but it makes it
impossible to write a panic-recovering interceptor. It also makes common
server-side interceptors (e.g., authentication) needlessly difficult to
write.

This commit reworks the Sender and Receiver interfaces into a StreamingClientConn
and StreamingHandlerConn. Just as unary interceptors wrap a UnaryFunc, streaming
interceptors now wrap StreamingClientFuncs and StreamingHandlerFuncs. This ends up
being quite a bit easier to understand.

Connect's internals change a lot, but the user-visible changes are
modest. Today, we have:

type Sender interface {
  Spec() Spec
  Send(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close(error) error
}

type Receiver interface {
  Spec() Spec
  Receive(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close() error
}

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamContext(context.Context) context.Context
  WrapStreamSender(context.Context, Sender) Sender
  WrapStreamReceiver(context.Context, Receiver) Receiver
}

With this commit, we change to:

type StreamingClientConn interface {
  Spec() Spec

  Send(any) error
  RequestHeader() http.Header
  CloseRequest() error

  Receive(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  CloseResponse() error
}

type StreamingHandlerConn interface {
  Spec() Spec

  Receive(any) error
  RequestHeader() http.Header

  Send(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  Close(error) error
}

type StreamingClientFunc func(Spec, http.Header) StreamingClientConn

type StreamingHandlerFunc func(context.Context, StreamingHandlerConn) error

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamingClient(StreamingClientFunc) StreamingClientFunc
  WrapStreamingHandler(StreamingHandlerFunc) StreamingHandlerFunc
}

Fixes #296.

Streaming interceptors aren't very useful in their current state. Rather
than wrapping user code, they wrap the the networking primitives used by
the user's code. This is a subtle distinction, but it makes it
impossible to write a panic-recovering interceptor. It also makes common
server-side interceptors (e.g., authentication) needlessly difficult to
write.

This commit reworks the Sender and Receiver interfaces into a ClientConn
and HandlerConn. Just as unary interceptors wrap a UnaryFunc, streaming
interceptors now wrap ClientConnFuncs and HandlerConnFuncs. This ends up
being quite a bit easier to understand.

Connect's internals change a lot, but the user-visible changes are
modest. Today, we have:

```go
type Sender interface {
  Spec() Spec
  Send(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close(error) error
}

type Receiver interface {
  Spec() Spec
  Receive(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close() error
}

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamContext(context.Context) context.Context
  WrapStreamSender(context.Context, Sender) Sender
  WrapStreamReceiver(context.Context, Receiver) Receiver
}
```

With this commit, we change to:

```go
type ClientConn interface {
  Spec() Spec

  Send(any) error
  RequestHeader() http.Header
  CloseRequest() error

  Receive(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  CloseResponse() error
}

type HandlerConn interface {
  Spec() Spec

  Receive(any) error
  RequestHeader() http.Header

  Send(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  Close(error) error
}

type ClientConnFunc func(Spec, http.Header) ClientConn

type HandlerConnFunc func(context.Context, HandlerConn) error

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamingClient(ClientConnFunc) ClientConnFunc
  WrapStreamingHandler(HandlerConnFunc) HandlerConnFunc
}
```

Fixes #296.
@akshayjshah akshayjshah requested review from pkwarren, amckinney, doriable and bufdev and removed request for doriable July 9, 2022 06:44
_ = sender.Close(sender.Send(response.Any()))
mergeHeaders(conn.ResponseHeader(), response.Header())
mergeHeaders(conn.ResponseTrailer(), response.Trailer())
return conn.Send(response.Any())
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep!

@bufdev
Copy link
Member

bufdev commented Jul 11, 2022

I'm not going to go through all the code, but can we talk briefly offline about interfaces? Want to make sure I understand this.

Copy link
Member

@bufdev bufdev left a comment

Choose a reason for hiding this comment

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

@akshayjshah and I talked offline, going to make a slight naming change, otherwise LGTM

Rename to make it clearer that the new Conn interfaces are only relevant
to users of streaming RPCs.
@akshayjshah akshayjshah merged commit fa42a99 into main Jul 12, 2022
@akshayjshah akshayjshah deleted the ajs/interceptor branch July 12, 2022 20:57
akshayjshah added a commit that referenced this pull request Jul 26, 2023
* Rework streaming interceptors

Streaming interceptors aren't very useful in their current state. Rather
than wrapping user code, they wrap the the networking primitives used by
the user's code. This is a subtle distinction, but it makes it
impossible to write a panic-recovering interceptor. It also makes common
server-side interceptors (e.g., authentication) needlessly difficult to
write.

This commit reworks the Sender and Receiver interfaces into a ClientConn
and HandlerConn. Just as unary interceptors wrap a UnaryFunc, streaming
interceptors now wrap ClientConnFuncs and HandlerConnFuncs. This ends up
being quite a bit easier to understand.

Connect's internals change a lot, but the user-visible changes are
modest. Today, we have:

```go
type Sender interface {
  Spec() Spec
  Send(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close(error) error
}

type Receiver interface {
  Spec() Spec
  Receive(any) error
  Header() http.Header
  Trailer() (http.Header, bool)
  Close() error
}

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamContext(context.Context) context.Context
  WrapStreamSender(context.Context, Sender) Sender
  WrapStreamReceiver(context.Context, Receiver) Receiver
}
```

With this commit, we change to:

```go
type ClientConn interface {
  Spec() Spec

  Send(any) error
  RequestHeader() http.Header
  CloseRequest() error

  Receive(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  CloseResponse() error
}

type HandlerConn interface {
  Spec() Spec

  Receive(any) error
  RequestHeader() http.Header

  Send(any) error
  ResponseHeader() http.Header
  ResponseTrailer() http.Header
  Close(error) error
}

type ClientConnFunc func(Spec, http.Header) ClientConn

type HandlerConnFunc func(context.Context, HandlerConn) error

type Interceptor interface {
  WrapUnary(UnaryFunc) UnaryFunc
  WrapStreamingClient(ClientConnFunc) ClientConnFunc
  WrapStreamingHandler(HandlerConnFunc) HandlerConnFunc
}
```

Fixes #296.

* Fix flaky test

* Update client_stream.go

Co-authored-by: Philip K. Warren <[email protected]>

* Update client_stream.go

Co-authored-by: Philip K. Warren <[email protected]>

* Update interceptor_ext_test.go

Co-authored-by: Philip K. Warren <[email protected]>

* Update interceptor_ext_test.go

Co-authored-by: Philip K. Warren <[email protected]>

* Also remove closing parens

* Restructure concurrency GoDoc for ClientConn

* Make missing trailers tests handle timing non-determinism

* Avoid a few map allocations

* Rename to Streaming*Conn

Rename to make it clearer that the new Conn interfaces are only relevant
to users of streaming RPCs.

Co-authored-by: Philip K. Warren <[email protected]>
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.

More request information in Interceptor.WrapStreamContext()
3 participants