11import * as _ from 'lodash' ;
2- import { MessageBody } from '../../types' ;
2+
3+ import { Headers , MessageBody } from '../../types' ;
34import {
45 isProbablyProtobuf ,
5- isValidProtobuf
6+ isValidProtobuf ,
7+ isProbablyGrpcProto ,
8+ isValidGrpcProto ,
69} from '../../util/protobuf' ;
710
811// Simplify a mime type as much as we can, without throwing any errors
@@ -21,7 +24,7 @@ export const getBaseContentType = (mimeType: string | undefined) => {
2124 return type + '/' + combinedSubTypes ;
2225 }
2326
24- // Otherwise, wr collect a list of types from most specific to most generic: [svg, xml] for image/svg+xml
27+ // Otherwise, we collect a list of types from most specific to most generic: [svg, xml] for image/svg+xml
2528 // and then look through in order to see if there are any matches here:
2629 const subTypes = combinedSubTypes . split ( '+' ) ;
2730 const possibleTypes = subTypes . map ( st => type + '/' + st ) ;
@@ -112,6 +115,9 @@ const mimeTypeToContentTypeMap: { [mimeType: string]: ViewableContentType } = {
112115 'application/x-protobuffer' : 'protobuf' , // Commonly seen in Google apps
113116
114117 'application/grpc+proto' : 'grpc-proto' , // Used in GRPC requests (protobuf but with special headers)
118+ 'application/grpc+protobuf' : 'grpc-proto' ,
119+ 'application/grpc-proto' : 'grpc-proto' ,
120+ 'application/grpc-protobuf' : 'grpc-proto' ,
115121
116122 'application/octet-stream' : 'raw'
117123} as const ;
@@ -147,19 +153,32 @@ export function getDefaultMimeType(contentType: ViewableContentType): string {
147153 return _ . findKey ( mimeTypeToContentTypeMap , ( c ) => c === contentType ) ! ;
148154}
149155
150- function isValidBase64Byte ( byte : number ) {
156+ function isAlphaNumOrEquals ( byte : number ) {
151157 return ( byte >= 65 && byte <= 90 ) || // A-Z
152158 ( byte >= 97 && byte <= 122 ) || // a-z
153159 ( byte >= 48 && byte <= 57 ) || // 0-9
154- byte === 43 || // +
155- byte === 47 || // /
156160 byte === 61 ; // =
157161}
158162
163+ function isValidStandardBase64Byte ( byte : number ) {
164+ // + / (standard)
165+ return byte === 43 ||
166+ byte === 47 ||
167+ isAlphaNumOrEquals ( byte ) ;
168+ }
169+
170+ function isValidURLSafeBase64Byte ( byte : number ) {
171+ // - _ (URL-safe version)
172+ return byte === 45 ||
173+ byte === 95 ||
174+ isAlphaNumOrEquals ( byte ) ;
175+ }
176+
159177export function getCompatibleTypes (
160178 contentType : ViewableContentType ,
161179 rawContentType : string | undefined ,
162- body : MessageBody | Buffer | undefined
180+ body : MessageBody | Buffer | undefined ,
181+ headers ?: Headers ,
163182) : ViewableContentType [ ] {
164183 let types = new Set ( [ contentType ] ) ;
165184
@@ -180,33 +199,41 @@ export function getCompatibleTypes(
180199 types . add ( 'xml' ) ;
181200 }
182201
183- if ( ! types . has ( 'grpc-proto' ) && rawContentType === 'application/grpc' ) {
184- types . add ( 'grpc-proto' )
185- }
186-
187202 if (
188203 body &&
189- isProbablyProtobuf ( body ) &&
190204 ! types . has ( 'protobuf' ) &&
191205 ! types . has ( 'grpc-proto' ) &&
206+ isProbablyProtobuf ( body ) &&
192207 // If it's probably unmarked protobuf, and it's a manageable size, try
193208 // parsing it just to check:
194209 ( body . length < 100_000 && isValidProtobuf ( body ) )
195210 ) {
196211 types . add ( 'protobuf' ) ;
197212 }
198213
214+ if (
215+ body &&
216+ ! types . has ( 'grpc-proto' ) &&
217+ isProbablyGrpcProto ( body , headers ?? { } ) &&
218+ // If it's probably unmarked gRPC, and it's a manageable size, try
219+ // parsing it just to check:
220+ ( body . length < 100_000 && isValidGrpcProto ( body , headers ?? { } ) )
221+ ) {
222+ types . add ( 'grpc-proto' ) ;
223+ }
224+
199225 // SVGs can always be shown as XML
200226 if ( rawContentType && rawContentType . startsWith ( 'image/svg' ) ) {
201227 types . add ( 'xml' ) ;
202228 }
203229
204230 if (
205231 body &&
206- body . length > 0 &&
207- body . length % 4 === 0 && // Multiple of 4 bytes
208- body . length < 1000 * 100 && // < 100 KB of content
209- body . every ( isValidBase64Byte )
232+ ! types . has ( 'base64' ) &&
233+ body . length >= 8 &&
234+ // body.length % 4 === 0 && // Multiple of 4 bytes (final padding may be omitted)
235+ body . length < 100_000 && // < 100 KB of content
236+ ( body . every ( isValidStandardBase64Byte ) || body . every ( isValidURLSafeBase64Byte ) )
210237 ) {
211238 types . add ( 'base64' ) ;
212239 }
0 commit comments