Uri resolveUri(Uri reference)

Resolve reference as an URI relative to this.

Returns the resolved URI.

The algorithm "Transform Reference" for resolving a reference is described in RFC-3986 Section 5.

Updated to handle the case where the base URI is just a relative path - that is: when it has no scheme or authority and the path does not start with a slash. In that case, the paths are combined without removing leading "..", and an empty path is not converted to "/".

Source

Uri resolveUri(Uri reference) {
  // From RFC 3986.
  String targetScheme;
  String targetUserInfo = "";
  String targetHost;
  int targetPort;
  String targetPath;
  String targetQuery;
  if (reference.scheme.isNotEmpty) {
    targetScheme = reference.scheme;
    if (reference.hasAuthority) {
      targetUserInfo = reference.userInfo;
      targetHost = reference.host;
      targetPort = reference.hasPort ? reference.port : null;
    }
    targetPath = _removeDotSegments(reference.path);
    if (reference.hasQuery) {
      targetQuery = reference.query;
    }
  } else {
    targetScheme = this.scheme;
    if (reference.hasAuthority) {
      targetUserInfo = reference.userInfo;
      targetHost = reference.host;
      targetPort = _makePort(reference.hasPort ? reference.port : null,
                             targetScheme);
      targetPath = _removeDotSegments(reference.path);
      if (reference.hasQuery) targetQuery = reference.query;
    } else {
      targetUserInfo = this._userInfo;
      targetHost = this._host;
      targetPort = this._port;
      if (reference.path == "") {
        targetPath = this._path;
        if (reference.hasQuery) {
          targetQuery = reference.query;
        } else {
          targetQuery = this._query;
        }
      } else {
        if (reference.hasAbsolutePath) {
          targetPath = _removeDotSegments(reference.path);
        } else {
          // This is the RFC 3986 behavior for merging.
          if (this.hasEmptyPath) {
            if (!this.hasScheme && !this.hasAuthority) {
              // Keep the path relative if no scheme or authority.
              targetPath = reference.path;
            } else {
              // Add path normalization on top of RFC algorithm.
              targetPath = _removeDotSegments("/" + reference.path);
            }
          } else {
            var mergedPath = _mergePaths(this._path, reference.path);
            if (this.hasScheme || this.hasAuthority || this.hasAbsolutePath) {
              targetPath = _removeDotSegments(mergedPath);
            } else {
              // Non-RFC 3986 beavior. If both base and reference are relative
              // path, allow the merged path to start with "..".
              // The RFC only specifies the case where the base has a scheme.
              targetPath = _normalizeRelativePath(mergedPath);
            }
          }
        }
        if (reference.hasQuery) targetQuery = reference.query;
      }
    }
  }
  String fragment = reference.hasFragment ? reference.fragment : null;
  return new Uri._internal(targetScheme,
                           targetUserInfo,
                           targetHost,
                           targetPort,
                           targetPath,
                           targetQuery,
                           fragment);
}