Practical URI parser for ws/wss/http/https URLs. Standards references: - RFC 3986: generic URI syntax used for scheme, authority, path, query, fragment. - RFC 3986 Section 3.2.2: IP-literal host syntax (bracketed IPv6 in authority). - RFC 6455 Section 3: ws/wss URI schemes (default ports handled in getProtocolPort). Note: this is not a full RFC validator; it is a fast parser with targeted syntax
| 137 | // |
| 138 | // Note: this is not a full RFC validator; it is a fast parser with targeted syntax checks. |
| 139 | clParseURL clParseURL::ParseURL(const std::string& URL) |
| 140 | { |
| 141 | clParseURL Result; |
| 142 | const std::size_t npos = std::string::npos; |
| 143 | std::size_t current = 0; |
| 144 | |
| 145 | // Step 1: read scheme (<scheme>:) and normalize to lowercase. |
| 146 | std::size_t schemeEnd = URL.find(':', current); |
| 147 | if (schemeEnd == npos) |
| 148 | { |
| 149 | return clParseURL(LUrlParserError_NoUrlCharacter); |
| 150 | } |
| 151 | |
| 152 | Result.m_Scheme = URL.substr(current, schemeEnd - current); |
| 153 | if (!IsSchemeValid(Result.m_Scheme)) |
| 154 | { |
| 155 | return clParseURL(LUrlParserError_InvalidSchemeName); |
| 156 | } |
| 157 | |
| 158 | std::transform(Result.m_Scheme.begin(), |
| 159 | Result.m_Scheme.end(), |
| 160 | Result.m_Scheme.begin(), |
| 161 | ::tolower); |
| 162 | |
| 163 | current = schemeEnd + 1; |
| 164 | |
| 165 | // Step 2: require authority prefix "//". |
| 166 | if (current + 1 >= URL.size() || URL[current] != '/' || URL[current + 1] != '/') |
| 167 | { |
| 168 | return clParseURL(LUrlParserError_NoDoubleSlash); |
| 169 | } |
| 170 | current += 2; |
| 171 | |
| 172 | // Step 3: locate the authority boundary (<authority> ends at '/' or '?'). |
| 173 | std::size_t authorityEnd = URL.find_first_of("/?", current); |
| 174 | if (authorityEnd == npos) |
| 175 | { |
| 176 | authorityEnd = URL.size(); |
| 177 | } |
| 178 | |
| 179 | // Step 4: parse optional user info (<user>[:<password>]@). |
| 180 | bool hasUserInfo = false; |
| 181 | std::size_t atPos = URL.find('@', current); |
| 182 | if (atPos != npos && atPos < authorityEnd) |
| 183 | { |
| 184 | hasUserInfo = true; |
| 185 | } |
| 186 | |
| 187 | if (hasUserInfo) |
| 188 | { |
| 189 | // User info can contain ':' but must contain exactly one '@' separator |
| 190 | // within the authority segment. |
| 191 | if (URL.find('@', atPos + 1) != npos && URL.find('@', atPos + 1) < authorityEnd) |
| 192 | { |
| 193 | return clParseURL(LUrlParserError_MultipleAtSignsInAuthority); |
| 194 | } |
| 195 | |
| 196 | std::size_t colonPos = URL.find(':', current); |
nothing calls this directly
no test coverage detected