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