A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://pkg.go.dev/github.com/connectrpc/vanguard-go below:

vanguard package - connectrpc.com/vanguard - Go Packages

Package vanguard provides a transcoder that acts like middleware for your RPC handlers, augmenting them to support additional protocols or message formats, including REST+JSON. The transcoder also acts as a router, handling dispatch of configured REST-ful URI paths to the right RPC handlers.

Use NewService or NewServiceWithSchema to create Service definitions wrap your existing HTTP and/or RPC handlers. Then pass those services to NewTranscoder.

package main

import (
	"bytes"
	"context"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"time"

	"connectrpc.com/connect"
	"connectrpc.com/vanguard"

	testv1 "connectrpc.com/vanguard/internal/gen/vanguard/test/v1"
	"connectrpc.com/vanguard/internal/gen/vanguard/test/v1/testv1connect"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	// This example shows Vanguard adding REST support to an RPC server built
	// with Connect. (To add REST, gRPC-Web, and Connect support to servers built
	// with grpc-go, use the connectrpc.com/vanguard/vanguardgrpc sub-package.)
	logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)

	// libraryRPC is an implementation of the testv1connect.LibraryService RPC
	// server. It's a pure RPC server, without any hand-written translation to or
	// from RESTful semantics.
	svc := &libraryRPC{}
	rpcRoute, rpcHandler := testv1connect.NewLibraryServiceHandler(svc)

	// Using Vanguard, the server can also accept RESTful requests. The Vanguard
	// Transcoder handles both REST and RPC traffic, so there's no need to mount
	// the RPC-only handler.
	services := []*vanguard.Service{vanguard.NewService(rpcRoute, rpcHandler)}
	transcoder, err := vanguard.NewTranscoder(services)
	if err != nil {
		logger.Println(err)
		return
	}

	// We can use any server that works with http.Handlers. Since this is a
	// testable example, we're using httptest.
	server := httptest.NewServer(transcoder)
	defer server.Close()

	// With the server running, we can make a RESTful call.
	client := server.Client()
	book := &testv1.Book{
		Title:       "2001: A Space Odyssey",
		Author:      "Arthur C. Clarke",
		Description: "A space voyage to Jupiter awakens the crew's intelligence.",
		Labels: map[string]string{
			"genre": "science fiction",
		},
	}
	body, err := protojson.Marshal(book)
	if err != nil {
		logger.Println(err)
		return
	}

	req, err := http.NewRequestWithContext(
		context.Background(), http.MethodPost,
		server.URL+"/v1/shelves/top/books",
		bytes.NewReader(body),
	)
	if err != nil {
		logger.Println(err)
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.URL.RawQuery = "book_id=2&request_id=123"

	rsp, err := client.Do(req)
	if err != nil {
		logger.Println(err)
		return
	}
	defer rsp.Body.Close()
	logger.Println(rsp.Status)
	logger.Println(rsp.Header.Get("Content-Type"))

	body, err = io.ReadAll(rsp.Body)
	if err != nil {
		logger.Println(err)
		return
	}
	if err := protojson.Unmarshal(body, book); err != nil {
		logger.Println(err)
		return
	}
	logger.Println(book.GetAuthor())
}

type libraryRPC struct {
	testv1connect.UnimplementedLibraryServiceHandler
}

func (s *libraryRPC) GetBook(_ context.Context, req *connect.Request[testv1.GetBookRequest]) (*connect.Response[testv1.Book], error) {
	msg := req.Msg
	rsp := connect.NewResponse(&testv1.Book{
		Name:        msg.GetName(),
		Parent:      strings.Join(strings.Split(msg.GetName(), "/")[:2], "/"),
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       "Do Androids Dream of Electric Sheep?",
		Author:      "Philip K. Dick",
		Description: "Have you seen Blade Runner?",
		Labels: map[string]string{
			"genre": "science fiction",
		},
	})
	return rsp, nil
}

func (s *libraryRPC) CreateBook(_ context.Context, req *connect.Request[testv1.CreateBookRequest]) (*connect.Response[testv1.Book], error) {
	msg := req.Msg
	book := req.Msg.GetBook()
	rsp := connect.NewResponse(&testv1.Book{
		Name:        strings.Join([]string{msg.GetParent(), "books", msg.GetBookId()}, "/"),
		Parent:      msg.GetParent(),
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       book.GetTitle(),
		Author:      book.GetAuthor(),
		Description: book.GetDescription(),
		Labels:      book.GetLabels(),
	})
	return rsp, nil
}
Output:

200 OK
application/json
Arthur C. Clarke
package main

import (
	"context"
	"errors"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"connectrpc.com/connect"
	"connectrpc.com/vanguard"

	testv1 "connectrpc.com/vanguard/internal/gen/vanguard/test/v1"
	"connectrpc.com/vanguard/internal/gen/vanguard/test/v1/testv1connect"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	// This example shows Vanguard adding RPC support to an REST server. This
	// lets organizations use RPC clients in new codebases without rewriting
	// existing REST services.
	logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)

	// libraryREST is an http.Handler that implements a RESTful server. The
	// implementation doesn't use Protobuf or RPC directly.
	restHandler := &libraryREST{}

	// Using Vanguard, the server can also accept RPC traffic. The Vanguard
	// Transcoder handles both REST and RPC traffic, so there's no need to mount
	// the REST-only handler.
	services := []*vanguard.Service{vanguard.NewService(
		testv1connect.LibraryServiceName,
		restHandler,
		// This tells vanguard that the service implementation only supports REST.
		vanguard.WithTargetProtocols(vanguard.ProtocolREST),
	)}
	transcoder, err := vanguard.NewTranscoder(services)
	if err != nil {
		logger.Println(err)
		return
	}

	// We can serve RPC and REST traffic using any server that works with
	// http.Handlers. Since this is a testable example, we're using httptest.
	server := httptest.NewServer(transcoder)
	defer server.Close()

	// With the server running, we can make an RPC call using a generated client.
	client := testv1connect.NewLibraryServiceClient(server.Client(), server.URL)
	rsp, err := client.GetBook(
		context.Background(),
		connect.NewRequest(&testv1.GetBookRequest{
			Name: "shelves/top/books/123",
		}),
	)
	if err != nil {
		logger.Println(err)
		return
	}
	logger.Println(rsp.Msg.GetDescription())
}

type libraryREST struct {
	libraryRPC
}

func (s *libraryREST) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
	urlPath := []byte(req.URL.Path)
	ctx := req.Context()
	var msg proto.Message
	var err error
	switch req.Method {
	case http.MethodGet:
		switch {
		case regexp.MustCompile("/v1/shelves/.*/books/.*").Match(urlPath):
			got, gotErr := s.GetBook(ctx, connect.NewRequest(&testv1.GetBookRequest{
				Name: req.URL.Path[len("/v1/"):],
			}))
			msg, err = got.Msg, gotErr
		default:
			err = connect.NewError(connect.CodeNotFound, errors.New("method not found"))
		}
	case http.MethodPost:
		switch {
		case regexp.MustCompile("/v1/shelves/.*").Match(urlPath):
			var book testv1.Book
			body, _ := io.ReadAll(req.Body)
			_ = protojson.Unmarshal(body, &book)
			got, gotErr := s.CreateBook(ctx, connect.NewRequest(&testv1.CreateBookRequest{
				Parent:    req.URL.Path[len("/v1/"):],
				BookId:    req.URL.Query().Get("book_id"),
				Book:      &book,
				RequestId: req.URL.Query().Get("request_id"),
			}))
			msg, err = got.Msg, gotErr
		default:
			err = connect.NewError(connect.CodeNotFound, errors.New("method not found"))
		}
	default:
		err = connect.NewError(connect.CodeNotFound, errors.New("method not found"))
	}
	rsp.Header().Set("Content-Type", "application/json")
	var body []byte
	if err != nil {
		code := connect.CodeInternal
		if ce := (*connect.Error)(nil); errors.As(err, &ce) {
			code = ce.Code()
		}
		body = []byte(`{"code":` + strconv.Itoa(int(code)) +
			`, "message":"` + err.Error() + `"}`)
	} else {
		body, _ = protojson.Marshal(msg)
	}
	rsp.WriteHeader(http.StatusOK)
	_, _ = rsp.Write(body)
}

type libraryRPC struct {
	testv1connect.UnimplementedLibraryServiceHandler
}

func (s *libraryRPC) GetBook(_ context.Context, req *connect.Request[testv1.GetBookRequest]) (*connect.Response[testv1.Book], error) {
	msg := req.Msg
	rsp := connect.NewResponse(&testv1.Book{
		Name:        msg.GetName(),
		Parent:      strings.Join(strings.Split(msg.GetName(), "/")[:2], "/"),
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       "Do Androids Dream of Electric Sheep?",
		Author:      "Philip K. Dick",
		Description: "Have you seen Blade Runner?",
		Labels: map[string]string{
			"genre": "science fiction",
		},
	})
	return rsp, nil
}

func (s *libraryRPC) CreateBook(_ context.Context, req *connect.Request[testv1.CreateBookRequest]) (*connect.Response[testv1.Book], error) {
	msg := req.Msg
	book := req.Msg.GetBook()
	rsp := connect.NewResponse(&testv1.Book{
		Name:        strings.Join([]string{msg.GetParent(), "books", msg.GetBookId()}, "/"),
		Parent:      msg.GetParent(),
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       book.GetTitle(),
		Author:      book.GetAuthor(),
		Description: book.GetDescription(),
		Labels:      book.GetLabels(),
	})
	return rsp, nil
}
Output:

Have you seen Blade Runner?
View Source
const (
	
	CompressionGzip = "gzip"
	
	
	CompressionIdentity = "identity"

	
	CodecProto = "proto"
	
	CodecJSON = "json"

	
	
	
	
	DefaultMaxMessageBufferBytes = math.MaxUint32
	
	
	
	
	DefaultMaxGetURLBytes = 8 * 1024
)

This section is empty.

This section is empty.

Codec is a message encoding format. It handles unmarshalling messages from bytes and back.

JSONCodec implements Codec, StableCodec, and RESTCodec for the JSON format. It uses the protojson package for its implementation.

NewJSONCodec is the default codec factory used for the codec named "json". The given resolver is used to unmarshal extensions and also to marshal and unmarshal instances of google.protobuf.Any.

By default, the returned codec is configured to emit unpopulated fields when marshalling and to discard unknown fields when unmarshalling.

IsBinary returns false, indicating that JSON is a text format. Implements StableCodec.

MarshalAppend implements Codec.

Name returns "json". Implements Codec.

Unmarshal implements Codec.

type ProtoCodec struct {
	
}

ProtoCodec implements Codec and StableCodec for the binary Protobuf format. It uses the proto package for its implementation.

NewProtoCodec is the default codec factory used for the codec name "proto". The given resolver is used to unmarshal extensions.

IsBinary returns true, indicating that Protobuf is a binary format. Implements StableCodec.

MarshalAppend implements Codec.

Name returns "proto". Implements Codec.

Unmarshal implements Codec.

Protocol represents an on-the-wire protocol for RPCs.

RESTCodec is a Codec with additional methods for marshalling and unmarshalling individual fields of a message. This is necessary to support query string variables and request and response bodies whose value is a specific field, not an entire message. The extra methods are only used by the REST protocol.

Service represents the configuration for a single RPC service.

NewService creates a new service definition for the given service path and handler. The service path must be the service's fully-qualified name, with an optional leading and trailing slash. This means you can provide generated constants for service names or you can provide the path returned by a New*Handler function generated by the [Protobuf plugin for Connect]. In fact, if you do not need to specify any service-specific options, you can directly wrap the call to the generated constructor with NewService:

vanguard.NewService(elizav1connect.NewElizaServiceHandler(elizaImpl))

If the given service path does not correspond to a known service (one whose schema is registered with the Protobuf runtime, usually from generated code), NewTranscoder will return an error. For these cases, where the corresponding service schema may be dynamically retrieved, use NewServiceWithSchema instead.

NewServiceWithSchema creates a new service using the given schema and handler. This option is appropriate for use with dynamic schemas.

The default type resolver for the service will use protoregistry.GlobalTypes if the given service matches a descriptor of the same name registered in protoregistry.GlobalFiles. Otherwise, the default resolver will use dynamic messages for the given service's request and response types. In either case, the default resolver will fall back to protoregistry.GlobalTypes for resolving extensions and message types for messages inside anypb.Any values.

type ServiceOption interface {
	
}

A ServiceOption configures how a Transcoder handles requests to a particular RPC service. ServiceOptions can be passed to NewService and NewServiceWithSchema. Default ServiceOptions, that apply to all services, can be defined by passing a WithDefaultServiceOptions option to NewTranscoder. This is useful when all or many services use the same options.

WithMaxGetURLBytes returns a service option that limits the size of URLs with the Connect unary protocol using the GET HTTP method. If a URL's length would exceed this limit, the POST HTTP method will be used instead (and the request contents moved from the URL to the body).

If set to zero or a negative value, a limit of 8 KB will be used.

WithMaxMessageBufferBytes returns a service option that limits buffering of data when handling the service to the given limit. If any payload in a request or response exceeds this, the RPC will fail with a "resource exhausted" error.

If set to zero or a negative value, a limit of 4 GB will be used.

WithNoTargetCompression returns a service option indicating that the server handler does not support compression.

WithTargetCodecs returns a service option indicating that the service handler supports the given codecs. By default, the handler is assumed only to support the "proto" codec.

WithTargetCompression returns a service option indicating that the service handler supports the given compression algorithms. By default, the handler is assumed only to support the "gzip" compression algorithm.

To configure the handler to not use any compression, one could use this option and supply no names. However, to make this scenario more readable, prefer WithNoTargetCompression instead.

WithTargetProtocols returns a service option indicating that the service handler supports the listed protocols. By default, the handler is assumed to support all but the REST protocol, which is true if the handler is a Connect handler (created using generated code from the protoc-gen-connect-go plugin or an explicit call to connect.NewUnaryHandler or its streaming equivalents).

WithTypeResolver returns a service option to use the given resolver when serializing and de-serializing messages. If not specified, this defaults to protoregistry.GlobalTypes.

StableCodec is an encoding format that can produce stable, deterministic output when marshalling data. This stable form is the result of the MarshalAppendStable method. So the codec's MarshalAppend method is free to produce unstable/non-deterministic output, if useful for improved performance. The performance penalty of stable output will only be taken when necessary.

This is used to encode messages that end up in the URL query string, for the Connect protocol when unary methods use the HTTP GET method. If the codec in use does not implement StableCodec then HTTP GET methods will not be used; a Transcoder will send all unary RPCs that use the Connect protocol and that codec as POST requests.

type Transcoder struct {
	
}

Transcoder is a Vanguard handler which acts like a router and a middleware. It transforms all supported input protocols (Connect, gRPC, gRPC-Web, REST) into a protocol that the service handlers support. It can do simple routing based on RPC method name, for simple protocols like Connect, gRPC, and gRPC-Web; but it can also route based on REST-ful URI paths configured with HTTP transcoding annotations.

See the package-level examples for sample usage.

NewTranscoder creates a new transcoder that handles the given services, with the configuration described by the given options. A non-nil error is returned if there is an issue with this configuration.

The returned handler does the routing and dispatch to the RPC handlers associated with each provided service. Routing supports more than just the service path provided to NewService since HTTP transcoding annotations are used to also support REST-ful URI paths for each method.

The returned handler also acts like a middleware, transparently "upgrading" the RPC handlers to support incoming request protocols they wouldn't otherwise support. This can be used to upgrade Connect handlers to support REST requests (based on HTTP transcoding configuration) or gRPC handlers to support Connect, gRPC-Web, or REST. This can even be used with a reverse proxy handler, to translate all incoming requests to a single protocol that another backend server supports.

If any options given implement ServiceOption, they are treated as default service options and apply to all configured services, unless overridden by a particular service.

ServeHTTP implements http.Handler, dispatching requests for configured services and transcoding protocols and message encoding as needed.

type TranscoderOption interface {
	
}

TranscoderOption is an option used to configure a Transcoder. See NewTranscoder.

WithCodec returns an option that instructs the transcoder to use the given function for instantiating codec implementations. The function is immediately invoked in order to determine the name of the codec. The name reported by codecs created with the function should all return the same name. (Otherwise, behavior is undefined.)

By default, "proto" and "json" codecs are supported using default options. This option can be used to support additional codecs or to override the default implementations (such as to change serialization or de-serialization options).

WithCompression returns an option that instructs the transcoder to use the given functions to instantiate compressors and decompressors for the given compression algorithm name.

By default, "gzip" compression is supported using default options. This option can be used to support additional compression algorithms or to override the default "gzip" implementation (such as to change the compression level).

WithDefaultServiceOptions returns an option that configures the given service options as defaults. They will apply to all services passed to NewTranscoder, except where overridden via an explicit ServiceOption passed to NewService / NewServiceWithSchema.

Providing multiple instances of this option will be cumulative: the union of all defaults are used with later options overriding any previous options.

WithRules returns an option that adds HTTP transcoding configuration to the set of configured services. The given rules must have a selector defined, and the selector must match at least one configured method. Otherwise, NewTranscoder will report a configuration error.

func WithUnknownHandler ΒΆ

WithUnknownHandler returns an option that instructs the transcoder to delegate to the given handler when a request arrives for an unknown endpoint. If no such option is used, the transcoder will reply with a simple "404 Not Found" error.

TypeResolver can resolve message and extension types and is used to instantiate messages as needed for the middleware to serialize/de-serialize request and response payloads.

Implementations of this interface should be comparable, so they can be used as map keys. Typical implementations are pointers to structs, which are suitable.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4