| 245 | const std::string& Uri::ToString() const { return impl_->string_rep_; } |
| 246 | |
| 247 | Status Uri::Parse(const std::string& uri_string) { |
| 248 | impl_->Reset(); |
| 249 | |
| 250 | const auto& s = impl_->KeepString(uri_string); |
| 251 | impl_->string_rep_ = s; |
| 252 | const char* error_pos; |
| 253 | int retval = |
| 254 | uriParseSingleUriExA(&impl_->uri_, s.data(), s.data() + s.size(), &error_pos); |
| 255 | if (retval != URI_SUCCESS) { |
| 256 | if (retval == URI_ERROR_SYNTAX) { |
| 257 | return Status::Invalid("Cannot parse URI: '", uri_string, |
| 258 | "' due to syntax error at character '", *error_pos, |
| 259 | "' (position ", error_pos - s.data(), ")"); |
| 260 | } else { |
| 261 | return Status::Invalid("Cannot parse URI: '", uri_string, "'"); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | const auto scheme = TextRangeToView(impl_->uri_.scheme); |
| 266 | if (scheme.empty()) { |
| 267 | return Status::Invalid("URI has empty scheme: '", uri_string, "'"); |
| 268 | } |
| 269 | impl_->is_file_uri_ = (scheme == "file"); |
| 270 | |
| 271 | // Gather path segments |
| 272 | auto path_seg = impl_->uri_.pathHead; |
| 273 | while (path_seg != nullptr) { |
| 274 | impl_->path_segments_.push_back(TextRangeToView(path_seg->text)); |
| 275 | path_seg = path_seg->next; |
| 276 | } |
| 277 | |
| 278 | // Decide whether URI path is absolute |
| 279 | impl_->is_absolute_path_ = false; |
| 280 | if (impl_->uri_.absolutePath == URI_TRUE) { |
| 281 | impl_->is_absolute_path_ = true; |
| 282 | } else if (has_host() && impl_->path_segments_.size() > 0) { |
| 283 | // When there's a host (even empty), uriparser considers the path relative. |
| 284 | // Several URI parsers for Python all consider it absolute, though. |
| 285 | // For example, the path for "file:///tmp/foo" is "/tmp/foo", not "tmp/foo". |
| 286 | // Similarly, the path for "file://localhost/" is "/". |
| 287 | // However, the path for "file://localhost" is "". |
| 288 | impl_->is_absolute_path_ = true; |
| 289 | } |
| 290 | #ifdef _WIN32 |
| 291 | // There's an exception on Windows: "file:/C:foo/bar" is relative. |
| 292 | if (impl_->is_file_uri_ && impl_->path_segments_.size() > 0) { |
| 293 | const auto& first_seg = impl_->path_segments_[0]; |
| 294 | if (IsDriveSpec(first_seg) && (first_seg.length() >= 3 && first_seg[2] != '/')) { |
| 295 | impl_->is_absolute_path_ = false; |
| 296 | } |
| 297 | } |
| 298 | #endif |
| 299 | |
| 300 | if (impl_->is_file_uri_ && !impl_->is_absolute_path_) { |
| 301 | return Status::Invalid("File URI cannot be relative: '", uri_string, "'"); |
| 302 | } |
| 303 | |
| 304 | // Parse port number |