LCOV - code coverage report
Current view: top level - waltz/http - fd_url.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 100 118 84.7 %
Date: 2026-08-13 04:56:22 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "fd_url.h"
       2             : #include "../../util/cstr/fd_cstr.h"
       3             : #include "../../util/log/fd_log.h"
       4             : 
       5             : fd_url_t *
       6             : fd_url_parse_cstr( fd_url_t *   const url,
       7             :                    char const * const url_str,
       8             :                    ulong        const url_str_len,
       9          45 :                    int *              opt_err ) {
      10          45 :   int err_[1];
      11          45 :   if( !opt_err ) opt_err = err_;
      12          45 :   *opt_err = FD_URL_SUCCESS;
      13             : 
      14          45 :   char const * const url_end = url_str+url_str_len;
      15             : 
      16          45 :   char const * const scheme     = url_str;
      17          45 :   ulong              scheme_len = 0UL;
      18          45 :   if( FD_UNLIKELY( url_str_len<8UL ) ) return NULL;
      19          45 :   if( fd_memeq( scheme, "http://", 7 ) ) {
      20          33 :     scheme_len = 7;
      21          33 :   } else if( fd_memeq( scheme, "https://", 8 ) ) {
      22           9 :     scheme_len = 8;
      23           9 :   } else {
      24           3 :     *opt_err = FD_URL_ERR_SCHEME;
      25           3 :     return NULL;
      26           3 :   }
      27             : 
      28          42 :   char const * const authority = scheme+scheme_len;
      29             : 
      30             :   /* Find end of authority */
      31          42 :   char const * authority_end;
      32          42 :   for( authority_end = authority;
      33         606 :        authority_end < url_end &&
      34         606 :        *authority_end != '/'   &&
      35         606 :        *authority_end != '?'   &&
      36         606 :        *authority_end != '#';
      37         567 :        authority_end++ ) {
      38         567 :     if( FD_UNLIKELY( *authority_end=='@' ) ) {
      39           3 :       *opt_err = FD_URL_ERR_USERINFO;
      40           3 :       return NULL; /* userinfo not supported */
      41           3 :     }
      42         567 :   }
      43          39 :   ulong const authority_len = (ulong)( authority_end-authority );
      44             : 
      45             :   /* Find port number */
      46          39 :   char const * const host     = authority;
      47          39 :   ulong              host_len = authority_len;
      48          39 :   char const *       port     = NULL;
      49          39 :   ulong              port_len = 0UL;
      50         462 :   for( ulong j=0UL; j<authority_len; j++ ) {
      51         453 :     if( authority[ j ]==':' ) {
      52          30 :       host_len = j;
      53          30 :       port     = authority    +j+1;
      54          30 :       port_len = authority_len-j-1;
      55          30 :       break;
      56          30 :     }
      57         453 :   }
      58             : 
      59          39 :   if( FD_UNLIKELY( host_len>255 ) ) {
      60           0 :     *opt_err = FD_URL_ERR_HOST_OVERSZ;
      61           0 :     return NULL;
      62           0 :   }
      63             : 
      64             : 
      65          39 :   *url = (fd_url_t){
      66          39 :     .scheme     = scheme,
      67          39 :     .scheme_len = scheme_len,
      68          39 :     .host       = host,
      69          39 :     .host_len   = host_len,
      70          39 :     .port       = port,
      71          39 :     .port_len   = port_len,
      72          39 :     .tail       = authority+authority_len,
      73          39 :     .tail_len   = (ulong)( url_end-(authority+authority_len) )
      74          39 :   };
      75             : 
      76          39 :   return url;
      77          39 : }
      78             : 
      79             : int
      80             : fd_url_parse_endpoint( fd_url_t *   url_,
      81             :                        char const * url_str,
      82             :                        ulong        url_str_len,
      83             :                        ushort *     tcp_port,
      84             :                        _Bool *      is_ssl,
      85          30 :                        char const * context ) {
      86          30 :   char const * ctx = context ? context : "URL";
      87             : 
      88          30 :   int url_err[1];
      89          30 :   fd_url_t * url = fd_url_parse_cstr( url_, url_str, url_str_len, url_err );
      90          30 :   if( FD_UNLIKELY( !url ) ) {
      91           0 :     switch( *url_err ) {
      92           0 :     case FD_URL_ERR_SCHEME:
      93           0 :       FD_LOG_WARNING(( "Invalid %s `%.*s`: must start with `http://` or `https://`", ctx, (int)url_str_len, url_str ));
      94           0 :       return -1;
      95           0 :     case FD_URL_ERR_HOST_OVERSZ:
      96           0 :       FD_LOG_WARNING(( "Invalid %s `%.*s`: domain name is too long", ctx, (int)url_str_len, url_str ));
      97           0 :       return -1;
      98           0 :     case FD_URL_ERR_USERINFO:
      99           0 :       FD_LOG_WARNING(( "Invalid %s `%.*s`: userinfo is not supported", ctx, (int)url_str_len, url_str ));
     100           0 :       return -1;
     101           0 :     default:
     102           0 :       FD_LOG_WARNING(( "Invalid %s `%.*s`", ctx, (int)url_str_len, url_str ));
     103           0 :       return -1;
     104           0 :     }
     105           0 :   }
     106             : 
     107             :   /* fd_url_parse_cstr() already guarantees http:// or https:// */
     108          30 :   *is_ssl = ( url->scheme_len==8UL );
     109             : 
     110          30 :   *tcp_port = *is_ssl ? 443 : 80;
     111          30 :   if( FD_LIKELY( !url->port ) ) return 0;
     112          27 :   if( FD_UNLIKELY( !url->port_len || url->port_len>5UL ) ) goto invalid_port;
     113             : 
     114          21 :   ulong port_no = 0UL;
     115          78 :   for( ulong i=0UL; i<url->port_len; i++ ) {
     116          63 :     uchar c = (uchar)url->port[ i ];
     117          63 :     if( FD_UNLIKELY( c<(uchar)'0' || c>(uchar)'9' ) ) goto invalid_port;
     118          57 :     port_no = 10UL*port_no + (ulong)(c-(uchar)'0');
     119          57 :   }
     120          15 :   if( FD_UNLIKELY( !port_no || port_no>USHORT_MAX ) ) goto invalid_port;
     121             : 
     122           9 :   *tcp_port = (ushort)port_no;
     123             : 
     124           9 :   return 0;
     125             : 
     126          18 : invalid_port:
     127          18 :   FD_LOG_WARNING(( "Invalid %s `%.*s`: invalid port number", ctx, (int)url_str_len, url_str ));
     128          18 :   return -1;
     129          15 : }
     130             : 
     131             : 
     132             : static inline int
     133          96 : fd_hex_unhex( int c ) {
     134          96 :   if( c>='0' && c<='9' ) return c-'0';
     135          48 :   if( c>='a' && c<='f' ) return c-'a'+0xa;
     136          42 :   if( c>='A' && c<='F' ) return c-'A'+0xa;
     137          24 :   return -1;
     138          42 : }
     139             : 
     140             : ulong
     141             : fd_url_unescape( char * const msg,
     142          66 :                  ulong  const len ) {
     143          66 :   char * end = msg+len;
     144          66 :   char * dst = msg;
     145         177 :   for( char * src=msg; src<end; src++ ) {
     146             :     /* invariant: dst<=src */
     147         138 :     if( FD_LIKELY( (*src)!='%' ) ) {
     148          78 :       *(dst++) = *src;
     149          78 :     } else {
     150          60 :       if( FD_UNLIKELY( src+2>=end ) ) return 0UL; /* truncated percent encoding */
     151          48 :       int hi = fd_hex_unhex( src[1] );
     152          48 :       int lo = fd_hex_unhex( src[2] );
     153          48 :       if( FD_UNLIKELY( (hi|lo)<0 ) ) return 0UL; /* invalid hex digit */
     154          33 :       *(dst++) = (char)( (hi<<4) | lo );
     155          33 :       src += 2;
     156          33 :     }
     157         138 :   }
     158          39 :   return (ulong)( dst-msg );
     159          66 : }

Generated by: LCOV version 1.14