blob: a1c09205287f33ae2f63355d4b9fb74ce67ddb19 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <decaf/net/URLStreamHandler.h>
#include <decaf/lang/exceptions/UnsupportedOperationException.h>
#include <decaf/lang/exceptions/SecurityException.h>
#include <decaf/lang/exceptions/StringIndexOutOfBoundsException.h>
#include <decaf/util/HashCode.h>
#include <decaf/internal/util/StringUtils.h>
#include <decaf/net/UnknownHostException.h>
#include <decaf/lang/Integer.h>
#include <decaf/internal/net/URLUtils.h>
using namespace decaf;
using namespace decaf::net;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::internal;
using namespace decaf::internal::util;
using namespace decaf::internal::net;
////////////////////////////////////////////////////////////////////////////////
URLStreamHandler::~URLStreamHandler() {}
////////////////////////////////////////////////////////////////////////////////
namespace {
String relativePath(const String& base, const String& path) {
if (path.startsWith("/")) {
return URLUtils::canonicalizePath(path, true);
} else if (base != "") {
String combined = base.substring(0, base.lastIndexOf('/') + 1) + path;
return URLUtils::canonicalizePath(combined, true);
} else {
return path;
}
}
}
////////////////////////////////////////////////////////////////////////////////
URLConnection* URLStreamHandler::openConnection(const URL& url DECAF_UNUSED, const Proxy* proxy DECAF_UNUSED) {
throw UnsupportedOperationException(__FILE__, __LINE__, "method has not been implemented yet");
}
////////////////////////////////////////////////////////////////////////////////
bool URLStreamHandler::equals(const URL& source, const URL& other) const {
if (!sameFile(source, other)) {
return false;
}
String s1 = source.getRef();
String s2 = other.getRef();
if (s1 != s2) {
return false;
}
s1 = source.getQuery();
s2 = other.getQuery();
return s1 == s2;
}
////////////////////////////////////////////////////////////////////////////////
int URLStreamHandler::hashCode(const URL& url) const {
return hashCode(toExternalForm(url));
}
////////////////////////////////////////////////////////////////////////////////
bool URLStreamHandler::sameFile(const URL& source, const URL& other) const {
String s1 = source.getProtocol();
String s2 = other.getProtocol();
if (s1 != s2) {
return false;
}
s1 = source.getFile();
s2 = other.getFile();
if (s1 != s2) {
return false;
}
if (!hostsEqual(source, other)) {
return false;
}
int p1 = source.getPort();
if (p1 == -1) {
p1 = getDefaultPort();
}
int p2 = other.getPort();
if (p2 == -1) {
p2 = getDefaultPort();
}
return p1 == p2;
}
////////////////////////////////////////////////////////////////////////////////
bool URLStreamHandler::hostsEqual(const URL& source, const URL& other) const {
// TODO need a NULL address.
// // Compare by addresses if known.
// InetAddress address1 = getHostAddress(source);
// InetAddress address2 = getHostAddress(other);
// if (address1 != null && address2 != null) {
// return address1.equals(address2);
// }
// Compare by name.
String host1 = URLUtils::getHost(source);
String host2 = URLUtils::getHost(other);
if (host1.isEmpty() && host2.isEmpty()) {
return true;
}
return host1.compareToIgnoreCase(host2) == 0;
}
////////////////////////////////////////////////////////////////////////////////
String URLStreamHandler::toExternalForm(const URL& url) const {
std::string answer;
answer.append(url.getProtocol().toString());
answer.append(":");
std::string authority = url.getAuthority().toString();
if (!authority.empty()) {
answer.append("//");
answer.append(url.getAuthority().toString());
}
String file = url.getFile();
String ref = url.getRef();
if (!file.isEmpty()) {
answer.append(file.toString());
}
if (!ref.isEmpty()) {
answer.append("#");
answer.append(ref.toString());
}
return String(answer);
}
////////////////////////////////////////////////////////////////////////////////
InetAddress URLStreamHandler::getHostAddress(const URL& url) const {
// TODO
try {
String host = url.getHost();
if (host.isEmpty()) {
return InetAddress::getLocalHost(); // null
}
//return InetAddress::getByName(host);
return InetAddress::getLocalHost(); // null
} catch (UnknownHostException& e) {
return InetAddress::getLocalHost(); // null
}
}
////////////////////////////////////////////////////////////////////////////////
int URLStreamHandler::getDefaultPort() const {
return -1;
}
////////////////////////////////////////////////////////////////////////////////
void URLStreamHandler::parseURL(URL& url, const String& spec, int start, int limit) {
if (limit < start || limit < 0) {
if ((limit <= Integer::MIN_VALUE + 1 && (start >= spec.length() || start < 0)) ||
(spec.startsWith("//", start) && spec.indexOf('/', start + 2) == -1)) {
throw StringIndexOutOfBoundsException(__FILE__, __LINE__, limit);
}
if (this != url.getURLStreamHandler()) {
throw SecurityException(__FILE__, __LINE__, "Only the URL's stream handler can modify");
}
return;
}
int fileStart;
String authority;
String userInfo;
String host;
int port = -1;
String path;
String query;
String ref;
if (spec.regionMatches(start, "//", 0, 2)) {
// Parse the authority from the spec.
int authorityStart = start + 2;
fileStart = URLUtils::findFirstOf(spec, "/?#", authorityStart, limit);
authority = spec.substring(authorityStart, fileStart);
int userInfoEnd = URLUtils::findFirstOf(spec, "@", authorityStart, fileStart);
int hostStart;
if (userInfoEnd != fileStart) {
userInfo = spec.substring(authorityStart, userInfoEnd);
hostStart = userInfoEnd + 1;
} else {
userInfo = "";
hostStart = authorityStart;
}
/*
* Extract the host and port. The host may be an IPv6 address with
* colons like "[::1]", in which case we look for the port delimiter
* colon after the ']' character.
*/
int colonSearchFrom = hostStart;
int ipv6End = URLUtils::findFirstOf(spec, "]", hostStart, fileStart);
if (ipv6End != fileStart) {
if (URLUtils::findFirstOf(spec, ":", hostStart, ipv6End) == ipv6End) {
throw IllegalArgumentException(__FILE__, __LINE__,
(std::string("Expected an IPv6 address: ") +
spec.substring(hostStart, ipv6End + 1).toString()).c_str());
}
colonSearchFrom = ipv6End;
}
int hostEnd = URLUtils::findFirstOf(spec, ":", colonSearchFrom, fileStart);
host = spec.substring(hostStart, hostEnd);
int portStart = hostEnd + 1;
if (portStart < fileStart) {
port = Integer::parseInt(spec.substring(portStart, fileStart).toString());
if (port < 0) {
throw IllegalArgumentException(__FILE__, __LINE__, "port < 0: %d", port);
}
}
path = "";
query = "";
ref = "";
} else {
// Get the authority from the context URL.
fileStart = start;
authority = url.getAuthority();
userInfo = url.getUserInfo();
host = url.getHost();
port = url.getPort();
path = url.getPath();
query = url.getQuery();
ref = url.getRef();
}
/*
* Extract the path, query and fragment. Each part has its own leading
* delimiter character. The query can contain slashes and the fragment
* can contain slashes and question marks.
* / path ? query # fragment
*/
int pos = fileStart;
while (pos < limit) {
int nextPos;
switch (spec.charAt(pos)) {
case '#':
nextPos = limit;
ref = spec.substring(pos + 1, nextPos);
break;
case '?':
nextPos = URLUtils::findFirstOf(spec, "#", pos, limit);
query = spec.substring(pos + 1, nextPos);
ref = "";
break;
default:
nextPos = URLUtils::findFirstOf(spec, "?#", pos, limit);
path = relativePath(path, spec.substring(pos, nextPos));
query = "";
ref = "";
break;
}
pos = nextPos;
}
path = URLUtils::authoritySafePath(authority, path);
setURL(url, url.getProtocol(), host, port, authority, userInfo, path, query, ref);
}
////////////////////////////////////////////////////////////////////////////////
void URLStreamHandler::setURL(URL& url, const String& protocol, const String& host, int port,
const String& authority, const String& userInfo,
const String& path, const String& query, const String& ref) {
if (this != url.getURLStreamHandler()) {
throw SecurityException(__FILE__, __LINE__, "Stream handler is not the URLs intance.");
}
url.set(protocol, host, port, authority, userInfo, path, query, ref);
}