LCOV - code coverage report
Current view: top level - waltz/http - fd_url.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 118 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 4 0.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           0 :                    int *              opt_err ) {
      10           0 :   int err_[1];
      11           0 :   if( !opt_err ) opt_err = err_;
      12           0 :   *opt_err = FD_URL_SUCCESS;
      13             : 
      14           0 :   char const * const url_end = url_str+url_str_len;
      15             : 
      16           0 :   char const * const scheme     = url_str;
      17           0 :   ulong              scheme_len = 0UL;
      18           0 :   if( FD_UNLIKELY( url_str_len<8UL ) ) return NULL;
      19           0 :   if( fd_memeq( scheme, "http://", 7 ) ) {
      20           0 :     scheme_len = 7;
      21           0 :   } else if( fd_memeq( scheme, "https://", 8 ) ) {
      22           0 :     scheme_len = 8;
      23           0 :   } else {
      24           0 :     *opt_err = FD_URL_ERR_SCHEME;
      25           0 :     return NULL;
      26           0 :   }
      27             : 
      28           0 :   char const * const authority = scheme+scheme_len;
      29             : 
      30             :   /* Find end of authority */
      31           0 :   char const * authority_end;
      32           0 :   for( authority_end = authority;
      33           0 :        authority_end < url_end &&
      34           0 :        *authority_end != '/'   &&
      35           0 :        *authority_end != '?'   &&
      36           0 :        *authority_end != '#';
      37           0 :        authority_end++ ) {
      38           0 :     if( FD_UNLIKELY( *authority_end=='@' ) ) {
      39           0 :       *opt_err = FD_URL_ERR_USERINFO;
      40           0 :       return NULL; /* userinfo not supported */
      41           0 :     }
      42           0 :   }
      43           0 :   ulong const authority_len = (ulong)( authority_end-authority );
      44             : 
      45             :   /* Find port number */
      46           0 :   char const * const host     = authority;
      47           0 :   ulong              host_len = authority_len;
      48           0 :   char const *       port     = NULL;
      49           0 :   ulong              port_len = 0UL;
      50           0 :   for( ulong j=0UL; j<authority_len; j++ ) {
      51           0 :     if( authority[ j ]==':' ) {
      52           0 :       host_len = j;
      53           0 :       port     = authority    +j+1;
      54           0 :       port_len = authority_len-j-1;
      55           0 :       break;
      56           0 :     }
      57           0 :   }
      58             : 
      59           0 :   if( FD_UNLIKELY( host_len>=FD_FQDN_BUF_MAX ) ) {
      60           0 :     *opt_err = FD_URL_ERR_HOST_OVERSZ;
      61           0 :     return NULL;
      62           0 :   }
      63             : 
      64             : 
      65           0 :   *url = (fd_url_t){
      66           0 :     .scheme     = scheme,
      67           0 :     .scheme_len = scheme_len,
      68           0 :     .host       = host,
      69           0 :     .host_len   = host_len,
      70           0 :     .port       = port,
      71           0 :     .port_len   = port_len,
      72           0 :     .tail       = authority+authority_len,
      73           0 :     .tail_len   = (ulong)( url_end-(authority+authority_len) )
      74           0 :   };
      75             : 
      76           0 :   return url;
      77           0 : }
      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           0 :                        char const * context ) {
      86           0 :   char const * ctx = context ? context : "URL";
      87             : 
      88           0 :   int url_err[1];
      89           0 :   fd_url_t * url = fd_url_parse_cstr( url_, url_str, url_str_len, url_err );
      90           0 :   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           0 :   *is_ssl = ( url->scheme_len==8UL );
     109             : 
     110           0 :   *tcp_port = *is_ssl ? 443 : 80;
     111           0 :   if( FD_LIKELY( !url->port ) ) return 0;
     112           0 :   if( FD_UNLIKELY( !url->port_len || url->port_len>5UL ) ) goto invalid_port;
     113             : 
     114           0 :   ulong port_no = 0UL;
     115           0 :   for( ulong i=0UL; i<url->port_len; i++ ) {
     116           0 :     uchar c = (uchar)url->port[ i ];
     117           0 :     if( FD_UNLIKELY( c<(uchar)'0' || c>(uchar)'9' ) ) goto invalid_port;
     118           0 :     port_no = 10UL*port_no + (ulong)(c-(uchar)'0');
     119           0 :   }
     120           0 :   if( FD_UNLIKELY( !port_no || port_no>USHORT_MAX ) ) goto invalid_port;
     121             : 
     122           0 :   *tcp_port = (ushort)port_no;
     123             : 
     124           0 :   return 0;
     125             : 
     126           0 : invalid_port:
     127           0 :   FD_LOG_WARNING(( "Invalid %s `%.*s`: invalid port number", ctx, (int)url_str_len, url_str ));
     128           0 :   return -1;
     129           0 : }
     130             : 
     131             : 
     132             : static inline int
     133           0 : fd_hex_unhex( int c ) {
     134           0 :   if( c>='0' && c<='9' ) return c-'0';
     135           0 :   if( c>='a' && c<='f' ) return c-'a'+0xa;
     136           0 :   if( c>='A' && c<='F' ) return c-'A'+0xa;
     137           0 :   return -1;
     138           0 : }
     139             : 
     140             : ulong
     141             : fd_url_unescape( char * const msg,
     142           0 :                  ulong  const len ) {
     143           0 :   char * end = msg+len;
     144           0 :   char * dst = msg;
     145           0 :   for( char * src=msg; src<end; src++ ) {
     146             :     /* invariant: dst<=src */
     147           0 :     if( FD_LIKELY( (*src)!='%' ) ) {
     148           0 :       *(dst++) = *src;
     149           0 :     } else {
     150           0 :       if( FD_UNLIKELY( src+2>=end ) ) return 0UL; /* truncated percent encoding */
     151           0 :       int hi = fd_hex_unhex( src[1] );
     152           0 :       int lo = fd_hex_unhex( src[2] );
     153           0 :       if( FD_UNLIKELY( (hi|lo)<0 ) ) return 0UL; /* invalid hex digit */
     154           0 :       *(dst++) = (char)( (hi<<4) | lo );
     155           0 :       src += 2;
     156           0 :     }
     157           0 :   }
     158           0 :   return (ulong)( dst-msg );
     159           0 : }

Generated by: LCOV version 1.14