--- a/src/server/util/util.c Sat Feb 21 14:40:03 2026 +0100 +++ b/src/server/util/util.c Sat Feb 21 19:36:15 2026 +0100 @@ -1173,3 +1173,132 @@ } return 0; } + +/* -------------------------- util_parse_http_uri -------------------------- */ + +NSAPI_PUBLIC int util_parse_uri(const char *uri, WSUri *result) { + if(!uri) { + return 0; + } + return util_parse_uri_len(uri, strlen(uri), result); +} + +NSAPI_PUBLIC int util_parse_uri_len(const char *uri, size_t length, WSUri *result) { + if(length == 0) { + return 0; + } + + memset(result, 0, sizeof(WSUri)); + + // get uri scheme + int scheme = -1; + const char *scheme_str = uri; + size_t scheme_len = length; + short port = 0; + size_t i = 0; + for(;i<length;i++) { + if(uri[i] == ':') { + if(i+3 < length && uri[i+1] == '/' && uri[i+2] == '/') { + cxstring scheme_s = cx_strn(uri, i); + scheme_len = i; + if(!cx_strcmp(scheme_s, "http")) { + scheme = WS_URI_HTTP; + port = 80; + } else if(!cx_strcmp(scheme_s, "https")) { + scheme = WS_URI_HTTPS; + port = 443; + } else { + scheme = WS_URI_OTHER; + } + i += 3; + break; + } else { + return 0; // invalid uri + } + } + } + result->scheme_num = scheme; + result->scheme = scheme_str; + result->schemelen = scheme_len; + + if(i == length) { + return 0; // invlid uri + } + + // get host + size_t hostlen = 0; + const char *port_str = NULL; + const char *path = NULL; + int is_ipv6_addr = 0; + size_t host_start = i; + if(uri[i] == '[') { + host_start++; + is_ipv6_addr = 1; + i++; + } + for(;i<length;i++) { + char c = uri[i]; + if(is_ipv6_addr) { + if(c == ']') { + // end of ipv6 address + result->host = uri + host_start; + result->hostlen = i - host_start; + is_ipv6_addr = 0; + + // the next character must be a port or path separator + if(i+1 < length) { + c = uri[i+1]; + if(c != '/' && c != ':') { + return 0; // invalid uri + } + } + } else if(c != ':' && c != '.' && c != '%' && !isalnum(c)) { + return 0; // invalid ipv6 address + } + } else if(c == ':') { + if(port_str) { + return 0; // error: port was already specified + } + if(!result->host) { + result->host = uri + host_start; + result->hostlen = i - host_start; + } + // get port + port_str = uri + i + 1; + } else if(c == '/') { + path = uri + i; + break; + } else if(c == '[' || c == ']') { + return 0; // invalid hostname + } + } + if(is_ipv6_addr) { + return 0; // ipv6 address was not terminated + } + + if(port_str) { + size_t port_len = uri + i - port_str; + cxstring port_s = cx_strn(port_str, port_len); + if(cx_strtos(port_s, &port, 10)) { + return 0; // invalid port + } + } + if(!result->host) { + result->host = uri + host_start; + result->hostlen = i - host_start; + } + if(result->hostlen == 0) { + return 0; + } + + size_t pathlen = 0; + if(path) { + pathlen = uri + length - path; + } + + result->path = path; + result->pathlen = pathlen; + result->port = port; + + return 1; +}