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 }