go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/grpc/prpc/format.go (about)

     1  // Copyright 2016 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package prpc
    16  
    17  import (
    18  	"fmt"
    19  	"mime"
    20  )
    21  
    22  const (
    23  	// ContentTypePRPC is the formal MIME content type for pRPC messages.
    24  	ContentTypePRPC = "application/prpc"
    25  	// ContentTypeJSON is the JSON content type.
    26  	ContentTypeJSON = "application/json"
    27  
    28  	mtPRPCEncoding       = "encoding"
    29  	mtPRPCEncodingBinary = "binary"
    30  	mtPRPCEncodingJSONPB = "json"
    31  	mtPRPCEncodingText   = "text"
    32  	mtPRPCBinary         = ContentTypePRPC + "; " + mtPRPCEncoding + "=" + mtPRPCEncodingBinary
    33  	mtPRPCJSONPB         = ContentTypeJSON
    34  	mtPRPCJSONPBLegacy   = ContentTypePRPC + "; " + mtPRPCEncoding + "=" + mtPRPCEncodingJSONPB
    35  	mtPRPCText           = ContentTypePRPC + "; " + mtPRPCEncoding + "=" + mtPRPCEncodingText
    36  
    37  	// JSONPBPrefix is prepended to a message in JSONPB format to avoid CSRF.
    38  	JSONPBPrefix = ")]}'\n"
    39  )
    40  
    41  var bytesJSONPBPrefix = []byte(JSONPBPrefix)
    42  
    43  // Format is the pRPC protobuf wire format specification.
    44  type Format int
    45  
    46  // (Ordered by preference).
    47  const (
    48  	// FormatBinary indicates that a message is encoded as a raw binary protobuf.
    49  	FormatBinary Format = iota
    50  	// FormatJSONPB indicates that a message is encoded as a JSON-serialized
    51  	// protobuf.
    52  	FormatJSONPB
    53  	// FormatText indicates that a message is encoded as a text protobuf.
    54  	FormatText
    55  )
    56  
    57  func (f Format) String() string {
    58  	switch f {
    59  	case FormatBinary:
    60  		return "FormatBinary"
    61  	case FormatJSONPB:
    62  		return "FormatJSONPB"
    63  	case FormatText:
    64  		return "FormatText"
    65  	}
    66  	return fmt.Sprintf("Unknown Format(%d)", int(f))
    67  }
    68  
    69  // FormatFromContentType converts Content-Type header value from a request to a
    70  // format.
    71  // Can return only FormatBinary, FormatJSONPB or FormatText.
    72  // In case of an error, format is undefined.
    73  func FormatFromContentType(v string) (Format, error) {
    74  	if v == "" {
    75  		return FormatBinary, nil
    76  	}
    77  	return FormatFromMediaType(v)
    78  }
    79  
    80  // FormatFromMediaType converts a media type ContentType and its parameters
    81  // into a pRPC Format.
    82  // Can return only FormatBinary, FormatJSONPB or FormatText.
    83  // In case of an error, format is undefined.
    84  func FormatFromMediaType(mediaType string) (Format, error) {
    85  	mt, params, err := mime.ParseMediaType(mediaType)
    86  	if err != nil {
    87  		return 0, err
    88  	}
    89  	switch mt {
    90  	case ContentTypePRPC:
    91  		for k := range params {
    92  			if k != mtPRPCEncoding {
    93  				return 0, fmt.Errorf("unexpected parameter %q", k)
    94  			}
    95  		}
    96  		return FormatFromEncoding(params[mtPRPCEncoding])
    97  
    98  	case ContentTypeJSON:
    99  		return FormatJSONPB, nil
   100  
   101  	case "", "*/*", "application/*", "application/grpc":
   102  		return FormatBinary, nil
   103  
   104  	default:
   105  		return 0, fmt.Errorf("unknown content type: %q", mt)
   106  	}
   107  }
   108  
   109  // FormatFromEncoding converts a media type encoding parameter into a pRPC
   110  // Format.
   111  // Can return only FormatBinary, FormatJSONPB or FormatText.
   112  // In case of an error, format is undefined.
   113  func FormatFromEncoding(v string) (Format, error) {
   114  	switch v {
   115  	case "", mtPRPCEncodingBinary:
   116  		return FormatBinary, nil
   117  	case mtPRPCEncodingJSONPB:
   118  		return FormatJSONPB, nil
   119  	case mtPRPCEncodingText:
   120  		return FormatText, nil
   121  	default:
   122  		return 0, fmt.Errorf(`invalid encoding parameter: %q. Valid values: `+
   123  			`"`+mtPRPCEncodingBinary+`", `+
   124  			`"`+mtPRPCEncodingJSONPB+`", `+
   125  			`"`+mtPRPCEncodingText+`"`, v)
   126  	}
   127  }
   128  
   129  // MediaType returns a full media type for f.
   130  func (f Format) MediaType() string {
   131  	switch f {
   132  	case FormatJSONPB:
   133  		return mtPRPCJSONPB
   134  	case FormatText:
   135  		return mtPRPCText
   136  	case FormatBinary:
   137  		fallthrough
   138  	default:
   139  		return mtPRPCBinary
   140  	}
   141  }