unknown

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 4, 2024 License: MIT Imports: 5 Imported by: 0

README

github.com/sudorandom/extraconnect-go/unknown

This package contains an interceptor for ConnectRPC clients and servers that tells you if you are receiving protobuf messages with unknown fields. This is useful to know when you should upgrade your gRPC clients or servers to the latest version.

Server Examples

Short example:

import (
    "log/slog"
    "github.com/sudorandom/extraconnect-go/unknown"
)

...
unknown.NewInterceptor(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
    slog.Warn("received a protobuf message with unknown fields", slog.Any("spec", spec), slog.Any("msg", msg))
    return nil
})

Full:

import (
    "connectrpc.com/connect"
    "github.com/sudorandom/extraconnect-go/unknown"
    "example/gen/greet/v1/greetv1connect"
)

func main() {
    greeter := &GreetServer{}
    mux := http.NewServeMux()
    path, handler := greetv1connect.NewGreetServiceHandler(greeter, connect.WithInterceptors(
        unknown.NewInterceptor(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
            return connect.NewError(connect.InvalidArgument, err)
        }),
    ))
    mux.Handle(path, handler)
    http.ListenAndServe("localhost:8080", h2c.NewHandler(mux, &http2.Server{}))
}

The first example simply emits a warning log and the second example will fail the request if the server receives a message with unknown fields. You can decide what to do. Here are some ideas:

  • Add to a metric that counts how often this happens
  • Fail the request/response; maybe the most useful in non-production integration environments
  • Emit a log
  • Add an annotation to the context to be used in the handler
  • ???

Client Examples

And it works the same for clients, too:

package main

import (
    "context"
    "log/slog"
    "net/http"

    greetv1 "example/gen/greet/v1"
    "example/gen/greet/v1/greetv1connect"

    "connectrpc.com/connect"
)

func main() {
    client := greetv1connect.NewGreetServiceClient(
        http.DefaultClient,
        "http://localhost:8080",
        connect.WithInterceptors(
            unknown.NewInterceptor(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
                slog.Warn("received a protobuf message with unknown fields", slog.Any("spec", spec), slog.Any("msg", msg))
                return nil
            })
        ),
    )
    res, err := client.Greet(
        context.Background(),
        connect.NewRequest(&greetv1.GreetRequest{Name: "Jane"}),
    )
    if err != nil {
        slog.Error(err.Error())
        return
    }
    slog.Info(res.Msg.Greeting)
}

Why?

gRPC systems can be quite complex. When making additions to protobuf files the server or the client often gets updated at different times. In a perfect world, this would all be synchronized. But we live in reality. Sometimes release schedules differ between components. Sometimes you just forget to update a component. Many times you might be consuming a gRPC service managed by another team and they don't tell you that they're changing things. I believe this interceptor helps with all of these cases. It allows you to raise the issue before it becomes a problem.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MessageHasUnknownFields

func MessageHasUnknownFields(msg protoreflect.Message) bool

MessageHasUnknownFields returns true if the given protoreflect.Message has unknown fields.

func NewInterceptor

func NewInterceptor(callback UnknownCallback) *interceptor

NewInterceptor creates a new interceptor appropriate to pass into a new ConnectRPC client or server. The given callback is called whenever a message is detected to have an unknown field. That means a field is being given to this client/server that does not. The callback can decide what to do. Any error returned from the callback will be used as an error in the request or response.

Types

type UnknownCallback

type UnknownCallback func(context.Context, connect.Spec, proto.Message) error

UnknownCallback is called whenever there is an unknown field. Note that the proto.Message is the base protobuf message for the RPC call. The message with the unknown field(s) can be nested deeper into this given message.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL