[vm] Move ResolveUri to platform

This will enable the standalone embedder to resolve uris in a follow
up CL:
https://dart-review.googlesource.com/c/sdk/+/361881

The implementation was using zones for allocation, this CL switches
that to using `malloc`. The caller is responsible for freeing the
memory if resolving succeeds.

This CL removes `Dart_DefaultCanonicalizeUrl`.

We don't have a run_platform_tests, so this CL keeps the tests in
run_vm_tests.

TEST=vm/cc/ParseUri
TEST=vm/cc/ResolveUri
TEST=tests/ffi/native_assets/asset_relative_test.dart

Bug: https://github.com/dart-lang/sdk/issues/55523
Change-Id: Ifb300d8164eb50506f22ce619fad0811f74ef34c
Cq-Include-Trybots: luci.dart.try:vm-aot-asan-linux-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,pkg-linux-debug-try,pkg-linux-release-arm64-try,pkg-linux-release-try,pkg-mac-release-try,pkg-mac-release-arm64-try,pkg-win-release-arm64-try,pkg-win-release-try,vm-aot-linux-debug-x64-try,vm-linux-debug-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-aot-win-debug-x64-try,vm-win-debug-arm64-try,vm-mac-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368423
Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Daco Harkes 2024-06-12 16:45:19 +00:00 committed by Commit Queue
parent 6b788d2a77
commit 4b2906cdc1
16 changed files with 841 additions and 861 deletions

View file

@ -63,6 +63,8 @@
- `Dart_NewListOf` and `Dart_IsLegacyType` functions are
removed from Dart C API.
- `Dart_DefaultCanonicalizeUrl` is removed from the Dart C API.
## 3.4.0
### Language

View file

@ -5,6 +5,7 @@
import("../../build/config/gclient_args.gni")
import("../../build/dart/dart_action.gni")
import("../../sdk_args.gni")
import("../platform/platform_sources.gni")
import("../runtime_args.gni")
import("../vm/compiler/compiler_sources.gni")
import("../vm/ffi/ffi_sources.gni")
@ -986,6 +987,7 @@ source_set("run_vm_tests_set") {
# The VM sources are already included in libdart, so we just want to add in
# the tests here.
platform_tests = rebase_path(platform_sources_tests, ".", "../platform")
vm_tests = rebase_path(vm_sources_tests, ".", "../vm")
compiler_tests = rebase_path(compiler_sources_tests, ".", "../vm/compiler")
heap_tests = rebase_path(heap_sources_tests, ".", "../vm/heap")
@ -1006,7 +1008,7 @@ source_set("run_vm_tests_set") {
"vmservice_impl.cc",
"vmservice_impl.h",
] + builtin_impl_tests + vm_tests + compiler_tests + heap_tests +
io_impl_tests
io_impl_tests + platform_tests
}
executable("run_vm_tests") {

View file

@ -15,6 +15,8 @@
#include "bin/utils.h"
#include "include/dart_tools_api.h"
#include "platform/growable_array.h"
#include "platform/uri.h"
#include "platform/utils.h"
namespace dart {
namespace bin {
@ -87,7 +89,18 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag,
if (is_dart_scheme_url || is_dart_library) {
return url;
}
return Dart_DefaultCanonicalizeUrl(library_url, url);
const char* url_cstr;
result = Dart_StringToCString(url, &url_cstr);
if (Dart_IsError(result)) {
return result;
}
CStringUniquePtr resolved_uri = ResolveUri(url_cstr, library_url_string);
if (!resolved_uri) {
return DartUtils::NewError("%s: Unable to canonicalize uri '%s'.",
__FUNCTION__, url_cstr);
}
result = Dart_NewStringFromCString(resolved_uri.get());
return result;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
if (tag == Dart_kKernelTag) {

View file

@ -3360,10 +3360,8 @@ typedef enum {
* Dart_kCanonicalizeUrl
*
* This tag indicates that the embedder should canonicalize 'url' with
* respect to 'library'. For most embedders, the
* Dart_DefaultCanonicalizeUrl function is a sufficient implementation
* of this tag. The return value should be a string holding the
* canonicalized url.
* respect to 'library'. For most embedders, this is resolving the `url`
* relative to the `library`s url (see `Dart_LibraryUrl`).
*
* Dart_kImportTag
*
@ -3457,26 +3455,6 @@ Dart_DeferredLoadCompleteError(intptr_t loading_unit_id,
const char* error_message,
bool transient);
/**
* Canonicalizes a url with respect to some library.
*
* The url is resolved with respect to the library's url and some url
* normalizations are performed.
*
* This canonicalization function should be sufficient for most
* embedders to implement the Dart_kCanonicalizeUrl tag.
*
* \param base_url The base url relative to which the url is
* being resolved.
* \param url The url being resolved and canonicalized. This
* parameter is a string handle.
*
* \return If no error occurs, a String object is returned. Otherwise
* an error handle is returned.
*/
DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url,
Dart_Handle url);
/**
* Loads the root library for the current isolate.
*

View file

@ -5,6 +5,7 @@
#include "lib/ffi_dynamic_library.h"
#include "platform/globals.h"
#include "platform/utils.h"
#if defined(DART_HOST_OS_WINDOWS)
#include <Psapi.h>
#include <Windows.h>
@ -13,13 +14,13 @@
#include <tchar.h>
#endif
#include "platform/uri.h"
#include "vm/bootstrap_natives.h"
#include "vm/dart_api_impl.h"
#include "vm/exceptions.h"
#include "vm/ffi/native_assets.h"
#include "vm/native_entry.h"
#include "vm/symbols.h"
#include "vm/uri.h"
#include "vm/zone_text_buffer.h"
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
@ -405,20 +406,19 @@ static void* FfiResolveAsset(Thread* const thread,
String::NewFormatted(
"%s%s", file_schema,
String::Handle(zone, GetPlatformScriptPath(thread)).ToCString()));
const char* target_uri = nullptr;
char* path_cstr = path.ToMallocCString();
#if defined(DART_TARGET_OS_WINDOWS)
ReplaceBackSlashes(path_cstr);
#endif
const bool resolved =
ResolveUri(path_cstr, platform_script_uri.ToCString(), &target_uri);
CStringUniquePtr target_uri =
ResolveUri(path_cstr, platform_script_uri.ToCString());
free(path_cstr);
if (!resolved) {
if (!target_uri) {
*error = OS::SCreate(/*use malloc*/ nullptr,
"Failed to resolve '%s' relative to '%s'.",
path.ToCString(), platform_script_uri.ToCString());
} else {
const char* target_path = target_uri + file_schema_length;
const char* target_path = target_uri.get() + file_schema_length;
handle = LoadDynamicLibrary(target_path, error);
}
} else if (asset_type.Equals(Symbols::system())) {

View file

@ -35,6 +35,8 @@ platform_sources = [
"unwinding_records.cc",
"unwinding_records.h",
"unwinding_records_win.cc",
"uri.cc",
"uri.h",
"utils.cc",
"utils.h",
"utils_android.cc",
@ -43,3 +45,5 @@ platform_sources = [
"utils_macos.cc",
"utils_win.cc",
]
platform_sources_tests = [ "uri_test.cc" ]

View file

@ -1,13 +1,51 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/uri.h"
#include "platform/uri.h"
#include "vm/zone.h"
#include <memory>
#include <utility>
#include "platform/allocation.h"
#include "platform/utils.h"
// TODO(https://dartbug.com/55925): Move this file to bin/.
namespace dart {
static CStringUniquePtr MakeCopyOfString(const char* str) {
if (str == nullptr) {
return CStringUniquePtr();
}
intptr_t len = strlen(str) + 1; // '\0'-terminated.
char* copy = static_cast<char*>(malloc(len));
strncpy(copy, str, len);
return CStringUniquePtr(copy);
}
static CStringUniquePtr MakeCopyOfStringN(const char* str, intptr_t len) {
ASSERT(len >= 0);
for (intptr_t i = 0; i < len; i++) {
if (str[i] == '\0') {
len = i;
break;
}
}
char* copy = static_cast<char*>(malloc(len + 1)); // +1 for '\0'
strncpy(copy, str, len);
copy[len] = '\0';
return CStringUniquePtr(copy);
}
static CStringUniquePtr PrintToString(const char* format, ...) {
va_list args;
va_start(args, format);
char* buffer = Utils::VSCreate(format, args);
va_end(args);
return CStringUniquePtr(buffer);
}
static bool IsUnreservedChar(intptr_t value) {
return ((value >= 'a' && value <= 'z') || (value >= 'A' && value <= 'Z') ||
(value >= '0' && value <= '9') || value == '-' || value == '.' ||
@ -78,14 +116,13 @@ static int GetEscapedValue(const char* str, intptr_t pos, intptr_t len) {
return HexValue(digit1) * 16 + HexValue(digit2);
}
static char* NormalizeEscapes(const char* str, intptr_t len) {
CStringUniquePtr NormalizeEscapes(const char* str, intptr_t len) {
// Allocate the buffer.
Zone* zone = ThreadState::Current()->zone();
// We multiply len by three because a percent-escape sequence is
// three characters long (e.g. ' ' -> '%20). +1 for '\0'. We could
// take two passes through the string and avoid the excess
// allocation, but it's zone-memory so it doesn't seem necessary.
char* buffer = zone->Alloc<char>(len * 3 + 1);
char* buffer = static_cast<char*>(malloc(len * 3 + 1));
// Copy the string, normalizing as we go.
intptr_t buffer_pos = 0;
@ -121,7 +158,7 @@ static char* NormalizeEscapes(const char* str, intptr_t len) {
}
}
buffer[buffer_pos] = '\0';
return buffer;
return CStringUniquePtr(buffer);
}
// Lower-case a string in place.
@ -145,65 +182,48 @@ static void StringLower(char* str) {
}
}
static void ClearParsedUri(ParsedUri* parsed_uri) {
parsed_uri->scheme = nullptr;
parsed_uri->userinfo = nullptr;
parsed_uri->host = nullptr;
parsed_uri->port = nullptr;
parsed_uri->path = nullptr;
parsed_uri->query = nullptr;
parsed_uri->fragment = nullptr;
}
static intptr_t ParseAuthority(const char* authority, ParsedUri* parsed_uri) {
Zone* zone = ThreadState::Current()->zone();
static intptr_t ParseAuthority(const char* authority, ParsedUri& parsed_uri) {
const char* current = authority;
intptr_t len = 0;
size_t userinfo_len = strcspn(current, "@/");
if (current[userinfo_len] == '@') {
// The '@' character follows the optional userinfo string.
parsed_uri->userinfo = NormalizeEscapes(current, userinfo_len);
parsed_uri.userinfo = NormalizeEscapes(current, userinfo_len);
current += userinfo_len + 1;
len += userinfo_len + 1;
} else {
parsed_uri->userinfo = nullptr;
}
size_t host_len = strcspn(current, ":/");
char* host = NormalizeEscapes(current, host_len);
StringLower(host);
parsed_uri->host = host;
CStringUniquePtr host = NormalizeEscapes(current, host_len);
StringLower(host.get());
parsed_uri.host = std::move(host);
len += host_len;
if (current[host_len] == ':') {
// The ':' character precedes the optional port string.
const char* port_start = current + host_len + 1; // +1 for ':'
size_t port_len = strcspn(port_start, "/");
parsed_uri->port = zone->MakeCopyOfStringN(port_start, port_len);
parsed_uri.port = MakeCopyOfStringN(port_start, port_len);
len += 1 + port_len; // +1 for ':'
} else {
parsed_uri->port = nullptr;
}
return len;
}
// Performs a simple parse of a uri into its components.
// See RFC 3986 Section 3: Syntax.
bool ParseUri(const char* uri, ParsedUri* parsed_uri) {
Zone* zone = ThreadState::Current()->zone();
std::unique_ptr<ParsedUri> ParseUri(const char* uri) {
auto parsed_uri = std::make_unique<ParsedUri>();
// The first ':' separates the scheme from the rest of the uri. If
// a ':' occurs after the first '/' it doesn't count.
size_t scheme_len = strcspn(uri, ":/");
const char* rest = uri;
if (uri[scheme_len] == ':') {
char* scheme = zone->MakeCopyOfStringN(uri, scheme_len);
StringLower(scheme);
parsed_uri->scheme = scheme;
CStringUniquePtr scheme = MakeCopyOfStringN(uri, scheme_len);
StringLower(scheme.get());
parsed_uri->scheme = std::move(scheme);
rest = uri + scheme_len + 1;
} else {
parsed_uri->scheme = nullptr;
}
// The first '#' separates the optional fragment
@ -213,8 +233,6 @@ bool ParseUri(const char* uri, ParsedUri* parsed_uri) {
const char* fragment_start = hash_pos + 1;
parsed_uri->fragment =
NormalizeEscapes(fragment_start, strlen(fragment_start));
} else {
parsed_uri->fragment = nullptr;
}
// The first '?' or '#' separates the hierarchical part from the
@ -224,8 +242,6 @@ bool ParseUri(const char* uri, ParsedUri* parsed_uri) {
// There is a query part.
const char* query_start = question_pos + 1;
parsed_uri->query = NormalizeEscapes(query_start, (hash_pos - query_start));
} else {
parsed_uri->query = nullptr;
}
const char* path_start = rest;
@ -233,21 +249,16 @@ bool ParseUri(const char* uri, ParsedUri* parsed_uri) {
// There is an authority part.
const char* authority_start = rest + 2; // 2 for '//'.
intptr_t authority_len = ParseAuthority(authority_start, parsed_uri);
intptr_t authority_len = ParseAuthority(authority_start, *parsed_uri.get());
if (authority_len < 0) {
ClearParsedUri(parsed_uri);
return false;
return std::unique_ptr<ParsedUri>();
}
path_start = authority_start + authority_len;
} else {
parsed_uri->userinfo = nullptr;
parsed_uri->host = nullptr;
parsed_uri->port = nullptr;
}
// The path is the substring between the authority and the query.
parsed_uri->path = NormalizeEscapes(path_start, (question_pos - path_start));
return true;
return parsed_uri;
}
static char* RemoveLastSegment(char* current, char* base) {
@ -279,13 +290,13 @@ static intptr_t SegmentLength(const char* input) {
}
// See RFC 3986 Section 5.2.4: Remove Dot Segments.
static const char* RemoveDotSegments(const char* path) {
CStringUniquePtr RemoveDotSegments(const char* path) {
const char* input = path;
// The output path will always be less than or equal to the size of
// the input path.
Zone* zone = ThreadState::Current()->zone();
char* buffer = zone->Alloc<char>(strlen(path) + 1); // +1 for '\0'
char* buffer = static_cast<char*>(malloc(strlen(path) + 1)); // +1 for '\0'
char* output = buffer;
while (*input != '\0') {
@ -337,22 +348,21 @@ static const char* RemoveDotSegments(const char* path) {
}
}
*output = '\0';
return buffer;
return CStringUniquePtr(buffer);
}
// See RFC 3986 Section 5.2.3: Merge Paths.
static const char* MergePaths(const char* base_path, const char* ref_path) {
Zone* zone = ThreadState::Current()->zone();
CStringUniquePtr MergePaths(const char* base_path, const char* ref_path) {
if (base_path[0] == '\0') {
// If the base_path is empty, we prepend '/'.
return zone->PrintToString("/%s", ref_path);
return PrintToString("/%s", ref_path);
}
// We need to find the last '/' in base_path.
const char* last_slash = strrchr(base_path, '/');
if (last_slash == nullptr) {
// There is no slash in the base_path. Return the ref_path unchanged.
return ref_path;
return MakeCopyOfString(ref_path);
}
// We found a '/' in the base_path. Cut off everything after it and
@ -360,7 +370,7 @@ static const char* MergePaths(const char* base_path, const char* ref_path) {
intptr_t truncated_base_len = last_slash - base_path;
intptr_t ref_path_len = strlen(ref_path);
intptr_t len = truncated_base_len + ref_path_len + 1; // +1 for '/'
char* buffer = zone->Alloc<char>(len + 1); // +1 for '\0'
char* buffer = static_cast<char*>(malloc(len + 1)); // +1 for '\0'
// Copy truncated base.
strncpy(buffer, base_path, truncated_base_len);
@ -371,16 +381,15 @@ static const char* MergePaths(const char* base_path, const char* ref_path) {
// Copy the ref_path.
strncpy((buffer + truncated_base_len + 1), ref_path, ref_path_len + 1);
return buffer;
return CStringUniquePtr(buffer);
}
static char* BuildUri(const ParsedUri& uri) {
Zone* zone = ThreadState::Current()->zone();
CStringUniquePtr BuildUri(const ParsedUri& uri) {
ASSERT(uri.path != nullptr);
const char* fragment = uri.fragment == nullptr ? "" : uri.fragment;
const char* fragment = uri.fragment == nullptr ? "" : uri.fragment.get();
const char* fragment_separator = uri.fragment == nullptr ? "" : "#";
const char* query = uri.query == nullptr ? "" : uri.query;
const char* query = uri.query == nullptr ? "" : uri.query.get();
const char* query_separator = uri.query == nullptr ? "" : "?";
// If there is no scheme for this uri, just build a relative uri of
@ -389,140 +398,129 @@ static char* BuildUri(const ParsedUri& uri) {
if (uri.scheme == nullptr) {
ASSERT(uri.userinfo == nullptr && uri.host == nullptr &&
uri.port == nullptr);
return zone->PrintToString("%s%s%s%s%s", uri.path, query_separator, query,
fragment_separator, fragment);
return PrintToString("%s%s%s%s%s", uri.path.get(), query_separator, query,
fragment_separator, fragment);
}
// Uri with no authority: "scheme:path[?query][#fragment]"
if (uri.host == nullptr) {
ASSERT(uri.userinfo == nullptr && uri.port == nullptr);
return zone->PrintToString("%s:%s%s%s%s%s", uri.scheme, uri.path,
query_separator, query, fragment_separator,
fragment);
return PrintToString("%s:%s%s%s%s%s", uri.scheme.get(), uri.path.get(),
query_separator, query, fragment_separator, fragment);
}
const char* user = uri.userinfo == nullptr ? "" : uri.userinfo;
const char* user = uri.userinfo == nullptr ? "" : uri.userinfo.get();
const char* user_separator = uri.userinfo == nullptr ? "" : "@";
const char* port = uri.port == nullptr ? "" : uri.port;
const char* port = uri.port == nullptr ? "" : uri.port.get();
const char* port_separator = uri.port == nullptr ? "" : ":";
// If the path doesn't start with a '/', add one. We need it to
// separate the path from the authority.
const char* path_separator =
((uri.path[0] == '\0' || uri.path[0] == '/') ? "" : "/");
((uri.path.get()[0] == '\0' || uri.path.get()[0] == '/') ? "" : "/");
// Uri with authority:
// "scheme://[userinfo@]host[:port][/]path[?query][#fragment]"
return zone->PrintToString(
return PrintToString(
"%s://%s%s%s%s%s%s%s%s%s%s%s", // There is *nothing* wrong with this.
uri.scheme, user, user_separator, uri.host, port_separator, port,
path_separator, uri.path, query_separator, query, fragment_separator,
fragment);
uri.scheme.get(), user, user_separator, uri.host.get(), port_separator,
port, path_separator, uri.path.get(), query_separator, query,
fragment_separator, fragment);
}
// See RFC 3986 Section 5: Reference Resolution
bool ResolveUri(const char* ref_uri,
const char* base_uri,
const char** target_uri) {
CStringUniquePtr ResolveUri(const char* ref_uri, const char* base_uri) {
// Parse the reference uri.
ParsedUri ref;
if (!ParseUri(ref_uri, &ref)) {
*target_uri = nullptr;
return false;
std::unique_ptr<ParsedUri> ref = ParseUri(ref_uri);
if (!ref) {
return CStringUniquePtr();
}
ParsedUri target;
if (ref.scheme != nullptr) {
if (strcmp(ref.scheme, "dart") == 0) {
Zone* zone = ThreadState::Current()->zone();
*target_uri = zone->MakeCopyOfString(ref_uri);
return true;
if (ref->scheme != nullptr) {
if (strcmp(ref->scheme.get(), "dart") == 0) {
return MakeCopyOfString(ref_uri);
}
// When the ref_uri specifies a scheme, the base_uri is ignored.
target.scheme = ref.scheme;
target.userinfo = ref.userinfo;
target.host = ref.host;
target.port = ref.port;
target.path = RemoveDotSegments(ref.path);
target.query = ref.query;
target.fragment = ref.fragment;
*target_uri = BuildUri(target);
return true;
target.scheme = std::move(ref->scheme);
target.userinfo = std::move(ref->userinfo);
target.host = std::move(ref->host);
target.port = std::move(ref->port);
target.path = std::move(ref->path);
target.query = std::move(ref->query);
target.fragment = std::move(ref->fragment);
return BuildUri(target);
}
// Parse the base uri.
ParsedUri base;
if (!ParseUri(base_uri, &base)) {
*target_uri = nullptr;
return false;
std::unique_ptr<ParsedUri> base = ParseUri(base_uri);
if (!base) {
return CStringUniquePtr();
}
if ((base.scheme != nullptr) && strcmp(base.scheme, "dart") == 0) {
Zone* zone = ThreadState::Current()->zone();
*target_uri = zone->MakeCopyOfString(ref_uri);
return true;
if ((base->scheme != nullptr) && strcmp(base->scheme.get(), "dart") == 0) {
return MakeCopyOfString(ref_uri);
}
if (ref.host != nullptr) {
if (ref->host != nullptr) {
// When the ref_uri specifies an authority, we only use the base scheme.
target.scheme = base.scheme;
target.userinfo = ref.userinfo;
target.host = ref.host;
target.port = ref.port;
target.path = RemoveDotSegments(ref.path);
target.query = ref.query;
target.fragment = ref.fragment;
*target_uri = BuildUri(target);
return true;
target.scheme = std::move(base->scheme);
target.userinfo = std::move(ref->userinfo);
target.host = std::move(ref->host);
target.port = std::move(ref->port);
target.path = RemoveDotSegments(ref->path.get());
target.query = std::move(ref->query);
target.fragment = std::move(ref->fragment);
return BuildUri(target);
}
if (ref.path[0] == '\0') {
if (ref->path.get()[0] == '\0') {
// Empty path. Use most parts of base_uri.
target.scheme = base.scheme;
target.userinfo = base.userinfo;
target.host = base.host;
target.port = base.port;
target.path = base.path;
target.query = ((ref.query == nullptr) ? base.query : ref.query);
target.fragment = ref.fragment;
*target_uri = BuildUri(target);
return true;
target.scheme = std::move(base->scheme);
target.userinfo = std::move(base->userinfo);
target.host = std::move(base->host);
target.port = std::move(base->port);
target.path = std::move(base->path);
target.query = ((ref->query == nullptr) ? std::move(base->query)
: std::move(ref->query));
target.fragment = std::move(ref->fragment);
return BuildUri(target);
} else if (ref.path[0] == '/') {
} else if (ref->path.get()[0] == '/') {
// Absolute path. ref_path wins.
target.scheme = base.scheme;
target.userinfo = base.userinfo;
target.host = base.host;
target.port = base.port;
target.path = RemoveDotSegments(ref.path);
target.query = ref.query;
target.fragment = ref.fragment;
*target_uri = BuildUri(target);
return true;
target.scheme = std::move(base->scheme);
target.userinfo = std::move(base->userinfo);
target.host = std::move(base->host);
target.port = std::move(base->port);
target.path = RemoveDotSegments(ref->path.get());
target.query = std::move(ref->query);
target.fragment = std::move(ref->fragment);
return BuildUri(target);
} else {
// Relative path. We need to merge the base path and the ref path.
if (base.scheme == nullptr && base.host == nullptr && base.path[0] != '/') {
if (base->scheme == nullptr && base->host == nullptr &&
base->path.get()[0] != '/') {
// The dart:core Uri class handles resolving a relative uri
// against a second relative uri specially, in a way not
// described in the RFC. We do not need to support this for
// library resolution. If we need to implement this later, we
// can.
*target_uri = nullptr;
return false;
return CStringUniquePtr();
}
target.scheme = base.scheme;
target.userinfo = base.userinfo;
target.host = base.host;
target.port = base.port;
target.path = RemoveDotSegments(MergePaths(base.path, ref.path));
target.query = ref.query;
target.fragment = ref.fragment;
*target_uri = BuildUri(target);
return true;
target.scheme = std::move(base->scheme);
target.userinfo = std::move(base->userinfo);
target.host = std::move(base->host);
target.port = std::move(base->port);
CStringUniquePtr merged_paths =
MergePaths(base->path.get(), ref->path.get());
target.path = RemoveDotSegments(merged_paths.get());
target.query = std::move(ref->query);
target.fragment = std::move(ref->fragment);
return BuildUri(target);
}
}

36
runtime/platform/uri.h Normal file
View file

@ -0,0 +1,36 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_PLATFORM_URI_H_
#define RUNTIME_PLATFORM_URI_H_
#include <memory>
#include "platform/utils.h"
namespace dart {
class ParsedUri {
public:
CStringUniquePtr scheme;
CStringUniquePtr userinfo;
CStringUniquePtr host;
CStringUniquePtr port;
CStringUniquePtr path;
CStringUniquePtr query;
CStringUniquePtr fragment;
};
// Parses a uri into its parts.
//
// Returns nullptr if the parse fails.
std::unique_ptr<ParsedUri> ParseUri(const char* uri);
// Resolves some reference uri with respect to a base uri.
//
// Returns nullptr if the resolve fails.
CStringUniquePtr ResolveUri(const char* ref_uri, const char* base_uri);
} // namespace dart
#endif // RUNTIME_PLATFORM_URI_H_

View file

@ -0,0 +1,609 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/unit_test.h"
#include "platform/uri.h"
#include "platform/utils.h"
namespace dart {
#define EXPECT_USTREQ(expected, actual) EXPECT_STREQ(expected, actual.get())
TEST_CASE(ParseUri_WithScheme_NoQueryNoUser) {
auto uri = ParseUri("foo://example.com:8042/over/there");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_WithQuery) {
auto uri = ParseUri("foo://example.com:8042/over/there?name=ferret");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT_USTREQ("name=ferret", uri->query);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_WithFragment) {
auto uri = ParseUri("foo://example.com:8042/over/there#fragment");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT(uri->query == nullptr);
EXPECT_USTREQ("fragment", uri->fragment);
}
TEST_CASE(ParseUri_WithScheme_WithQueryWithFragment) {
auto uri = ParseUri("foo://example.com:8042/over/there?name=ferret#fragment");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT_USTREQ("name=ferret", uri->query);
EXPECT_USTREQ("fragment", uri->fragment);
}
TEST_CASE(ParseUri_WithScheme_WithUser) {
auto uri = ParseUri("foo://user@example.com:8042/over/there");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT_USTREQ("user", uri->userinfo);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_ShortPath) {
auto uri = ParseUri("foo://example.com:8042/");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_EmptyPath) {
auto uri = ParseUri("foo://example.com:8042");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_Rootless1) {
auto uri = ParseUri("foo:here");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("here", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_Rootless2) {
auto uri = ParseUri("foo:or/here");
EXPECT(uri);
EXPECT_USTREQ("foo", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("or/here", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_AbsPath_WithAuthority) {
auto uri = ParseUri("//example.com:8042/over/there");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("example.com", uri->host);
EXPECT_USTREQ("8042", uri->port);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_AbsPath_NoAuthority) {
auto uri = ParseUri("/over/there");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/over/there", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
// Colons are permitted in path segments, in many cases.
TEST_CASE(ParseUri_NoScheme_AbsPath_StrayColon) {
auto uri = ParseUri("/ov:er/there");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/ov:er/there", uri->path);
EXPECT(uri->query == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Rootless1) {
auto uri = ParseUri("here");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("here", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Rootless2) {
auto uri = ParseUri("or/here");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("or/here", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Empty) {
auto uri = ParseUri("");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_QueryOnly) {
auto uri = ParseUri("?name=ferret");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("", uri->path);
EXPECT_USTREQ("name=ferret", uri->query);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_FragmentOnly) {
auto uri = ParseUri("#fragment");
EXPECT(uri);
EXPECT(uri->scheme == nullptr);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("", uri->path);
EXPECT(uri->query == nullptr);
EXPECT_USTREQ("fragment", uri->fragment);
}
TEST_CASE(ParseUri_LowerCaseScheme) {
auto uri = ParseUri("ScHeMe:path");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("path", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_PathQueryFragment) {
auto uri =
ParseUri("scheme:/This%09Is A P%61th?This%09Is A Qu%65ry#A Fr%61gment");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/This%09Is%20A%20Path", uri->path);
EXPECT_USTREQ("This%09Is%20A%20Query", uri->query);
EXPECT_USTREQ("A%20Fragment", uri->fragment);
}
TEST_CASE(ParseUri_NormalizeEscapes_UppercaseEscapesPreferred) {
auto uri = ParseUri("scheme:/%1b%1B");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/%1B%1B", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_Authority) {
auto uri = ParseUri("scheme://UsEr N%61%4de@h%4FsT.c%6fm:80/");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT_USTREQ("UsEr%20NaMe", uri->userinfo); // Normalized, case preserved.
EXPECT_USTREQ("host.com", uri->host); // Normalized, lower-cased.
EXPECT_USTREQ("80", uri->port);
EXPECT_USTREQ("/", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_UppercaseEscapeInHost) {
auto uri = ParseUri("scheme://tEst%1b/");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT_USTREQ("test%1B", uri->host); // Notice that %1B is upper-cased.
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/", uri->path);
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ParseUri_BrokenEscapeSequence) {
auto uri = ParseUri("scheme:/%1g");
EXPECT(uri);
EXPECT_USTREQ("scheme", uri->scheme);
EXPECT(uri->userinfo == nullptr);
EXPECT(uri->host == nullptr);
EXPECT(uri->port == nullptr);
EXPECT_USTREQ("/%1g", uri->path); // Broken sequence is unchanged.
EXPECT(uri->query == nullptr);
EXPECT(uri->fragment == nullptr);
}
TEST_CASE(ResolveUri_WithScheme_NoAuthorityNoQuery) {
auto target_uri = ResolveUri("rscheme:/ref/path",
"bscheme://buser@bhost:11/base/path?baseQuery");
EXPECT(target_uri);
EXPECT_USTREQ("rscheme:/ref/path", target_uri);
}
TEST_CASE(ResolveUri_WithScheme_WithAuthorityWithQuery) {
auto target_uri = ResolveUri("rscheme://ruser@rhost:22/ref/path?refQuery",
"bscheme://buser@bhost:11/base/path?baseQuery");
EXPECT(target_uri);
EXPECT_USTREQ("rscheme://ruser@rhost:22/ref/path?refQuery", target_uri);
}
TEST_CASE(ResolveUri_NoScheme_WithAuthority) {
auto target_uri = ResolveUri("//ruser@rhost:22/ref/path",
"bscheme://buser@bhost:11/base/path?baseQuery");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://ruser@rhost:22/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_AbsolutePath) {
auto target_uri =
ResolveUri("/ref/path", "bscheme://buser@bhost:11/base/path?baseQuery");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://buser@bhost:11/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePath) {
auto target_uri =
ResolveUri("ref/path", "bscheme://buser@bhost:11/base/path?baseQuery");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://buser@bhost:11/base/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePathEmptyBasePath) {
auto target_uri = ResolveUri("ref/path", "bscheme://buser@bhost:11");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://buser@bhost:11/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePathWeirdBasePath) {
auto target_uri = ResolveUri("ref/path", "bscheme:base");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme:ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPath) {
auto target_uri =
ResolveUri("", "bscheme://buser@bhost:11/base/path?baseQuery#bfragment");
EXPECT(target_uri);
// Note that we drop the base fragment from the resolved uri->
EXPECT_USTREQ("bscheme://buser@bhost:11/base/path?baseQuery", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPathWithQuery) {
auto target_uri = ResolveUri(
"?refQuery", "bscheme://buser@bhost:11/base/path?baseQuery#bfragment");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://buser@bhost:11/base/path?refQuery", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPathWithFragment) {
auto target_uri = ResolveUri(
"#rfragment", "bscheme://buser@bhost:11/base/path?baseQuery#bfragment");
EXPECT(target_uri);
EXPECT_USTREQ("bscheme://buser@bhost:11/base/path?baseQuery#rfragment",
target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveOneDotSegment) {
auto target_uri = ResolveUri("./refpath", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/a/b/c/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTwoDotSegments) {
auto target_uri = ResolveUri("././refpath", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/a/b/c/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveOneDotDotSegment) {
auto target_uri = ResolveUri("../refpath", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/a/b/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTwoDotDotSegments) {
auto target_uri = ResolveUri("../../refpath", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/a/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTooManyDotDotSegments) {
auto target_uri =
ResolveUri("../../../../../../../../../refpath", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsNothingLeft1) {
auto target_uri = ResolveUri("../../../../..", "scheme://auth/a/b/c/d");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsNothingLeft2) {
auto target_uri = ResolveUri(".", "scheme://auth/");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsInitialPrefix) {
auto target_uri = ResolveUri("../../../../refpath", "scheme://auth");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsMixed) {
auto target_uri = ResolveUri("../../1/./2/../3/4/../5/././6/../7",
"scheme://auth/a/b/c/d/e");
EXPECT(target_uri);
EXPECT_USTREQ("scheme://auth/a/b/1/3/5/7", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_PathQueryFragment) {
auto target_uri = ResolveUri(
"#A Fr%61gment", "scheme:/This%09Is A P%61th?This%09Is A Qu%65ry");
EXPECT(target_uri);
EXPECT_USTREQ(
"scheme:/This%09Is%20A%20Path?This%09Is%20A%20Query#A%20Fragment",
target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_UppercaseHexPreferred) {
auto target_uri = ResolveUri("", "scheme:/%1b%1B");
EXPECT(target_uri);
EXPECT_USTREQ("scheme:/%1B%1B", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_Authority) {
auto target_uri = ResolveUri("", "scheme://UsEr N%61%4de@h%4FsT.c%6fm:80/");
EXPECT(target_uri);
// userinfo is normalized and case is preserved. host is normalized
// and lower-cased.
EXPECT_USTREQ("scheme://UsEr%20NaMe@host.com:80/", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_BrokenEscapeSequence) {
auto target_uri = ResolveUri("", "scheme:/%1g");
EXPECT(target_uri);
// We don't change broken escape sequences.
EXPECT_USTREQ("scheme:/%1g", target_uri);
}
TEST_CASE(ResolveUri_DataUri) {
const char* data_uri =
"data:application/"
"dart;charset=utf-8,%20%20%20%20%20%20%20%20import%20%22dart:isolate%22;%"
"0A%0A%20%20%20%20%20%20%20%20import%20%22package:stream_channel/"
"stream_channel.dart%22;%0A%0A%20%20%20%20%20%20%20%20import%20%"
"22package:test/src/runner/plugin/"
"remote_platform_helpers.dart%22;%0A%20%20%20%20%20%20%20%20import%20%"
"22package:test/src/runner/vm/"
"catch_isolate_errors.dart%22;%0A%0A%20%20%20%20%20%20%20%20import%20%"
"22file:///home/sra/xxxx/dev_compiler/test/"
"all_tests.dart%22%20as%20test;%0A%0A%20%20%20%20%20%20%20%20void%20main("
"_,%20SendPort%20message)%20%7B%0A%20%20%20%20%20%20%20%20%20%20var%"
"20channel%20=%20serializeSuite(()%20%7B%0A%20%20%20%20%20%20%20%20%20%"
"20%20%20catchIsolateErrors();%0A%20%20%20%20%20%20%20%20%20%20%20%"
"20return%20test.main;%0A%20%20%20%20%20%20%20%20%20%20%7D);%0A%20%20%20%"
"20%20%20%20%20%20%20new%20IsolateChannel.connectSend(message).pipe("
"channel);%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20";
auto target_uri = ResolveUri(
data_uri, "bscheme://buser@bhost:11/base/path?baseQuery#bfragment");
EXPECT(target_uri);
EXPECT_USTREQ(data_uri, target_uri);
}
// dart:core Uri allows for the base url to be relative (no scheme, no
// authority, relative path) but this behavior is not in RFC 3986. We
// do not implement this.
TEST_CASE(ResolveUri_RelativeBase_NotImplemented) {
EXPECT(!ResolveUri("../r1", "b1/b2").get());
EXPECT(!ResolveUri("..", "b1/b2").get());
EXPECT(!ResolveUri("../..", "b1/b2").get());
EXPECT(!ResolveUri("../../..", "b1/b2").get());
EXPECT(!ResolveUri("../../../r1", "b1/b2").get());
EXPECT(!ResolveUri("../r1", "../../b1/b2/b3").get());
EXPECT(!ResolveUri("../../../r1", "../../b1/b2/b3").get());
}
CStringUniquePtr TestResolve(const char* base_uri, const char* uri) {
auto target_uri = ResolveUri(uri, base_uri);
EXPECT(target_uri);
return target_uri;
}
// This test is ported from sdk/tests/corelib/uri_test.dart (testUriPerRFCs).
TEST_CASE(ResolveUri_TestUriPerRFCs) {
const char* base = "http://a/b/c/d;p?q";
// From RFC 3986
EXPECT_USTREQ("g:h", TestResolve(base, "g:h"));
EXPECT_USTREQ("http://a/b/c/g", TestResolve(base, "g"));
EXPECT_USTREQ("http://a/b/c/g", TestResolve(base, "./g"));
EXPECT_USTREQ("http://a/b/c/g/", TestResolve(base, "g/"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "/g"));
EXPECT_USTREQ("http://g", TestResolve(base, "//g"));
EXPECT_USTREQ("http://a/b/c/d;p?y", TestResolve(base, "?y"));
EXPECT_USTREQ("http://a/b/c/g?y", TestResolve(base, "g?y"));
EXPECT_USTREQ("http://a/b/c/d;p?q#s", TestResolve(base, "#s"));
EXPECT_USTREQ("http://a/b/c/g#s", TestResolve(base, "g#s"));
EXPECT_USTREQ("http://a/b/c/g?y#s", TestResolve(base, "g?y#s"));
EXPECT_USTREQ("http://a/b/c/;x", TestResolve(base, ";x"));
EXPECT_USTREQ("http://a/b/c/g;x", TestResolve(base, "g;x"));
EXPECT_USTREQ("http://a/b/c/g;x?y#s", TestResolve(base, "g;x?y#s"));
EXPECT_USTREQ("http://a/b/c/d;p?q", TestResolve(base, ""));
EXPECT_USTREQ("http://a/b/c/", TestResolve(base, "."));
EXPECT_USTREQ("http://a/b/c/", TestResolve(base, "./"));
EXPECT_USTREQ("http://a/b/", TestResolve(base, ".."));
EXPECT_USTREQ("http://a/b/", TestResolve(base, "../"));
EXPECT_USTREQ("http://a/b/g", TestResolve(base, "../g"));
EXPECT_USTREQ("http://a/", TestResolve(base, "../.."));
EXPECT_USTREQ("http://a/", TestResolve(base, "../../"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "../../g"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "../../../g"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "../../../../g"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "/./g"));
EXPECT_USTREQ("http://a/g", TestResolve(base, "/../g"));
EXPECT_USTREQ("http://a/b/c/g.", TestResolve(base, "g."));
EXPECT_USTREQ("http://a/b/c/.g", TestResolve(base, ".g"));
EXPECT_USTREQ("http://a/b/c/g..", TestResolve(base, "g.."));
EXPECT_USTREQ("http://a/b/c/..g", TestResolve(base, "..g"));
EXPECT_USTREQ("http://a/b/g", TestResolve(base, "./../g"));
EXPECT_USTREQ("http://a/b/c/g/", TestResolve(base, "./g/."));
EXPECT_USTREQ("http://a/b/c/g/h", TestResolve(base, "g/./h"));
EXPECT_USTREQ("http://a/b/c/h", TestResolve(base, "g/../h"));
EXPECT_USTREQ("http://a/b/c/g;x=1/y", TestResolve(base, "g;x=1/./y"));
EXPECT_USTREQ("http://a/b/c/y", TestResolve(base, "g;x=1/../y"));
EXPECT_USTREQ("http://a/b/c/g?y/./x", TestResolve(base, "g?y/./x"));
EXPECT_USTREQ("http://a/b/c/g?y/../x", TestResolve(base, "g?y/../x"));
EXPECT_USTREQ("http://a/b/c/g#s/./x", TestResolve(base, "g#s/./x"));
EXPECT_USTREQ("http://a/b/c/g#s/../x", TestResolve(base, "g#s/../x"));
EXPECT_USTREQ("http:g", TestResolve(base, "http:g"));
// Additional tests (not from RFC 3986).
EXPECT_USTREQ("http://a/b/g;p/h;s", TestResolve(base, "../g;p/h;s"));
base = "s:a/b";
EXPECT_USTREQ("s:/c", TestResolve(base, "../c"));
}
// This test is ported from sdk/tests/corelib/uri_test.dart (testResolvePath).
TEST_CASE(ResolveUri_MoreDotSegmentTests) {
const char* base = "/";
EXPECT_USTREQ("/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_USTREQ("/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_USTREQ("/mid/6", TestResolve(base, "mid/content=5/../6"));
EXPECT_USTREQ("/a/b/e", TestResolve(base, "a/b/c/d/../../e"));
EXPECT_USTREQ("/a/b/e", TestResolve(base, "../a/b/c/d/../../e"));
EXPECT_USTREQ("/a/b/e", TestResolve(base, "./a/b/c/d/../../e"));
EXPECT_USTREQ("/a/b/e", TestResolve(base, "../a/b/./c/d/../../e"));
EXPECT_USTREQ("/a/b/e", TestResolve(base, "./a/b/./c/d/../../e"));
EXPECT_USTREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/."));
EXPECT_USTREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/./."));
EXPECT_USTREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/././."));
#define LH "http://localhost"
base = LH;
EXPECT_USTREQ(LH "/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_USTREQ(LH "/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_USTREQ(LH "/mid/6", TestResolve(base, "mid/content=5/../6"));
EXPECT_USTREQ(LH "/a/b/e", TestResolve(base, "a/b/c/d/../../e"));
EXPECT_USTREQ(LH "/a/b/e", TestResolve(base, "../a/b/c/d/../../e"));
EXPECT_USTREQ(LH "/a/b/e", TestResolve(base, "./a/b/c/d/../../e"));
EXPECT_USTREQ(LH "/a/b/e", TestResolve(base, "../a/b/./c/d/../../e"));
EXPECT_USTREQ(LH "/a/b/e", TestResolve(base, "./a/b/./c/d/../../e"));
EXPECT_USTREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/."));
EXPECT_USTREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/./."));
EXPECT_USTREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/././."));
#undef LH
}
TEST_CASE(ResolveUri_WindowsPaths_Forwardslash_NoScheme) {
EXPECT_USTREQ(
"c:/Users/USERNA~1/AppData/Local/Temp/a/b.dll",
TestResolve("C:/Users/USERNA~1/AppData/Local/Temp/a/out.dill", "b.dll"));
}
// > Here are some examples which may be accepted by some applications on
// > Windows systems
// https://en.wikipedia.org/wiki/File_URI_scheme
// "file:///C:/"
TEST_CASE(ResolveUri_WindowsPaths_Forwardslash_FileScheme) {
EXPECT_USTREQ(
"file:///"
"C:/Users/USERNA~1/AppData/Local/Temp/a/b.dll",
TestResolve("file:///C:/Users/USERNA~1/AppData/Local/Temp/a/out.dill",
"b.dll"));
}
TEST_CASE(ResolveUri_WindowsPaths_Backslash) {
EXPECT_USTREQ(
"file:///b.dll",
TestResolve(
"file:///C:\\Users\\USERNA~1\\AppData\\Local\\Temp\\a\\out.dill",
"b.dll"));
}
} // namespace dart

View file

@ -22,6 +22,10 @@ class CAllocUniquePtr : public std::unique_ptr<T, decltype(std::free)*> {
: std::unique_ptr<T, decltype(std::free)*>(nullptr, std::free) {}
explicit CAllocUniquePtr(T* value)
: std::unique_ptr<T, decltype(std::free)*>(value, std::free) {}
CAllocUniquePtr& operator=(nullptr_t value) {
std::unique_ptr<T, decltype(std::free)*>::operator=(value);
return *this;
}
};
using CStringUniquePtr = CAllocUniquePtr<char>;

View file

@ -83,7 +83,6 @@ main() {
"Dart_CurrentIsolateGroupId",
"Dart_DebugName",
"Dart_DebugNameToCString",
"Dart_DefaultCanonicalizeUrl",
"Dart_DeferredLoadComplete",
"Dart_DeferredLoadCompleteError",
"Dart_DeleteFinalizableHandle",

View file

@ -53,7 +53,6 @@
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/uri.h"
#include "vm/version.h"
#include "vm/zone_text_buffer.h"
@ -5387,29 +5386,6 @@ Dart_SetLibraryTagHandler(Dart_LibraryTagHandler handler) {
return Api::Success();
}
DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url,
Dart_Handle url) {
DARTSCOPE(Thread::Current());
API_TIMELINE_DURATION(T);
CHECK_CALLBACK_STATE(T);
const String& base_uri = Api::UnwrapStringHandle(Z, base_url);
if (base_uri.IsNull()) {
RETURN_TYPE_ERROR(Z, base_url, String);
}
const String& uri = Api::UnwrapStringHandle(Z, url);
if (uri.IsNull()) {
RETURN_TYPE_ERROR(Z, url, String);
}
const char* resolved_uri;
if (!ResolveUri(uri.ToCString(), base_uri.ToCString(), &resolved_uri)) {
return Api::NewError("%s: Unable to canonicalize uri '%s'.", CURRENT_FUNC,
uri.ToCString());
}
return Api::NewHandle(T, String::New(resolved_uri));
}
DART_EXPORT Dart_Handle
Dart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler) {
Isolate* isolate = Isolate::Current();

View file

@ -11,6 +11,7 @@
#include "bin/isolate_data.h"
#include "platform/globals.h"
#include "platform/uri.h"
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/assembler/disassembler.h"
@ -379,7 +380,24 @@ static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag,
if (Dart_IsError(library_url)) {
return library_url;
}
return Dart_DefaultCanonicalizeUrl(library_url, url);
const char* library_url_cstr;
Dart_Handle result = Dart_StringToCString(library_url, &library_url_cstr);
if (Dart_IsError(result)) {
return result;
}
const char* url_cstr;
result = Dart_StringToCString(url, &url_cstr);
if (Dart_IsError(result)) {
return result;
}
CStringUniquePtr resolved_uri = ResolveUri(url_cstr, library_url_cstr);
if (!resolved_uri) {
return DartUtils::NewError("%s: Unable to canonicalize uri '%s'.",
__FUNCTION__, url_cstr);
}
result = Dart_NewStringFromCString(resolved_uri.get());
return result;
}
UNREACHABLE();
return Dart_Null();

View file

@ -1,33 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_URI_H_
#define RUNTIME_VM_URI_H_
#include "platform/utils.h"
#include "vm/globals.h"
namespace dart {
struct ParsedUri {
const char* scheme;
const char* userinfo;
const char* host;
const char* port;
const char* path;
const char* query;
const char* fragment;
};
// Parses a uri into its parts. Returns false if the parse fails.
bool ParseUri(const char* uri, ParsedUri* parsed_uri);
// Resolves some reference uri with respect to a base uri.
bool ResolveUri(const char* ref_uri,
const char* base_uri,
const char** target_uri);
} // namespace dart
#endif // RUNTIME_VM_URI_H_

View file

@ -1,623 +0,0 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/uri.h"
#include "vm/unit_test.h"
namespace dart {
TEST_CASE(ParseUri_WithScheme_NoQueryNoUser) {
ParsedUri uri;
EXPECT(ParseUri("foo://example.com:8042/over/there", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_WithQuery) {
ParsedUri uri;
EXPECT(ParseUri("foo://example.com:8042/over/there?name=ferret", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT_STREQ("name=ferret", uri.query);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_WithFragment) {
ParsedUri uri;
EXPECT(ParseUri("foo://example.com:8042/over/there#fragment", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT(uri.query == nullptr);
EXPECT_STREQ("fragment", uri.fragment);
}
TEST_CASE(ParseUri_WithScheme_WithQueryWithFragment) {
ParsedUri uri;
EXPECT(
ParseUri("foo://example.com:8042/over/there?name=ferret#fragment", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT_STREQ("name=ferret", uri.query);
EXPECT_STREQ("fragment", uri.fragment);
}
TEST_CASE(ParseUri_WithScheme_WithUser) {
ParsedUri uri;
EXPECT(ParseUri("foo://user@example.com:8042/over/there", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT_STREQ("user", uri.userinfo);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_ShortPath) {
ParsedUri uri;
EXPECT(ParseUri("foo://example.com:8042/", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_EmptyPath) {
ParsedUri uri;
EXPECT(ParseUri("foo://example.com:8042", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_Rootless1) {
ParsedUri uri;
EXPECT(ParseUri("foo:here", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("here", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_WithScheme_Rootless2) {
ParsedUri uri;
EXPECT(ParseUri("foo:or/here", &uri));
EXPECT_STREQ("foo", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("or/here", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_AbsPath_WithAuthority) {
ParsedUri uri;
EXPECT(ParseUri("//example.com:8042/over/there", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("example.com", uri.host);
EXPECT_STREQ("8042", uri.port);
EXPECT_STREQ("/over/there", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_AbsPath_NoAuthority) {
ParsedUri uri;
EXPECT(ParseUri("/over/there", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/over/there", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
// Colons are permitted in path segments, in many cases.
TEST_CASE(ParseUri_NoScheme_AbsPath_StrayColon) {
ParsedUri uri;
EXPECT(ParseUri("/ov:er/there", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/ov:er/there", uri.path);
EXPECT(uri.query == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Rootless1) {
ParsedUri uri;
EXPECT(ParseUri("here", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("here", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Rootless2) {
ParsedUri uri;
EXPECT(ParseUri("or/here", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("or/here", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_Empty) {
ParsedUri uri;
EXPECT(ParseUri("", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_QueryOnly) {
ParsedUri uri;
EXPECT(ParseUri("?name=ferret", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("", uri.path);
EXPECT_STREQ("name=ferret", uri.query);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NoScheme_FragmentOnly) {
ParsedUri uri;
EXPECT(ParseUri("#fragment", &uri));
EXPECT(uri.scheme == nullptr);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("", uri.path);
EXPECT(uri.query == nullptr);
EXPECT_STREQ("fragment", uri.fragment);
}
TEST_CASE(ParseUri_LowerCaseScheme) {
ParsedUri uri;
EXPECT(ParseUri("ScHeMe:path", &uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("path", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_PathQueryFragment) {
ParsedUri uri;
EXPECT(ParseUri("scheme:/This%09Is A P%61th?This%09Is A Qu%65ry#A Fr%61gment",
&uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/This%09Is%20A%20Path", uri.path);
EXPECT_STREQ("This%09Is%20A%20Query", uri.query);
EXPECT_STREQ("A%20Fragment", uri.fragment);
}
TEST_CASE(ParseUri_NormalizeEscapes_UppercaseEscapesPreferred) {
ParsedUri uri;
EXPECT(ParseUri("scheme:/%1b%1B", &uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/%1B%1B", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_Authority) {
ParsedUri uri;
EXPECT(ParseUri("scheme://UsEr N%61%4de@h%4FsT.c%6fm:80/", &uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT_STREQ("UsEr%20NaMe", uri.userinfo); // Normalized, case preserved.
EXPECT_STREQ("host.com", uri.host); // Normalized, lower-cased.
EXPECT_STREQ("80", uri.port);
EXPECT_STREQ("/", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_NormalizeEscapes_UppercaseEscapeInHost) {
ParsedUri uri;
EXPECT(ParseUri("scheme://tEst%1b/", &uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT_STREQ("test%1B", uri.host); // Notice that %1B is upper-cased.
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/", uri.path);
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ParseUri_BrokenEscapeSequence) {
ParsedUri uri;
EXPECT(ParseUri("scheme:/%1g", &uri));
EXPECT_STREQ("scheme", uri.scheme);
EXPECT(uri.userinfo == nullptr);
EXPECT(uri.host == nullptr);
EXPECT(uri.port == nullptr);
EXPECT_STREQ("/%1g", uri.path); // Broken sequence is unchanged.
EXPECT(uri.query == nullptr);
EXPECT(uri.fragment == nullptr);
}
TEST_CASE(ResolveUri_WithScheme_NoAuthorityNoQuery) {
const char* target_uri;
EXPECT(ResolveUri("rscheme:/ref/path",
"bscheme://buser@bhost:11/base/path?baseQuery",
&target_uri));
EXPECT_STREQ("rscheme:/ref/path", target_uri);
}
TEST_CASE(ResolveUri_WithScheme_WithAuthorityWithQuery) {
const char* target_uri;
EXPECT(ResolveUri("rscheme://ruser@rhost:22/ref/path?refQuery",
"bscheme://buser@bhost:11/base/path?baseQuery",
&target_uri));
EXPECT_STREQ("rscheme://ruser@rhost:22/ref/path?refQuery", target_uri);
}
TEST_CASE(ResolveUri_NoScheme_WithAuthority) {
const char* target_uri;
EXPECT(ResolveUri("//ruser@rhost:22/ref/path",
"bscheme://buser@bhost:11/base/path?baseQuery",
&target_uri));
EXPECT_STREQ("bscheme://ruser@rhost:22/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_AbsolutePath) {
const char* target_uri;
EXPECT(ResolveUri("/ref/path", "bscheme://buser@bhost:11/base/path?baseQuery",
&target_uri));
EXPECT_STREQ("bscheme://buser@bhost:11/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePath) {
const char* target_uri;
EXPECT(ResolveUri("ref/path", "bscheme://buser@bhost:11/base/path?baseQuery",
&target_uri));
EXPECT_STREQ("bscheme://buser@bhost:11/base/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePathEmptyBasePath) {
const char* target_uri;
EXPECT(ResolveUri("ref/path", "bscheme://buser@bhost:11", &target_uri));
EXPECT_STREQ("bscheme://buser@bhost:11/ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_RelativePathWeirdBasePath) {
const char* target_uri;
EXPECT(ResolveUri("ref/path", "bscheme:base", &target_uri));
EXPECT_STREQ("bscheme:ref/path", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPath) {
const char* target_uri;
EXPECT(ResolveUri("",
"bscheme://buser@bhost:11/base/path?baseQuery#bfragment",
&target_uri));
// Note that we drop the base fragment from the resolved uri.
EXPECT_STREQ("bscheme://buser@bhost:11/base/path?baseQuery", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPathWithQuery) {
const char* target_uri;
EXPECT(ResolveUri("?refQuery",
"bscheme://buser@bhost:11/base/path?baseQuery#bfragment",
&target_uri));
EXPECT_STREQ("bscheme://buser@bhost:11/base/path?refQuery", target_uri);
}
TEST_CASE(ResolveUri_NoSchemeNoAuthority_EmptyPathWithFragment) {
const char* target_uri;
EXPECT(ResolveUri("#rfragment",
"bscheme://buser@bhost:11/base/path?baseQuery#bfragment",
&target_uri));
EXPECT_STREQ("bscheme://buser@bhost:11/base/path?baseQuery#rfragment",
target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveOneDotSegment) {
const char* target_uri;
EXPECT(ResolveUri("./refpath", "scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/a/b/c/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTwoDotSegments) {
const char* target_uri;
EXPECT(ResolveUri("././refpath", "scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/a/b/c/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveOneDotDotSegment) {
const char* target_uri;
EXPECT(ResolveUri("../refpath", "scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/a/b/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTwoDotDotSegments) {
const char* target_uri;
EXPECT(ResolveUri("../../refpath", "scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/a/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveTooManyDotDotSegments) {
const char* target_uri;
EXPECT(ResolveUri("../../../../../../../../../refpath",
"scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsNothingLeft1) {
const char* target_uri;
EXPECT(ResolveUri("../../../../..", "scheme://auth/a/b/c/d", &target_uri));
EXPECT_STREQ("scheme://auth/", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsNothingLeft2) {
const char* target_uri;
EXPECT(ResolveUri(".", "scheme://auth/", &target_uri));
EXPECT_STREQ("scheme://auth/", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsInitialPrefix) {
const char* target_uri;
EXPECT(ResolveUri("../../../../refpath", "scheme://auth", &target_uri));
EXPECT_STREQ("scheme://auth/refpath", target_uri);
}
TEST_CASE(ResolveUri_RemoveDots_RemoveDotSegmentsMixed) {
const char* target_uri;
EXPECT(ResolveUri("../../1/./2/../3/4/../5/././6/../7",
"scheme://auth/a/b/c/d/e", &target_uri));
EXPECT_STREQ("scheme://auth/a/b/1/3/5/7", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_PathQueryFragment) {
const char* target_uri;
EXPECT(ResolveUri("#A Fr%61gment",
"scheme:/This%09Is A P%61th?This%09Is A Qu%65ry",
&target_uri));
EXPECT_STREQ(
"scheme:/This%09Is%20A%20Path?This%09Is%20A%20Query#A%20Fragment",
target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_UppercaseHexPreferred) {
const char* target_uri;
EXPECT(ResolveUri("", "scheme:/%1b%1B", &target_uri));
EXPECT_STREQ("scheme:/%1B%1B", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_Authority) {
const char* target_uri;
EXPECT(
ResolveUri("", "scheme://UsEr N%61%4de@h%4FsT.c%6fm:80/", &target_uri));
// userinfo is normalized and case is preserved. host is normalized
// and lower-cased.
EXPECT_STREQ("scheme://UsEr%20NaMe@host.com:80/", target_uri);
}
TEST_CASE(ResolveUri_NormalizeEscapes_BrokenEscapeSequence) {
const char* target_uri;
EXPECT(ResolveUri("", "scheme:/%1g", &target_uri));
// We don't change broken escape sequences.
EXPECT_STREQ("scheme:/%1g", target_uri);
}
TEST_CASE(ResolveUri_DataUri) {
const char* data_uri =
"data:application/"
"dart;charset=utf-8,%20%20%20%20%20%20%20%20import%20%22dart:isolate%22;%"
"0A%0A%20%20%20%20%20%20%20%20import%20%22package:stream_channel/"
"stream_channel.dart%22;%0A%0A%20%20%20%20%20%20%20%20import%20%"
"22package:test/src/runner/plugin/"
"remote_platform_helpers.dart%22;%0A%20%20%20%20%20%20%20%20import%20%"
"22package:test/src/runner/vm/"
"catch_isolate_errors.dart%22;%0A%0A%20%20%20%20%20%20%20%20import%20%"
"22file:///home/sra/xxxx/dev_compiler/test/"
"all_tests.dart%22%20as%20test;%0A%0A%20%20%20%20%20%20%20%20void%20main("
"_,%20SendPort%20message)%20%7B%0A%20%20%20%20%20%20%20%20%20%20var%"
"20channel%20=%20serializeSuite(()%20%7B%0A%20%20%20%20%20%20%20%20%20%"
"20%20%20catchIsolateErrors();%0A%20%20%20%20%20%20%20%20%20%20%20%"
"20return%20test.main;%0A%20%20%20%20%20%20%20%20%20%20%7D);%0A%20%20%20%"
"20%20%20%20%20%20%20new%20IsolateChannel.connectSend(message).pipe("
"channel);%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20";
const char* target_uri;
EXPECT(ResolveUri(data_uri,
"bscheme://buser@bhost:11/base/path?baseQuery#bfragment",
&target_uri));
EXPECT_STREQ(data_uri, target_uri);
}
// dart:core Uri allows for the base url to be relative (no scheme, no
// authority, relative path) but this behavior is not in RFC 3986. We
// do not implement this.
TEST_CASE(ResolveUri_RelativeBase_NotImplemented) {
const char* target_uri;
EXPECT(!ResolveUri("../r1", "b1/b2", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("..", "b1/b2", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("../..", "b1/b2", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("../../..", "b1/b2", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("../../../r1", "b1/b2", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("../r1", "../../b1/b2/b3", &target_uri));
EXPECT(target_uri == nullptr);
EXPECT(!ResolveUri("../../../r1", "../../b1/b2/b3", &target_uri));
EXPECT(target_uri == nullptr);
}
static const char* TestResolve(const char* base_uri, const char* uri) {
const char* target_uri;
EXPECT(ResolveUri(uri, base_uri, &target_uri));
return target_uri;
}
// This test is ported from sdk/tests/corelib/uri_test.dart (testUriPerRFCs).
TEST_CASE(ResolveUri_TestUriPerRFCs) {
const char* base = "http://a/b/c/d;p?q";
// From RFC 3986
EXPECT_STREQ("g:h", TestResolve(base, "g:h"));
EXPECT_STREQ("http://a/b/c/g", TestResolve(base, "g"));
EXPECT_STREQ("http://a/b/c/g", TestResolve(base, "./g"));
EXPECT_STREQ("http://a/b/c/g/", TestResolve(base, "g/"));
EXPECT_STREQ("http://a/g", TestResolve(base, "/g"));
EXPECT_STREQ("http://g", TestResolve(base, "//g"));
EXPECT_STREQ("http://a/b/c/d;p?y", TestResolve(base, "?y"));
EXPECT_STREQ("http://a/b/c/g?y", TestResolve(base, "g?y"));
EXPECT_STREQ("http://a/b/c/d;p?q#s", TestResolve(base, "#s"));
EXPECT_STREQ("http://a/b/c/g#s", TestResolve(base, "g#s"));
EXPECT_STREQ("http://a/b/c/g?y#s", TestResolve(base, "g?y#s"));
EXPECT_STREQ("http://a/b/c/;x", TestResolve(base, ";x"));
EXPECT_STREQ("http://a/b/c/g;x", TestResolve(base, "g;x"));
EXPECT_STREQ("http://a/b/c/g;x?y#s", TestResolve(base, "g;x?y#s"));
EXPECT_STREQ("http://a/b/c/d;p?q", TestResolve(base, ""));
EXPECT_STREQ("http://a/b/c/", TestResolve(base, "."));
EXPECT_STREQ("http://a/b/c/", TestResolve(base, "./"));
EXPECT_STREQ("http://a/b/", TestResolve(base, ".."));
EXPECT_STREQ("http://a/b/", TestResolve(base, "../"));
EXPECT_STREQ("http://a/b/g", TestResolve(base, "../g"));
EXPECT_STREQ("http://a/", TestResolve(base, "../.."));
EXPECT_STREQ("http://a/", TestResolve(base, "../../"));
EXPECT_STREQ("http://a/g", TestResolve(base, "../../g"));
EXPECT_STREQ("http://a/g", TestResolve(base, "../../../g"));
EXPECT_STREQ("http://a/g", TestResolve(base, "../../../../g"));
EXPECT_STREQ("http://a/g", TestResolve(base, "/./g"));
EXPECT_STREQ("http://a/g", TestResolve(base, "/../g"));
EXPECT_STREQ("http://a/b/c/g.", TestResolve(base, "g."));
EXPECT_STREQ("http://a/b/c/.g", TestResolve(base, ".g"));
EXPECT_STREQ("http://a/b/c/g..", TestResolve(base, "g.."));
EXPECT_STREQ("http://a/b/c/..g", TestResolve(base, "..g"));
EXPECT_STREQ("http://a/b/g", TestResolve(base, "./../g"));
EXPECT_STREQ("http://a/b/c/g/", TestResolve(base, "./g/."));
EXPECT_STREQ("http://a/b/c/g/h", TestResolve(base, "g/./h"));
EXPECT_STREQ("http://a/b/c/h", TestResolve(base, "g/../h"));
EXPECT_STREQ("http://a/b/c/g;x=1/y", TestResolve(base, "g;x=1/./y"));
EXPECT_STREQ("http://a/b/c/y", TestResolve(base, "g;x=1/../y"));
EXPECT_STREQ("http://a/b/c/g?y/./x", TestResolve(base, "g?y/./x"));
EXPECT_STREQ("http://a/b/c/g?y/../x", TestResolve(base, "g?y/../x"));
EXPECT_STREQ("http://a/b/c/g#s/./x", TestResolve(base, "g#s/./x"));
EXPECT_STREQ("http://a/b/c/g#s/../x", TestResolve(base, "g#s/../x"));
EXPECT_STREQ("http:g", TestResolve(base, "http:g"));
// Additional tests (not from RFC 3986).
EXPECT_STREQ("http://a/b/g;p/h;s", TestResolve(base, "../g;p/h;s"));
base = "s:a/b";
EXPECT_STREQ("s:/c", TestResolve(base, "../c"));
}
// This test is ported from sdk/tests/corelib/uri_test.dart (testResolvePath).
TEST_CASE(ResolveUri_MoreDotSegmentTests) {
const char* base = "/";
EXPECT_STREQ("/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_STREQ("/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_STREQ("/mid/6", TestResolve(base, "mid/content=5/../6"));
EXPECT_STREQ("/a/b/e", TestResolve(base, "a/b/c/d/../../e"));
EXPECT_STREQ("/a/b/e", TestResolve(base, "../a/b/c/d/../../e"));
EXPECT_STREQ("/a/b/e", TestResolve(base, "./a/b/c/d/../../e"));
EXPECT_STREQ("/a/b/e", TestResolve(base, "../a/b/./c/d/../../e"));
EXPECT_STREQ("/a/b/e", TestResolve(base, "./a/b/./c/d/../../e"));
EXPECT_STREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/."));
EXPECT_STREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/./."));
EXPECT_STREQ("/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/././."));
#define LH "http://localhost"
base = LH;
EXPECT_STREQ(LH "/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_STREQ(LH "/a/g", TestResolve(base, "/a/b/c/./../../g"));
EXPECT_STREQ(LH "/mid/6", TestResolve(base, "mid/content=5/../6"));
EXPECT_STREQ(LH "/a/b/e", TestResolve(base, "a/b/c/d/../../e"));
EXPECT_STREQ(LH "/a/b/e", TestResolve(base, "../a/b/c/d/../../e"));
EXPECT_STREQ(LH "/a/b/e", TestResolve(base, "./a/b/c/d/../../e"));
EXPECT_STREQ(LH "/a/b/e", TestResolve(base, "../a/b/./c/d/../../e"));
EXPECT_STREQ(LH "/a/b/e", TestResolve(base, "./a/b/./c/d/../../e"));
EXPECT_STREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/."));
EXPECT_STREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/./."));
EXPECT_STREQ(LH "/a/b/e/", TestResolve(base, "./a/b/./c/d/../../e/././."));
#undef LH
}
TEST_CASE(ResolveUri_WindowsPaths_Forwardslash_NoScheme) {
EXPECT_STREQ(
"c:/Users/USERNA~1/AppData/Local/Temp/a/b.dll",
TestResolve("C:/Users/USERNA~1/AppData/Local/Temp/a/out.dill", "b.dll"));
}
// > Here are some examples which may be accepted by some applications on
// > Windows systems
// https://en.wikipedia.org/wiki/File_URI_scheme
// "file:///C:/"
TEST_CASE(ResolveUri_WindowsPaths_Forwardslash_FileScheme) {
EXPECT_STREQ(
"file:///"
"C:/Users/USERNA~1/AppData/Local/Temp/a/b.dll",
TestResolve("file:///C:/Users/USERNA~1/AppData/Local/Temp/a/out.dill",
"b.dll"));
}
TEST_CASE(ResolveUri_WindowsPaths_Backslash) {
EXPECT_STREQ(
"file:///b.dll",
TestResolve(
"file:///C:\\Users\\USERNA~1\\AppData\\Local\\Temp\\a\\out.dill",
"b.dll"));
}
} // namespace dart

View file

@ -362,8 +362,6 @@ vm_sources = [
"unwinding_records.cc",
"unwinding_records.h",
"unwinding_records_win.cc",
"uri.cc",
"uri.h",
"v8_snapshot_writer.cc",
"v8_snapshot_writer.h",
"virtual_memory.cc",
@ -470,7 +468,6 @@ vm_sources_tests = [
"unicode_test.cc",
"unit_test.cc",
"unit_test.h",
"uri_test.cc",
"utils_test.cc",
"virtual_memory_test.cc",
"zone_test.cc",