encoding.go

  1/*
  2 *
  3 * Copyright 2017 gRPC authors.
  4 *
  5 * Licensed under the Apache License, Version 2.0 (the "License");
  6 * you may not use this file except in compliance with the License.
  7 * You may obtain a copy of the License at
  8 *
  9 *     http://www.apache.org/licenses/LICENSE-2.0
 10 *
 11 * Unless required by applicable law or agreed to in writing, software
 12 * distributed under the License is distributed on an "AS IS" BASIS,
 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14 * See the License for the specific language governing permissions and
 15 * limitations under the License.
 16 *
 17 */
 18
 19// Package encoding defines the interface for the compressor and codec, and
 20// functions to register and retrieve compressors and codecs.
 21//
 22// # Experimental
 23//
 24// Notice: This package is EXPERIMENTAL and may be changed or removed in a
 25// later release.
 26package encoding
 27
 28import (
 29	"io"
 30	"strings"
 31
 32	"google.golang.org/grpc/internal/grpcutil"
 33)
 34
 35// Identity specifies the optional encoding for uncompressed streams.
 36// It is intended for grpc internal use only.
 37const Identity = "identity"
 38
 39// Compressor is used for compressing and decompressing when sending or
 40// receiving messages.
 41//
 42// If a Compressor implements `DecompressedSize(compressedBytes []byte) int`,
 43// gRPC will invoke it to determine the size of the buffer allocated for the
 44// result of decompression.  A return value of -1 indicates unknown size.
 45type Compressor interface {
 46	// Compress writes the data written to wc to w after compressing it.  If an
 47	// error occurs while initializing the compressor, that error is returned
 48	// instead.
 49	Compress(w io.Writer) (io.WriteCloser, error)
 50	// Decompress reads data from r, decompresses it, and provides the
 51	// uncompressed data via the returned io.Reader.  If an error occurs while
 52	// initializing the decompressor, that error is returned instead.
 53	Decompress(r io.Reader) (io.Reader, error)
 54	// Name is the name of the compression codec and is used to set the content
 55	// coding header.  The result must be static; the result cannot change
 56	// between calls.
 57	Name() string
 58}
 59
 60var registeredCompressor = make(map[string]Compressor)
 61
 62// RegisterCompressor registers the compressor with gRPC by its name.  It can
 63// be activated when sending an RPC via grpc.UseCompressor().  It will be
 64// automatically accessed when receiving a message based on the content coding
 65// header.  Servers also use it to send a response with the same encoding as
 66// the request.
 67//
 68// NOTE: this function must only be called during initialization time (i.e. in
 69// an init() function), and is not thread-safe.  If multiple Compressors are
 70// registered with the same name, the one registered last will take effect.
 71func RegisterCompressor(c Compressor) {
 72	registeredCompressor[c.Name()] = c
 73	if !grpcutil.IsCompressorNameRegistered(c.Name()) {
 74		grpcutil.RegisteredCompressorNames = append(grpcutil.RegisteredCompressorNames, c.Name())
 75	}
 76}
 77
 78// GetCompressor returns Compressor for the given compressor name.
 79func GetCompressor(name string) Compressor {
 80	return registeredCompressor[name]
 81}
 82
 83// Codec defines the interface gRPC uses to encode and decode messages.  Note
 84// that implementations of this interface must be thread safe; a Codec's
 85// methods can be called from concurrent goroutines.
 86type Codec interface {
 87	// Marshal returns the wire format of v.
 88	Marshal(v any) ([]byte, error)
 89	// Unmarshal parses the wire format into v.
 90	Unmarshal(data []byte, v any) error
 91	// Name returns the name of the Codec implementation. The returned string
 92	// will be used as part of content type in transmission.  The result must be
 93	// static; the result cannot change between calls.
 94	Name() string
 95}
 96
 97var registeredCodecs = make(map[string]any)
 98
 99// RegisterCodec registers the provided Codec for use with all gRPC clients and
100// servers.
101//
102// The Codec will be stored and looked up by result of its Name() method, which
103// should match the content-subtype of the encoding handled by the Codec.  This
104// is case-insensitive, and is stored and looked up as lowercase.  If the
105// result of calling Name() is an empty string, RegisterCodec will panic. See
106// Content-Type on
107// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
108// more details.
109//
110// NOTE: this function must only be called during initialization time (i.e. in
111// an init() function), and is not thread-safe.  If multiple Codecs are
112// registered with the same name, the one registered last will take effect.
113func RegisterCodec(codec Codec) {
114	if codec == nil {
115		panic("cannot register a nil Codec")
116	}
117	if codec.Name() == "" {
118		panic("cannot register Codec with empty string result for Name()")
119	}
120	contentSubtype := strings.ToLower(codec.Name())
121	registeredCodecs[contentSubtype] = codec
122}
123
124// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is
125// registered for the content-subtype.
126//
127// The content-subtype is expected to be lowercase.
128func GetCodec(contentSubtype string) Codec {
129	c, _ := registeredCodecs[contentSubtype].(Codec)
130	return c
131}