LCOV - code coverage report
Current view: top level - waltz/grpc - fd_grpc_codec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 113 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 5 0.0 %

          Line data    Source code
       1             : #include "fd_grpc_codec.h"
       2             : #include "../h2/fd_hpack.h"
       3             : #include "../h2/fd_hpack_wr.h"
       4             : 
       5             : #include <limits.h>
       6             : 
       7             : static int
       8           0 : fd_hpack_wr_content_type_grpc( fd_h2_rbuf_t * rbuf_tx ) {
       9           0 :   static char const code[] =
      10           0 :     "\x5f" "\x16" "application/grpc+proto";
      11           0 :   if( FD_UNLIKELY( fd_h2_rbuf_free_sz( rbuf_tx)<sizeof(code)-1 ) ) return 0;
      12           0 :   fd_h2_rbuf_push( rbuf_tx, code, sizeof(code)-1 );
      13           0 :   return 1;
      14           0 : }
      15             : 
      16             : int
      17             : fd_grpc_h2_gen_request_hdrs( fd_grpc_req_hdrs_t const * req,
      18             :                              fd_h2_rbuf_t *             rbuf_tx,
      19             :                              char const *               version,
      20           0 :                              ulong                      version_len ) {
      21           0 :   if( FD_UNLIKELY( !fd_hpack_wr_method_post( rbuf_tx ) ) ) return 0;
      22           0 :   if( FD_UNLIKELY( !fd_hpack_wr_scheme( rbuf_tx, 1 ) ) ) return 0;
      23           0 :   if( FD_UNLIKELY( !fd_hpack_wr_path( rbuf_tx, req->path, req->path_len ) ) ) return 0;
      24           0 :   if( req->host_len ) {
      25           0 :     if( FD_UNLIKELY( !fd_hpack_wr_authority( rbuf_tx, req->host, req->host_len, req->port ) ) ) return 0;
      26           0 :   }
      27           0 :   if( FD_UNLIKELY( !fd_hpack_wr_trailers( rbuf_tx ) ) ) return 0;
      28           0 :   if( FD_UNLIKELY( !fd_hpack_wr_content_type_grpc( rbuf_tx ) ) ) return 0;
      29             : 
      30           0 :   static char const user_agent[] = "grpc-firedancer/";
      31           0 :   ulong const user_agent_len = sizeof(user_agent)-1 + version_len;
      32           0 :   if( FD_UNLIKELY( !fd_hpack_wr_user_agent( rbuf_tx, user_agent_len ) ) ) return 0;
      33           0 :   fd_h2_rbuf_push( rbuf_tx, user_agent, sizeof(user_agent)-1 );
      34           0 :   fd_h2_rbuf_push( rbuf_tx, version,    version_len          );
      35             : 
      36           0 :   if( req->bearer_auth_len ) {
      37           0 :     if( FD_UNLIKELY( !fd_hpack_wr_auth_bearer( rbuf_tx, req->bearer_auth, req->bearer_auth_len ) ) ) return 0;
      38           0 :   }
      39           0 :   return 1;
      40           0 : }
      41             : 
      42             : /* fd_grpc_h2_parse_num parses a strictly-decimal unsigned integer from
      43             :    a header value.  Every byte must be in ['0','9'].  Returns the parsed
      44             :    value, or UINT_MAX on failure (empty, any non-digit character
      45             :    including whitespace/sign/hex prefix, or overflow). */
      46             : 
      47             : static uint
      48             : fd_grpc_h2_parse_num( char const * num,
      49           0 :                       ulong        num_len ) {
      50           0 :   if( FD_UNLIKELY( !num_len || num_len>10UL ) ) return UINT_MAX;
      51           0 :   ulong val = 0;
      52           0 :   for( ulong i=0; i<num_len; i++ ) {
      53           0 :     uint d = (uint)( (uchar)num[i] - '0' );
      54           0 :     if( FD_UNLIKELY( d>9U ) ) return UINT_MAX;
      55           0 :     val = val*10UL + d;
      56           0 :   }
      57           0 :   if( FD_UNLIKELY( val>(ulong)UINT_MAX ) ) return UINT_MAX;
      58           0 :   return (uint)val;
      59           0 : }
      60             : 
      61             : int
      62             : fd_grpc_h2_read_response_hdrs( fd_grpc_resp_hdrs_t *       resp,
      63             :                                fd_h2_hdr_matcher_t const * matcher,
      64             :                                uchar const *               payload,
      65           0 :                                ulong                       payload_sz ) {
      66           0 :   fd_hpack_rd_t hpack_rd[1];
      67           0 :   fd_hpack_rd_init( hpack_rd, payload, payload_sz );
      68           0 :   while( !fd_hpack_rd_done( hpack_rd ) )  {
      69           0 :     static FD_TL uchar scratch_buf[ 4096 ];
      70           0 :     uchar * scratch = scratch_buf;
      71           0 :     fd_h2_hdr_t hdr[1];
      72           0 :     uint err = fd_hpack_rd_next( hpack_rd, hdr, &scratch, scratch_buf+sizeof(scratch_buf) );
      73           0 :     if( FD_UNLIKELY( err ) ) {
      74           0 :       FD_LOG_WARNING(( "Failed to parse response headers (%u-%s)", err, fd_h2_strerror( err ) ));
      75           0 :       return FD_H2_ERR_PROTOCOL;
      76           0 :     }
      77             : 
      78           0 :     int hdr_idx = fd_h2_hdr_match( matcher, hdr->name, hdr->name_len, hdr->hint );
      79           0 :     switch( hdr_idx ) {
      80           0 :     case FD_H2_HDR_STATUS: {
      81           0 :       if( FD_UNLIKELY( hdr->value_len!=3 ) ) {
      82           0 :         FD_LOG_WARNING(( "Invalid HTTP status length %u", hdr->value_len ));
      83           0 :         return FD_H2_ERR_PROTOCOL;
      84           0 :       }
      85           0 :       uint h2_status = fd_grpc_h2_parse_num( hdr->value, hdr->value_len );
      86             :       /* [100,999] matches the http crate's StatusCode::from_bytes used
      87             :          by tonic/h2 (first digit 1-9). RFC 9110 only defines 100-599
      88             :          but the h2 crate accepts the full 3-digit range. */
      89           0 :       if( FD_UNLIKELY( h2_status<100U || h2_status>999U ) ) {
      90           0 :         FD_LOG_WARNING(( "Invalid HTTP status %u", h2_status ));
      91           0 :         return FD_H2_ERR_PROTOCOL;
      92           0 :       }
      93           0 :       resp->h2_status = h2_status;
      94           0 :       break;
      95           0 :     }
      96           0 :     case FD_H2_HDR_CONTENT_TYPE:
      97           0 :       resp->is_grpc_proto =
      98           0 :         ( ( hdr->value_len==(sizeof("application/grpc")-1UL) &&
      99           0 :             fd_memeq( hdr->value, "application/grpc", sizeof("application/grpc")-1UL ) ) ||
     100           0 :           ( hdr->value_len==(sizeof("application/grpc+proto")-1UL) &&
     101           0 :             fd_memeq( hdr->value, "application/grpc+proto", sizeof("application/grpc+proto")-1UL ) ) );
     102           0 :       break;
     103           0 :     case FD_GRPC_HDR_STATUS: {
     104           0 :       uint grpc_status = fd_grpc_h2_parse_num( hdr->value, hdr->value_len );
     105             :       /* Tonic's Code::from_bytes maps any unrecognized grpc-status
     106             :          (including >16, non-numeric, empty) to Code::Unknown rather
     107             :          than rejecting the stream. We match that behavior here so that
     108             :          in all cases, we don't cause spurious RST_STREAMs. */
     109           0 :       if( FD_UNLIKELY( grpc_status>FD_GRPC_STATUS_UNAUTHENTICATED ) ) {
     110           0 :         int trunc_len = (int)fd_ulong_min( hdr->value_len, 32UL );
     111           0 :         FD_LOG_WARNING(( "Unknown grpc-status \"%.*s\", treating as UNKNOWN", trunc_len, hdr->value ));
     112           0 :         grpc_status = FD_GRPC_STATUS_UNKNOWN;
     113           0 :       }
     114           0 :       resp->grpc_status = grpc_status;
     115           0 :       break;
     116           0 :     }
     117           0 :     case FD_GRPC_HDR_MESSAGE:
     118           0 :       resp->grpc_msg_len = (uint)fd_ulong_min( hdr->value_len, sizeof(resp->grpc_msg) );
     119           0 :       if( resp->grpc_msg_len ) {
     120           0 :         fd_memcpy( resp->grpc_msg, hdr->value, resp->grpc_msg_len );
     121           0 :       }
     122           0 :       break;
     123           0 :     }
     124           0 :   }
     125           0 :   return FD_H2_SUCCESS;
     126           0 : }
     127             : 
     128             : char const *
     129           0 : fd_grpc_status_cstr( uint status ) {
     130           0 :   switch( status ) {
     131           0 :   case FD_GRPC_STATUS_OK:                   return "ok";
     132           0 :   case FD_GRPC_STATUS_CANCELLED:            return "cancelled";
     133           0 :   case FD_GRPC_STATUS_UNKNOWN:              return "unknown";
     134           0 :   case FD_GRPC_STATUS_INVALID_ARGUMENT:     return "invalid argument";
     135           0 :   case FD_GRPC_STATUS_DEADLINE_EXCEEDED:    return "deadline exceeded";
     136           0 :   case FD_GRPC_STATUS_NOT_FOUND:            return "not found";
     137           0 :   case FD_GRPC_STATUS_ALREADY_EXISTS:       return "already exists";
     138           0 :   case FD_GRPC_STATUS_PERMISSION_DENIED:    return "permission denied";
     139           0 :   case FD_GRPC_STATUS_RESOURCE_EXHAUSTED:   return "resource exhausted";
     140           0 :   case FD_GRPC_STATUS_FAILED_PRECONDITION:  return "failed precondition";
     141           0 :   case FD_GRPC_STATUS_ABORTED:              return "aborted";
     142           0 :   case FD_GRPC_STATUS_OUT_OF_RANGE:         return "out of range";
     143           0 :   case FD_GRPC_STATUS_UNIMPLEMENTED:        return "unimplemented";
     144           0 :   case FD_GRPC_STATUS_INTERNAL:             return "internal";
     145           0 :   case FD_GRPC_STATUS_UNAVAILABLE:          return "unavailable";
     146           0 :   case FD_GRPC_STATUS_DATA_LOSS:            return "data loss";
     147           0 :   case FD_GRPC_STATUS_UNAUTHENTICATED:      return "unauthenticated";
     148           0 :   default:                                  return "unknown";
     149           0 :   }
     150           0 : }

Generated by: LCOV version 1.14