From 422cb0462a5cb5dc94f1f4218569033f17657efd Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 30 Dec 2017 18:20:30 +0300 Subject: [PATCH] Add java.net.URL implementation --- .../classlib/java/net/THttpURLConnection.java | 179 ++++ .../java/net/TMalformedURLException.java | 29 + .../classlib/java/net/TProtocolException.java | 29 + .../org/teavm/classlib/java/net/TURI.java | 7 + .../org/teavm/classlib/java/net/TURL.java | 381 ++++++++ .../classlib/java/net/TURLConnection.java | 301 ++++++ .../classlib/java/net/TURLStreamHandler.java | 285 ++++++ .../java/net/TURLStreamHandlerFactory.java | 21 + .../java/net/TUnknownServiceException.java | 29 + .../java/net/impl/TXHRStreamHandler.java | 28 + .../java/net/impl/TXHRURLConnection.java | 215 +++++ .../org/teavm/jso/ajax/XMLHttpRequest.java | 2 + .../org/teavm/classlib/java/net/URLTest.java | 876 ++++++++++++++++++ 13 files changed, 2382 insertions(+) create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/THttpURLConnection.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TMalformedURLException.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TProtocolException.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TURL.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TURLConnection.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandler.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandlerFactory.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/TUnknownServiceException.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRStreamHandler.java create mode 100644 classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRURLConnection.java create mode 100644 tests/src/test/java/org/teavm/classlib/java/net/URLTest.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/THttpURLConnection.java b/classlib/src/main/java/org/teavm/classlib/java/net/THttpURLConnection.java new file mode 100644 index 000000000..8d9051e0c --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/THttpURLConnection.java @@ -0,0 +1,179 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; +import java.io.InputStream; + +public abstract class THttpURLConnection extends TURLConnection { + private String[] methodTokens = { "GET", "DELETE", "HEAD", "OPTIONS", "POST", "PUT", "TRACE" }; + protected String method = "GET"; + protected int responseCode = -1; + protected String responseMessage; + protected boolean instanceFollowRedirects = followRedirects; + private static boolean followRedirects = true; + protected int chunkLength = -1; + protected int fixedContentLength = -1; + private final static int DEFAULT_CHUNK_LENGTH = 1024; + + public final static int HTTP_ACCEPTED = 202; + public final static int HTTP_BAD_GATEWAY = 502; + public final static int HTTP_BAD_METHOD = 405; + public final static int HTTP_BAD_REQUEST = 400; + public final static int HTTP_CLIENT_TIMEOUT = 408; + public final static int HTTP_CONFLICT = 409; + public final static int HTTP_CREATED = 201; + public final static int HTTP_ENTITY_TOO_LARGE = 413; + public final static int HTTP_FORBIDDEN = 403; + public final static int HTTP_GATEWAY_TIMEOUT = 504; + public final static int HTTP_GONE = 410; + public final static int HTTP_INTERNAL_ERROR = 500; + public final static int HTTP_LENGTH_REQUIRED = 411; + public final static int HTTP_MOVED_PERM = 301; + public final static int HTTP_MOVED_TEMP = 302; + public final static int HTTP_MULT_CHOICE = 300; + public final static int HTTP_NO_CONTENT = 204; + public final static int HTTP_NOT_ACCEPTABLE = 406; + public final static int HTTP_NOT_AUTHORITATIVE = 203; + public final static int HTTP_NOT_FOUND = 404; + public final static int HTTP_NOT_IMPLEMENTED = 501; + public final static int HTTP_NOT_MODIFIED = 304; + public final static int HTTP_OK = 200; + public final static int HTTP_PARTIAL = 206; + public final static int HTTP_PAYMENT_REQUIRED = 402; + public final static int HTTP_PRECON_FAILED = 412; + public final static int HTTP_PROXY_AUTH = 407; + public final static int HTTP_REQ_TOO_LONG = 414; + public final static int HTTP_RESET = 205; + public final static int HTTP_SEE_OTHER = 303; + @Deprecated + public final static int HTTP_SERVER_ERROR = 500; + public final static int HTTP_USE_PROXY = 305; + public final static int HTTP_UNAUTHORIZED = 401; + public final static int HTTP_UNSUPPORTED_TYPE = 415; + public final static int HTTP_UNAVAILABLE = 503; + public final static int HTTP_VERSION = 505; + + protected THttpURLConnection(TURL url) { + super(url); + } + + public abstract void disconnect(); + + public InputStream getErrorStream() { + return null; + } + + public static boolean getFollowRedirects() { + return followRedirects; + } + + public String getRequestMethod() { + return method; + } + + public int getResponseCode() throws IOException { + // Call getInputStream() first since getHeaderField() doesn't return + // exceptions + getInputStream(); + String response = getHeaderField(0); + if (response == null) { + return -1; + } + response = response.trim(); + int mark = response.indexOf(" ") + 1; + if (mark == 0) { + return -1; + } + int last = mark + 3; + if (last > response.length()) { + last = response.length(); + } + responseCode = Integer.parseInt(response.substring(mark, last)); + if (last + 1 <= response.length()) { + responseMessage = response.substring(last + 1); + } + return responseCode; + } + + public String getResponseMessage() throws IOException { + if (responseMessage != null) { + return responseMessage; + } + getResponseCode(); + return responseMessage; + } + + public static void setFollowRedirects(boolean auto) { + followRedirects = auto; + } + + public void setRequestMethod(String method) throws TProtocolException { + if (connected) { + throw new TProtocolException(); + } + for (int i = 0; i < methodTokens.length; i++) { + if (methodTokens[i].equals(method)) { + // if there is a supported method that matches the desired + // method, then set the current method and return + this.method = methodTokens[i]; + return; + } + } + throw new TProtocolException(); + } + + public boolean getInstanceFollowRedirects() { + return instanceFollowRedirects; + } + + public void setInstanceFollowRedirects(boolean followRedirects) { + instanceFollowRedirects = followRedirects; + } + + @Override + public long getHeaderFieldDate(String field, long defaultValue) { + return super.getHeaderFieldDate(field, defaultValue); + } + + public void setFixedLengthStreamingMode(int contentLength) { + if (super.connected) { + throw new IllegalStateException(); + } + if (0 < chunkLength) { + throw new IllegalStateException(); + } + if (0 > contentLength) { + throw new IllegalArgumentException(); + } + this.fixedContentLength = contentLength; + } + + public void setChunkedStreamingMode(int chunklen) { + if (super.connected) { + throw new IllegalStateException(); + } + if (0 <= fixedContentLength) { + throw new IllegalStateException(); + } + if (0 >= chunklen) { + chunkLength = DEFAULT_CHUNK_LENGTH; + } else { + chunkLength = chunklen; + } + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TMalformedURLException.java b/classlib/src/main/java/org/teavm/classlib/java/net/TMalformedURLException.java new file mode 100644 index 000000000..c9e52860c --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TMalformedURLException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; + +public class TMalformedURLException extends IOException { + public TMalformedURLException() { + super(); + } + + public TMalformedURLException(String detailMessage) { + super(detailMessage); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TProtocolException.java b/classlib/src/main/java/org/teavm/classlib/java/net/TProtocolException.java new file mode 100644 index 000000000..315030539 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TProtocolException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; + +public class TProtocolException extends IOException { + public TProtocolException() { + super(); + } + + public TProtocolException(String detailMessage) { + super(detailMessage); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TURI.java b/classlib/src/main/java/org/teavm/classlib/java/net/TURI.java index e15afbfc3..e86ab2dde 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/net/TURI.java +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TURI.java @@ -1664,4 +1664,11 @@ public final class TURI implements TComparable, TSerializable { return convertHexToLowerCase(TString.wrap(result.toString())); } + + public TURL toURL() throws TMalformedURLException { + if (!absolute) { + throw new IllegalArgumentException(); + } + return new TURL(toString()); + } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TURL.java b/classlib/src/main/java/org/teavm/classlib/java/net/TURL.java new file mode 100644 index 000000000..e8f988956 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TURL.java @@ -0,0 +1,381 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.teavm.classlib.java.lang.TString; +import org.teavm.classlib.java.net.impl.TXHRStreamHandler; + +public final class TURL implements Serializable { + private int hashCode; + private String file; + private String protocol; + private String host; + private int port = -1; + private String authority; + private transient String userInfo; + private transient String path; + private transient String query; + private String ref; + private static Map streamHandlers = new HashMap<>(); + transient TURLStreamHandler strmHandler; + private static TURLStreamHandlerFactory streamHandlerFactory; + + public static void setURLStreamHandlerFactory(TURLStreamHandlerFactory streamFactory) { + Objects.requireNonNull(streamFactory); + streamHandlers.clear(); + streamHandlerFactory = streamFactory; + } + + public TURL(String spec) throws TMalformedURLException { + this(null, spec, (TURLStreamHandler) null); + } + + public TURL(TURL context, String spec) throws TMalformedURLException { + this(context, spec, null); + } + + public TURL(TURL context, String spec, TURLStreamHandler handler) throws TMalformedURLException { + strmHandler = handler; + + if (spec == null) { + throw new TMalformedURLException(); + } + spec = spec.trim(); + + // The spec includes a protocol if it includes a colon character + // before the first occurrence of a slash character. Note that, + // "protocol" is the field which holds this URLs protocol. + int index; + try { + index = spec.indexOf(':'); + } catch (NullPointerException e) { + throw new TMalformedURLException(e.toString()); + } + int startIPv6Addr = spec.indexOf('['); + if (index >= 0) { + if (startIPv6Addr == -1 || index < startIPv6Addr) { + protocol = spec.substring(0, index); + // According to RFC 2396 scheme part should match + // the following expression: + // alpha *( alpha | digit | "+" | "-" | "." ) + char c = protocol.charAt(0); + boolean valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + for (int i = 1; valid && (i < protocol.length()); i++) { + c = protocol.charAt(i); + valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') + || c == '+' || c == '-' || c == '.'; + } + if (!valid) { + protocol = null; + index = -1; + } else { + protocol = protocol.toLowerCase(); + } + } + } + + if (protocol != null) { + // If the context was specified, and it had the same protocol + // as the spec, then fill in the receiver's slots from the values + // in the context but still allow them to be over-ridden later + // by the values in the spec. + if (context != null && protocol.equals(context.getProtocol())) { + String cPath = context.getPath(); + if (cPath != null && cPath.startsWith("/")) { + set(protocol, context.getHost(), context.getPort(), context.getAuthority(), context.getUserInfo(), + cPath, context.getQuery(), null); + } + if (strmHandler == null) { + strmHandler = context.strmHandler; + } + } + } else { + // If the spec did not include a protocol, then the context + // *must* be specified. Fill in the receiver's slots from the + // values in the context, but still allow them to be over-ridden + // by the values in the ("relative") spec. + if (context == null) { + throw new TMalformedURLException(); + } + set(context.getProtocol(), context.getHost(), context.getPort(), context.getAuthority(), + context.getUserInfo(), context.getPath(), context.getQuery(), null); + if (strmHandler == null) { + strmHandler = context.strmHandler; + } + } + + // If the stream handler has not been determined, set it + // to the default for the specified protocol. + if (strmHandler == null) { + setupStreamHandler(); + if (strmHandler == null) { + throw new TMalformedURLException(); + } + } + + // Let the handler parse the TURL. If the handler throws + // any exception, throw MalformedURLException instead. + // + // Note: We want "index" to be the index of the start of the scheme + // specific part of the TURL. At this point, it will be either + // -1 or the index of the colon after the protocol, so we + // increment it to point at either character 0 or the character + // after the colon. + try { + strmHandler.parseURL(this, spec, ++index, spec.length()); + } catch (Exception e) { + throw new TMalformedURLException(e.toString()); + } + + if (port < -1) { + throw new TMalformedURLException(); + } + } + + public TURL(String protocol, String host, String file) throws TMalformedURLException { + this(protocol, host, -1, file, null); + } + + public TURL(String protocol, String host, int port, String file) throws TMalformedURLException { + this(protocol, host, port, file, null); + } + + public TURL(String protocol, String host, int port, String file, TURLStreamHandler handler) + throws TMalformedURLException { + if (port < -1) { + throw new TMalformedURLException(); + } + + if (host != null && host.contains(":") && host.charAt(0) != '[') { + host = "[" + host + "]"; + } + + Objects.requireNonNull(protocol); + + this.protocol = protocol; + this.host = host; + this.port = port; + + // Set the fields from the arguments. Handle the case where the + // passed in "file" includes both a file and a reference part. + int index = -1; + index = file.indexOf("#", file.lastIndexOf("/")); //$NON-NLS-1$ //$NON-NLS-2$ + if (index >= 0) { + this.file = file.substring(0, index); + ref = file.substring(index + 1); + } else { + this.file = file; + } + fixURL(false); + + // Set the stream handler for the TURL either to the handler + // argument if it was specified, or to the default for the + // receiver's protocol if the handler was null. + if (handler == null) { + setupStreamHandler(); + if (strmHandler == null) { + throw new TMalformedURLException(); + } + } else { + strmHandler = handler; + } + } + + void fixURL(boolean fixHost) { + int index; + if (host != null && host.length() > 0) { + authority = host; + if (port != -1) { + authority = authority + ":" + port; + } + } + if (fixHost) { + index = -1; + if (host != null) { + index = host.lastIndexOf('@'); + } + if (index >= 0) { + userInfo = host.substring(0, index); + host = host.substring(index + 1); + } else { + userInfo = null; + } + } + + index = -1; + if (file != null) { + index = file.indexOf('?'); + } + if (index >= 0) { + query = file.substring(index + 1); + path = file.substring(0, index); + } else { + query = null; + path = file; + } + } + + protected void set(String protocol, String host, int port, String file, String ref) { + if (this.protocol == null) { + this.protocol = protocol; + } + this.host = host; + this.file = file; + this.port = port; + this.ref = ref; + hashCode = 0; + fixURL(true); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (this == o) { + return true; + } + if (this.getClass() != o.getClass()) { + return false; + } + return strmHandler.equals(this, (TURL) o); + } + + public boolean sameFile(TURL otherURL) { + return strmHandler.sameFile(this, otherURL); + } + + @Override + public int hashCode() { + if (hashCode == 0) { + hashCode = strmHandler.hashCode(this); + } + return hashCode; + } + + void setupStreamHandler() { + // Check for a cached (previously looked up) handler for + // the requested protocol. + strmHandler = streamHandlers.get(protocol); + if (strmHandler != null) { + return; + } + + // If there is a stream handler factory, then attempt to + // use it to create the handler. + if (streamHandlerFactory != null) { + strmHandler = streamHandlerFactory.createURLStreamHandler(protocol); + if (strmHandler != null) { + streamHandlers.put(protocol, strmHandler); + return; + } + } + + switch (protocol) { + case "http": + case "https": + strmHandler = new TXHRStreamHandler(); + break; + default: + break; + } + + } + + public InputStream openStream() throws IOException { + return openConnection().getInputStream(); + } + + public TURLConnection openConnection() throws IOException { + return strmHandler.openConnection(this); + } + + public TURI toURI() throws TURISyntaxException { + return new TURI((TString) (Object) toExternalForm()); + } + + @Override + public String toString() { + return toExternalForm(); + } + + public String toExternalForm() { + if (strmHandler == null) { + return "unknown protocol(" + protocol + ")://" + host + file; + } + return strmHandler.toExternalForm(this); + } + + public String getFile() { + return file; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getProtocol() { + return protocol; + } + + public String getRef() { + return ref; + } + + public String getQuery() { + return query; + } + + public String getPath() { + return path; + } + + public String getUserInfo() { + return userInfo; + } + + public String getAuthority() { + return authority; + } + + protected void set(String protocol, String host, int port, String authority, String userInfo, String path, + String query, String ref) { + String filePart = path; + if (query != null && !query.isEmpty()) { + filePart = filePart != null ? filePart + "?" + query : "?" + query; + } + set(protocol, host, port, filePart, ref); + this.authority = authority; + this.userInfo = userInfo; + this.path = path; + this.query = query; + } + + public int getDefaultPort() { + return strmHandler.getDefaultPort(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TURLConnection.java b/classlib/src/main/java/org/teavm/classlib/java/net/TURLConnection.java new file mode 100644 index 000000000..e740fa255 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TURLConnection.java @@ -0,0 +1,301 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class TURLConnection { + protected TURL url; + private static boolean defaultAllowUserInteraction; + private static boolean defaultUseCaches = true; + private long lastModified = -1; + protected long ifModifiedSince; + protected boolean useCaches = defaultUseCaches; + protected boolean connected; + protected boolean doOutput; + protected boolean doInput = true; + protected boolean allowUserInteraction = defaultAllowUserInteraction; + private int readTimeout; + private int connectTimeout; + private Map> requestProperties = new HashMap<>(); + + protected TURLConnection(TURL url) { + this.url = url; + } + + public abstract void connect() throws IOException; + + public boolean getAllowUserInteraction() { + return allowUserInteraction; + } + + public String getContentEncoding() { + return getHeaderField("Content-Encoding"); + } + + public int getContentLength() { + return getHeaderFieldInt("Content-Length", -1); + } + + public String getContentType() { + return getHeaderField("Content-Type"); + } + + public long getDate() { + return getHeaderFieldDate("Date", 0); + } + + public static boolean getDefaultAllowUserInteraction() { + return defaultAllowUserInteraction; + } + + @Deprecated + public static String getDefaultRequestProperty(String field) { + return null; + } + + public boolean getDefaultUseCaches() { + return defaultUseCaches; + } + + public boolean getDoInput() { + return doInput; + } + + public boolean getDoOutput() { + return doOutput; + } + + public long getExpiration() { + return getHeaderFieldDate("Expires", 0); + } + + public String getHeaderField(int pos) { + return null; + } + + public Map> getHeaderFields() { + return Collections.emptyMap(); + } + + public Map> getRequestProperties() { + if (connected) { + throw new IllegalStateException(); + } + + HashMap> map = new HashMap<>(); + for (String key : requestProperties.keySet()) { + map.put(key, Collections.unmodifiableList(requestProperties.get(key))); + } + return Collections.unmodifiableMap(map); + } + + public void addRequestProperty(String field, String newValue) { + if (connected) { + throw new IllegalStateException(); + } + if (field == null) { + throw new NullPointerException(); + } + + List valuesList = requestProperties.get(field); + if (valuesList == null) { + valuesList = new ArrayList(); + valuesList.add(0, newValue); + requestProperties.put(field, valuesList); + } else { + valuesList.add(0, newValue); + } + } + + public String getHeaderField(String key) { + return null; + } + + @SuppressWarnings("deprecation") + public long getHeaderFieldDate(String field, long defaultValue) { + String date = getHeaderField(field); + if (date == null) { + return defaultValue; + } + try { + return Date.parse(date); + } catch (Exception e) { + return defaultValue; + } + } + + public int getHeaderFieldInt(String field, int defaultValue) { + try { + return Integer.parseInt(getHeaderField(field)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public String getHeaderFieldKey(int posn) { + return null; + } + + public long getIfModifiedSince() { + return ifModifiedSince; + } + + public InputStream getInputStream() throws IOException { + throw new TUnknownServiceException(); + } + + public long getLastModified() { + if (lastModified == -1) { + lastModified = getHeaderFieldDate("Last-Modified", 0); + } + return lastModified; + } + + public OutputStream getOutputStream() throws IOException { + throw new TUnknownServiceException(); + } + + public String getRequestProperty(String field) { + if (connected) { + throw new IllegalStateException(); + } + List valuesList = requestProperties.get(field); + if (valuesList == null) { + return null; + } + return valuesList.get(0); + } + + public TURL getURL() { + return url; + } + + public boolean getUseCaches() { + return useCaches; + } + + private String parseTypeString(String typeString) { + StringBuilder typeStringBuffer = new StringBuilder(typeString); + for (int i = 0; i < typeStringBuffer.length(); i++) { + // if non-alphanumeric, replace it with '_' + char c = typeStringBuffer.charAt(i); + if (!(Character.isLetter(c) || Character.isDigit(c) || c == '.')) { + typeStringBuffer.setCharAt(i, '_'); + } + } + return typeStringBuffer.toString(); + } + + public void setAllowUserInteraction(boolean newValue) { + if (connected) { + throw new IllegalStateException(); + } + this.allowUserInteraction = newValue; + } + + public static void setDefaultAllowUserInteraction(boolean allows) { + defaultAllowUserInteraction = allows; + } + + @Deprecated + public static void setDefaultRequestProperty(String field, String value) { + } + + public void setDefaultUseCaches(boolean newValue) { + if (connected) { + throw new IllegalAccessError(); + } + defaultUseCaches = newValue; + } + + public void setDoInput(boolean newValue) { + if (connected) { + throw new IllegalStateException(); + } + this.doInput = newValue; + } + + public void setDoOutput(boolean newValue) { + if (connected) { + throw new IllegalStateException(); + } + this.doOutput = newValue; + } + + public void setIfModifiedSince(long newValue) { + if (connected) { + throw new IllegalStateException(); + } + this.ifModifiedSince = newValue; + } + + public void setRequestProperty(String field, String newValue) { + if (connected) { + throw new IllegalStateException(); + } + if (field == null) { + throw new NullPointerException(); + } + + List valuesList = new ArrayList(); + valuesList.add(newValue); + requestProperties.put(field, valuesList); + } + + public void setUseCaches(boolean newValue) { + if (connected) { + throw new IllegalStateException(); + } + this.useCaches = newValue; + } + + public void setConnectTimeout(int timeout) { + if (0 > timeout) { + throw new IllegalArgumentException(); + } + this.connectTimeout = timeout; + } + + public int getConnectTimeout() { + return connectTimeout; + } + + public void setReadTimeout(int timeout) { + if (0 > timeout) { + throw new IllegalArgumentException(); + } + this.readTimeout = timeout; + } + + public int getReadTimeout() { + return readTimeout; + } + + @Override + public String toString() { + return getClass().getName() + ":" + url.toString(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandler.java b/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandler.java new file mode 100644 index 000000000..fffccada2 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandler.java @@ -0,0 +1,285 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; +import java.util.Objects; + +public abstract class TURLStreamHandler { + protected abstract TURLConnection openConnection(TURL u) throws IOException; + + protected void parseURL(TURL u, String str, int start, int end) { + if (end < start || end < 0) { + // Checks to ensure string index exception ahead of + // security exception for compatibility. + if (end <= Integer.MIN_VALUE + 1 && (start >= str.length() || start < 0) + || str.startsWith("//", start) && str.indexOf('/', start + 2) == -1) { + throw new StringIndexOutOfBoundsException(end); + } + return; + } + String parseString = str.substring(start, end); + end -= start; + int fileIdx = 0; + + // Default is to use info from context + String host = u.getHost(); + int port = u.getPort(); + String ref = u.getRef(); + String file = u.getPath(); + String query = u.getQuery(); + String authority = u.getAuthority(); + String userInfo = u.getUserInfo(); + + int refIdx = parseString.indexOf('#', 0); + if (parseString.startsWith("//") && !parseString.startsWith("////")) { + int hostIdx = 2; + int portIdx; + port = -1; + fileIdx = parseString.indexOf('/', hostIdx); + int questionMarkIndex = parseString.indexOf('?', hostIdx); + if ((questionMarkIndex != -1) + && ((fileIdx == -1) || (fileIdx > questionMarkIndex))) { + fileIdx = questionMarkIndex; + } + if (fileIdx == -1) { + fileIdx = end; + // Use default + file = ""; + } + int hostEnd = fileIdx; + if (refIdx != -1 && refIdx < fileIdx) { + hostEnd = refIdx; + } + int userIdx = parseString.lastIndexOf('@', hostEnd); + authority = parseString.substring(hostIdx, hostEnd); + if (userIdx > -1) { + userInfo = parseString.substring(hostIdx, userIdx); + hostIdx = userIdx + 1; + } + + portIdx = parseString.indexOf(':', userIdx == -1 ? hostIdx + : userIdx); + int endOfIPv6Addr = parseString.indexOf(']'); + // if there are square braces, ie. IPv6 address, use last ':' + if (endOfIPv6Addr != -1) { + try { + if (parseString.length() > endOfIPv6Addr + 1) { + char c = parseString.charAt(endOfIPv6Addr + 1); + if (c == ':') { + portIdx = endOfIPv6Addr + 1; + } else { + portIdx = -1; + } + } else { + portIdx = -1; + } + } catch (Exception e) { + // Ignored + } + } + + if (portIdx == -1 || portIdx > fileIdx) { + host = parseString.substring(hostIdx, hostEnd); + } else { + host = parseString.substring(hostIdx, portIdx); + String portString = parseString.substring(portIdx + 1, hostEnd); + if (portString.length() == 0) { + port = -1; + } else { + port = Integer.parseInt(portString); + } + } + } + + if (refIdx > -1) { + ref = parseString.substring(refIdx + 1, end); + } + int fileEnd = refIdx == -1 ? end : refIdx; + + int queryIdx = parseString.lastIndexOf('?', fileEnd); + boolean canonicalize = false; + if (queryIdx > -1) { + query = parseString.substring(queryIdx + 1, fileEnd); + if (queryIdx == 0 && file != null) { + if (file.equals("")) { + file = "/"; + } else if (file.startsWith("/")) { + canonicalize = true; + } + int last = file.lastIndexOf('/') + 1; + file = file.substring(0, last); + } + fileEnd = queryIdx; + } else + // Don't inherit query unless only the ref is changed + if (refIdx != 0) { + query = null; + } + + if (fileIdx > -1) { + if (fileIdx < end && parseString.charAt(fileIdx) == '/') { + file = parseString.substring(fileIdx, fileEnd); + } else if (fileEnd > fileIdx) { + if (file == null) { + file = ""; + } else if (file.equals("")) { + file = "/"; + } else if (file.startsWith("/")) { + canonicalize = true; + } + int last = file.lastIndexOf('/') + 1; + if (last == 0) { + file = parseString.substring(fileIdx, fileEnd); + } else { + file = file.substring(0, last) + parseString.substring(fileIdx, fileEnd); + } + } + } + if (file == null) { + file = ""; + } + + if (host == null) { + host = ""; + } + + if (canonicalize) { + // modify file if there's any relative referencing + file = canonicalizePath(file); + } + + setURL(u, u.getProtocol(), host, port, authority, userInfo, file, query, ref); + } + + private static String canonicalizePath(String path) { + int dirIndex; + + while (true) { + dirIndex = path.indexOf("/./"); + if (dirIndex < 0) { + break; + } + path = path.substring(0, dirIndex + 1) + path.substring(dirIndex + 3); + } + + if (path.endsWith("/.")) { + path = path.substring(0, path.length() - 1); + } + + while (true) { + dirIndex = path.indexOf("/../"); + if (dirIndex < 0) { + break; + } + if (dirIndex != 0) { + path = path.substring(0, path.lastIndexOf('/', dirIndex - 1)) + path.substring(dirIndex + 3); + } else { + path = path.substring(dirIndex + 3); + } + } + + if (path.endsWith("/..") && path.length() > 3) { + path = path.substring(0, path.lastIndexOf('/', path.length() - 4) + 1); + } + return path; + } + + @Deprecated + protected void setURL(TURL u, String protocol, String host, int port, String file, String ref) { + u.set(protocol, host, port, file, ref); + } + + protected void setURL(TURL u, String protocol, String host, int port, String authority, String userInfo, + String file, String query, String ref) { + u.set(protocol, host, port, authority, userInfo, file, query, ref); + } + + protected String toExternalForm(TURL url) { + StringBuilder answer = new StringBuilder(); + answer.append(url.getProtocol()); + answer.append(':'); + String authority = url.getAuthority(); + if (authority != null && authority.length() > 0) { + answer.append("//"); + answer.append(url.getAuthority()); + } + + String file = url.getFile(); + String ref = url.getRef(); + if (file != null) { + answer.append(file); + } + if (ref != null) { + answer.append('#'); + answer.append(ref); + } + return answer.toString(); + } + + protected boolean equals(TURL url1, TURL url2) { + if (!sameFile(url1, url2)) { + return false; + } + return Objects.equals(url1.getRef(), url2.getRef()) && Objects.equals(url1.getQuery(), url2.getQuery()); + } + + protected int getDefaultPort() { + return -1; + } + + protected int hashCode(TURL url) { + return toExternalForm(url).hashCode(); + } + + protected boolean hostsEqual(TURL url1, TURL url2) { + // Compare by name. + String host1 = getHost(url1); + String host2 = getHost(url2); + if (host1 == null && host2 == null) { + return true; + } + return host1 != null && host1.equalsIgnoreCase(host2); + } + + protected boolean sameFile(TURL url1, TURL url2) { + if (!Objects.equals(url1.getProtocol(), url2.getProtocol()) + || !Objects.equals(url1.getFile(), url2.getFile())) { + return false; + } + if (!hostsEqual(url1, url2)) { + return false; + } + int p1 = url1.getPort(); + if (p1 == -1) { + p1 = getDefaultPort(); + } + int p2 = url2.getPort(); + if (p2 == -1) { + p2 = getDefaultPort(); + } + return p1 == p2; + } + + private static String getHost(TURL url) { + String host = url.getHost(); + if ("file".equals(url.getProtocol()) && "".equals(host)) { + host = "localhost"; + } + return host; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandlerFactory.java b/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandlerFactory.java new file mode 100644 index 000000000..97d6f336f --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TURLStreamHandlerFactory.java @@ -0,0 +1,21 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +public interface TURLStreamHandlerFactory { + TURLStreamHandler createURLStreamHandler(String protocol); +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/TUnknownServiceException.java b/classlib/src/main/java/org/teavm/classlib/java/net/TUnknownServiceException.java new file mode 100644 index 000000000..1b9e6c61e --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/TUnknownServiceException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import java.io.IOException; + +public class TUnknownServiceException extends IOException { + public TUnknownServiceException() { + super(); + } + + public TUnknownServiceException(String detailMessage) { + super(detailMessage); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRStreamHandler.java b/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRStreamHandler.java new file mode 100644 index 000000000..4be5bd72a --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRStreamHandler.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ +package org.teavm.classlib.java.net.impl; + +import java.io.IOException; +import org.teavm.classlib.java.net.TURL; +import org.teavm.classlib.java.net.TURLConnection; +import org.teavm.classlib.java.net.TURLStreamHandler; + +public class TXHRStreamHandler extends TURLStreamHandler { + @Override + protected TURLConnection openConnection(TURL u) throws IOException { + return new TXHRURLConnection(u); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRURLConnection.java b/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRURLConnection.java new file mode 100644 index 000000000..7e5afb842 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/net/impl/TXHRURLConnection.java @@ -0,0 +1,215 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed 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. + */ +package org.teavm.classlib.java.net.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.teavm.classlib.java.net.THttpURLConnection; +import org.teavm.classlib.java.net.TURL; +import org.teavm.interop.Async; +import org.teavm.jso.ajax.XMLHttpRequest; +import org.teavm.jso.typedarrays.ArrayBuffer; +import org.teavm.jso.typedarrays.Int8Array; +import org.teavm.platform.async.AsyncCallback; + +public class TXHRURLConnection extends THttpURLConnection { + private XMLHttpRequest xhr; + private ByteArrayOutputStream outputStream; + private ByteArrayInputStream inputStream; + private ByteArrayInputStream errorStream; + private Map responseHeaders = new HashMap<>(); + private String[] responseHeaderKeys; + private String[] responseHeaderValues; + private Map> headerFields = new HashMap<>(); + + public TXHRURLConnection(TURL url) { + super(url); + } + + @Override + public void disconnect() { + connected = false; + xhr = null; + responseHeaders = null; + responseHeaderKeys = null; + responseHeaderValues = null; + inputStream = null; + outputStream = null; + errorStream = null; + } + + @Override + public void connect() throws IOException { + if (connected) { + return; + } + + xhr = XMLHttpRequest.create(); + xhr.open(method, url.toString()); + for (Map.Entry> entry : getRequestProperties().entrySet()) { + for (String value : entry.getValue()) { + xhr.setRequestHeader(entry.getKey(), value); + } + } + xhr.setResponseType("arraybuffer"); + try { + performRequest(); + } finally { + connected = true; + } + } + + @Async + private native Boolean performRequest(); + private void performRequest(AsyncCallback callback) { + xhr.setOnReadyStateChange(() -> { + if (xhr.getReadyState() != XMLHttpRequest.DONE) { + return; + } + + responseCode = xhr.getStatus(); + responseMessage = xhr.getStatusText(); + + Int8Array array = Int8Array.create((ArrayBuffer) xhr.getResponse()); + byte[] bytes = new byte[array.getLength()]; + for (int i = 0; i < bytes.length; ++i) { + bytes[i] = array.get(i); + } + ByteArrayInputStream stream = new ByteArrayInputStream(bytes); + + parseHeaders(xhr.getAllResponseHeaders()); + + int responseGroup = responseCode / 100; + if (responseGroup == 4 || responseGroup == 5) { + errorStream = stream; + inputStream = null; + } else { + inputStream = stream; + errorStream = null; + } + callback.complete(Boolean.TRUE); + }); + + if (outputStream != null) { + byte[] bytes = outputStream.toByteArray(); + Int8Array array = Int8Array.create(bytes.length); + for (int i = 0; i < bytes.length; ++i) { + array.set(i, bytes[i]); + } + xhr.send(array.getBuffer()); + } else { + xhr.send(); + } + } + + private void parseHeaders(String headers) { + int index = 0; + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + responseHeaders = new HashMap<>(); + headerFields = new HashMap<>(); + while (index < headers.length()) { + int next = headers.indexOf("\r\n", index); + if (next < 0) { + next = headers.length(); + } + + int colon = headers.indexOf(':', index); + if (colon < 0) { + colon = headers.length(); + } + String key = colon < next ? headers.substring(index, colon) : headers.substring(index, next); + String value = colon < next ? headers.substring(colon + 1, next).trim() : ""; + key = key.trim(); + keys.add(key); + values.add(value); + + List headerFieldValues = headerFields.get(key); + if (headerFieldValues == null) { + headerFieldValues = new ArrayList<>(); + headerFields.put(key, headerFieldValues); + } + headerFieldValues.add(value); + + key = key.toLowerCase(); + responseHeaders.put(key, value); + + index = next + 2; + } + + responseHeaderKeys = keys.toArray(new String[keys.size()]); + responseHeaderValues = values.toArray(new String[values.size()]); + } + + @Override + public String getHeaderFieldKey(int posn) { + if (responseHeaderKeys == null || posn >= responseHeaderKeys.length) { + return null; + } + return responseHeaderKeys[posn]; + } + + @Override + public String getHeaderField(int pos) { + if (responseHeaderValues == null || pos >= responseHeaderValues.length) { + return null; + } + return responseHeaderValues[pos]; + } + + @Override + public String getHeaderField(String key) { + return responseHeaders != null ? responseHeaders.get(key.toLowerCase()) : null; + } + + @Override + public Map> getHeaderFields() { + return headerFields; + } + + @Override + public InputStream getInputStream() throws IOException { + connect(); + + int responseGroup = responseCode / 100; + if (responseGroup == 4 || responseGroup == 5) { + inputStream = new ByteArrayInputStream(new byte[0]); + throw new IOException("HTTP status: " + responseCode + " " + responseMessage); + } + + return inputStream; + } + + @Override + public InputStream getErrorStream() { + return errorStream; + } + + @Override + public OutputStream getOutputStream() throws IOException { + if (outputStream == null) { + outputStream = new ByteArrayOutputStream(); + } + return outputStream; + } +} diff --git a/jso/apis/src/main/java/org/teavm/jso/ajax/XMLHttpRequest.java b/jso/apis/src/main/java/org/teavm/jso/ajax/XMLHttpRequest.java index 3e7d0ee16..4368bfd63 100644 --- a/jso/apis/src/main/java/org/teavm/jso/ajax/XMLHttpRequest.java +++ b/jso/apis/src/main/java/org/teavm/jso/ajax/XMLHttpRequest.java @@ -43,6 +43,8 @@ public abstract class XMLHttpRequest implements JSObject { public abstract void send(String data); + public abstract void send(JSObject data); + public abstract void setRequestHeader(String name, String value); public abstract String getAllResponseHeaders(); diff --git a/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java b/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java new file mode 100644 index 000000000..714c9b957 --- /dev/null +++ b/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java @@ -0,0 +1,876 @@ +/* + * Copyright 2018 Alexey Andreev. + * + * Licensed 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. + */ + +package org.teavm.classlib.java.net; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.teavm.junit.TeaVMTestRunner; + +@RunWith(TeaVMTestRunner.class) +public class URLTest { + URL u; + URL u1; + URL u2; + URL u3; + URL u4; + URL u5; + boolean caught; + + + @Test + public void test_ConstructorLjava_lang_String() throws IOException { + // Tests for multiple URL instantiation basic parsing test + u = new URL("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"); + assertEquals("u returns a wrong protocol", "http", u.getProtocol()); + assertEquals("u returns a wrong host", "www.yahoo1.com", u.getHost()); + assertEquals("u returns a wrong port", 8080, u.getPort()); + assertEquals("u returns a wrong file", "/dir1/dir2/test.cgi?point1.html", u.getFile()); + assertEquals("u returns a wrong anchor", "anchor1", u.getRef()); + + // test for no file + u1 = new URL("http://www.yahoo2.com:9999"); + assertEquals("u1 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("u1 returns a wrong host", "www.yahoo2.com", u1.getHost()); + assertEquals("u1 returns a wrong port", 9999, u1.getPort()); + assertTrue("u1 returns a wrong file", u1.getFile().equals("")); + assertNull("u1 returns a wrong anchor", u1.getRef()); + + // test for no port + u2 = new URL("http://www.yahoo3.com/dir1/dir2/test.cgi?point1.html#anchor1"); + assertEquals("u2 returns a wrong protocol", "http", u2.getProtocol()); + assertEquals("u2 returns a wrong host", "www.yahoo3.com", u2.getHost()); + assertEquals("u2 returns a wrong port", -1, u2.getPort()); + assertEquals("u2 returns a wrong file", "/dir1/dir2/test.cgi?point1.html", u2.getFile()); + assertEquals("u2 returns a wrong anchor", "anchor1", u2.getRef()); + + // test for no port + URL u2a = new URL("file://www.yahoo3.com/dir1/dir2/test.cgi#anchor1"); + assertEquals("u2a returns a wrong protocol", "file", u2a.getProtocol()); + assertEquals("u2a returns a wrong host", "www.yahoo3.com", u2a.getHost()); + assertEquals("u2a returns a wrong port", -1, u2a.getPort()); + assertEquals("u2a returns a wrong file", "/dir1/dir2/test.cgi", u2a.getFile()); + assertEquals("u2a returns a wrong anchor", "anchor1", u2a.getRef()); + + // test for no file, no port + u3 = new URL("http://www.yahoo4.com/"); + assertEquals("u3 returns a wrong protocol", "http", u3.getProtocol()); + assertEquals("u3 returns a wrong host", "www.yahoo4.com", u3.getHost()); + assertEquals("u3 returns a wrong port", -1, u3.getPort()); + assertEquals("u3 returns a wrong file", "/", u3.getFile()); + assertNull("u3 returns a wrong anchor", u3.getRef()); + + // test for no file, no port + URL u3a = new URL("file://www.yahoo4.com/"); + assertEquals("u3a returns a wrong protocol", "file", u3a.getProtocol()); + assertEquals("u3a returns a wrong host", "www.yahoo4.com", u3a.getHost()); + assertEquals("u3a returns a wrong port", -1, u3a.getPort()); + assertEquals("u3a returns a wrong file", "/", u3a.getFile()); + assertNull("u3a returns a wrong anchor", u3a.getRef()); + + // test for no file, no port + URL u3b = new URL("file://www.yahoo4.com"); + assertEquals("u3b returns a wrong protocol", "file", u3b.getProtocol()); + assertEquals("u3b returns a wrong host", "www.yahoo4.com", u3b.getHost()); + assertEquals("u3b returns a wrong port", -1, u3b.getPort()); + assertTrue("u3b returns a wrong file", u3b.getFile().equals("")); + assertNull("u3b returns a wrong anchor", u3b.getRef()); + + // test for non-port ":" and wierd characters occurrences + u4 = new URL("http://www.yahoo5.com/di!@$%^&*()_+r1/di:::r2/test.cgi?point1.html#anchor1"); + assertEquals("u4 returns a wrong protocol", "http", u4.getProtocol()); + assertEquals("u4 returns a wrong host", "www.yahoo5.com", u4.getHost()); + assertEquals("u4 returns a wrong port", -1, u4.getPort()); + assertEquals("u4 returns a wrong file", "/di!@$%^&*()_+r1/di:::r2/test.cgi?point1.html", u4.getFile()); + assertEquals("u4 returns a wrong anchor", "anchor1", u4.getRef()); + + u5 = new URL("file:/testing.tst"); + assertEquals("u5 returns a wrong protocol", "file", u5.getProtocol()); + assertTrue("u5 returns a wrong host", u5.getHost().equals("")); + assertEquals("u5 returns a wrong port", -1, u5.getPort()); + assertEquals("u5 returns a wrong file", "/testing.tst", u5.getFile()); + assertNull("u5 returns a wrong anchor", u5.getRef()); + + URL u5a = new URL("file:testing.tst"); + assertEquals("u5a returns a wrong protocol", "file", u5a.getProtocol()); + assertTrue("u5a returns a wrong host", u5a.getHost().equals("")); + assertEquals("u5a returns a wrong port", -1, u5a.getPort()); + assertEquals("u5a returns a wrong file", "testing.tst", u5a.getFile()); + assertNull("u5a returns a wrong anchor", u5a.getRef()); + + URL u6 = new URL("http://host:/file"); + assertEquals("u6 return a wrong port", -1, u6.getPort()); + + URL u7 = new URL("file:../../file.txt"); + assertTrue("u7 returns a wrong file: " + u7.getFile(), u7.getFile().equals("../../file.txt")); + + URL u8 = new URL("http://[fec0::1:20d:60ff:fe24:7410]:35/file.txt"); + assertTrue("u8 returns a wrong protocol " + u8.getProtocol(), u8.getProtocol().equals("http")); + assertTrue("u8 returns a wrong host " + u8.getHost(), u8.getHost().equals("[fec0::1:20d:60ff:fe24:7410]")); + assertTrue("u8 returns a wrong port " + u8.getPort(), u8.getPort() == 35); + assertTrue("u8 returns a wrong file " + u8.getFile(), u8.getFile().equals("/file.txt")); + assertNull("u8 returns a wrong anchor " + u8.getRef(), u8.getRef()); + + URL u9 = new URL("file://[fec0::1:20d:60ff:fe24:7410]/file.txt#sogood"); + assertTrue("u9 returns a wrong protocol " + u9.getProtocol(), u9.getProtocol().equals("file")); + assertTrue("u9 returns a wrong host " + u9.getHost(), u9.getHost().equals("[fec0::1:20d:60ff:fe24:7410]")); + assertTrue("u9 returns a wrong port " + u9.getPort(), u9.getPort() == -1); + assertTrue("u9 returns a wrong file " + u9.getFile(), u9.getFile().equals("/file.txt")); + assertTrue("u9 returns a wrong anchor " + u9.getRef(), u9.getRef().equals("sogood")); + + URL u10 = new URL("file://[fec0::1:20d:60ff:fe24:7410]"); + assertTrue("u10 returns a wrong protocol " + u10.getProtocol(), u10.getProtocol().equals("file")); + assertTrue("u10 returns a wrong host " + u10.getHost(), u10.getHost() + .equals("[fec0::1:20d:60ff:fe24:7410]")); + assertTrue("u10 returns a wrong port " + u10.getPort(), u10.getPort() == -1); + + URL u11 = new URL("file:////file.txt"); + assertNull("u11 returns a wrong authority " + u11.getAuthority(), u11.getAuthority()); + assertTrue("u11 returns a wrong file " + u11.getFile(), u11.getFile().equals("////file.txt")); + + URL u12 = new URL("file:///file.txt"); + assertTrue("u12 returns a wrong authority", u12.getAuthority().equals("")); + assertTrue("u12 returns a wrong file " + u12.getFile(), u12.getFile().equals("/file.txt")); + + + // test for error catching + + // Bad HTTP format - no "//" + u = new URL("http:www.yahoo5.com::22/dir1/di:::r2/test.cgi?point1.html#anchor1"); + + caught = false; + try { + u = new URL("http://www.yahoo5.com::22/dir1/di:::r2/test.cgi?point1.html#anchor1"); + } catch (MalformedURLException e) { + caught = true; + } + assertTrue("Should have throw MalformedURLException", caught); + + // unknown protocol + try { + u = new URL("myProtocol://www.yahoo.com:22"); + } catch (MalformedURLException e) { + caught = true; + } + assertTrue("3 Failed to throw MalformedURLException", caught); + + caught = false; + // no protocol + try { + u = new URL("www.yahoo.com"); + } catch (MalformedURLException e) { + caught = true; + } + assertTrue("4 Failed to throw MalformedURLException", caught); + + caught = false; + + URL u1 = null; + try { + // No leading or trailing spaces. + u1 = new URL("file:/some/path"); + assertEquals("5 got wrong file length1", 10, u1.getFile().length()); + + // Leading spaces. + u1 = new URL(" file:/some/path"); + assertEquals("5 got wrong file length2", 10, u1.getFile().length()); + + // Trailing spaces. + u1 = new URL("file:/some/path "); + assertEquals("5 got wrong file length3", 10, u1.getFile().length()); + + // Leading and trailing. + u1 = new URL(" file:/some/path "); + assertEquals("5 got wrong file length4", 10, u1.getFile().length()); + + // in-place spaces. + u1 = new URL(" file: /some/path "); + assertEquals("5 got wrong file length5", 12, u1.getFile().length()); + + } catch (MalformedURLException e) { + fail("5 Did not expect the exception " + e); + } + + // testing jar protocol with relative path + // to make sure it's not canonicalized + try { + String file = "file:/a!/b/../d"; + + u = new URL("jar:" + file); + assertEquals("Wrong file (jar protocol, relative path)", file, u.getFile()); + } catch (MalformedURLException e) { + fail("Unexpected exception (jar protocol, relative path)" + e); + } + } + + @Test + public void test_ConstructorLjava_net_URLLjava_lang_String() + throws Exception { + // Test for method java.net.URL(java.net.URL, java.lang.String) + u = new URL("http://www.yahoo.com"); + URL uf = new URL("file://www.yahoo.com"); + // basic ones + u1 = new URL(u, "file.java"); + assertEquals("1 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("1 returns a wrong host", "www.yahoo.com", u1.getHost()); + assertEquals("1 returns a wrong port", -1, u1.getPort()); + assertEquals("1 returns a wrong file", "/file.java", u1.getFile()); + assertNull("1 returns a wrong anchor", u1.getRef()); + + URL u1f = new URL(uf, "file.java"); + assertEquals("1f returns a wrong protocol", "file", u1f.getProtocol()); + assertEquals("1f returns a wrong host", "www.yahoo.com", u1f.getHost()); + assertEquals("1f returns a wrong port", -1, u1f.getPort()); + assertEquals("1f returns a wrong file", "/file.java", u1f.getFile()); + assertNull("1f returns a wrong anchor", u1f.getRef()); + + u1 = new URL(u, "dir1/dir2/../file.java"); + assertEquals("3 returns a wrong protocol", "http", u1.getProtocol()); + assertTrue("3 returns a wrong host: " + u1.getHost(), u1.getHost().equals("www.yahoo.com")); + assertEquals("3 returns a wrong port", -1, u1.getPort()); + assertEquals("3 returns a wrong file", "/dir1/dir2/../file.java", u1.getFile()); + assertNull("3 returns a wrong anchor", u1.getRef()); + + u1 = new URL(u, "http:dir1/dir2/../file.java"); + assertEquals("3a returns a wrong protocol", "http", u1.getProtocol()); + assertTrue("3a returns a wrong host: " + u1.getHost(), u1.getHost().equals("")); + assertEquals("3a returns a wrong port", -1, u1.getPort()); + assertEquals("3a returns a wrong file", "dir1/dir2/../file.java", u1.getFile()); + assertNull("3a returns a wrong anchor", u1.getRef()); + + u = new URL("http://www.apache.org/testing/"); + u1 = new URL(u, "file.java"); + assertEquals("4 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("4 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("4 returns a wrong port", -1, u1.getPort()); + assertEquals("4 returns a wrong file", "/testing/file.java", u1.getFile()); + assertNull("4 returns a wrong anchor", u1.getRef()); + + uf = new URL("file://www.apache.org/testing/"); + u1f = new URL(uf, "file.java"); + assertEquals("4f returns a wrong protocol", "file", u1f.getProtocol()); + assertEquals("4f returns a wrong host", "www.apache.org", u1f.getHost()); + assertEquals("4f returns a wrong port", -1, u1f.getPort()); + assertEquals("4f returns a wrong file", "/testing/file.java", u1f.getFile()); + assertNull("4f returns a wrong anchor", u1f.getRef()); + + uf = new URL("file:/testing/"); + u1f = new URL(uf, "file.java"); + assertEquals("4fa returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("4fa returns a wrong host", u1f.getHost().equals("")); + assertEquals("4fa returns a wrong port", -1, u1f.getPort()); + assertEquals("4fa returns a wrong file", "/testing/file.java", u1f.getFile()); + assertNull("4fa returns a wrong anchor", u1f.getRef()); + + uf = new URL("file:testing/"); + u1f = new URL(uf, "file.java"); + assertEquals("4fb returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("4fb returns a wrong host", u1f.getHost().equals("")); + assertEquals("4fb returns a wrong port", -1, u1f.getPort()); + assertEquals("4fb returns a wrong file", "testing/file.java", u1f.getFile()); + assertNull("4fb returns a wrong anchor", u1f.getRef()); + + u1f = new URL(uf, "file:file.java"); + assertEquals("4fc returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("4fc returns a wrong host", u1f.getHost().equals("")); + assertEquals("4fc returns a wrong port", -1, u1f.getPort()); + assertEquals("4fc returns a wrong file", "file.java", u1f.getFile()); + assertNull("4fc returns a wrong anchor", u1f.getRef()); + + u1f = new URL(uf, "file:"); + assertEquals("4fd returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("4fd returns a wrong host", u1f.getHost().equals("")); + assertEquals("4fd returns a wrong port", -1, u1f.getPort()); + assertTrue("4fd returns a wrong file", u1f.getFile().equals("")); + assertNull("4fd returns a wrong anchor", u1f.getRef()); + + u = new URL("http://www.apache.org/testing"); + u1 = new URL(u, "file.java"); + assertEquals("5 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("5 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("5 returns a wrong port", -1, u1.getPort()); + assertEquals("5 returns a wrong file", "/file.java", u1.getFile()); + assertNull("5 returns a wrong anchor", u1.getRef()); + + uf = new URL("file://www.apache.org/testing"); + u1f = new URL(uf, "file.java"); + assertEquals("5f returns a wrong protocol", "file", u1f.getProtocol()); + assertEquals("5f returns a wrong host", "www.apache.org", u1f.getHost()); + assertEquals("5f returns a wrong port", -1, u1f.getPort()); + assertEquals("5f returns a wrong file", "/file.java", u1f.getFile()); + assertNull("5f returns a wrong anchor", u1f.getRef()); + + uf = new URL("file:/testing"); + u1f = new URL(uf, "file.java"); + assertEquals("5fa returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("5fa returns a wrong host", u1f.getHost().equals("")); + assertEquals("5fa returns a wrong port", -1, u1f.getPort()); + assertEquals("5fa returns a wrong file", "/file.java", u1f.getFile()); + assertNull("5fa returns a wrong anchor", u1f.getRef()); + + uf = new URL("file:testing"); + u1f = new URL(uf, "file.java"); + assertEquals("5fb returns a wrong protocol", "file", u1f.getProtocol()); + assertTrue("5fb returns a wrong host", u1f.getHost().equals("")); + assertEquals("5fb returns a wrong port", -1, u1f.getPort()); + assertEquals("5fb returns a wrong file", "file.java", u1f.getFile()); + assertNull("5fb returns a wrong anchor", u1f.getRef()); + + u = new URL("http://www.apache.org/testing/foobaz"); + u1 = new URL(u, "/file.java"); + assertEquals("6 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("6 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("6 returns a wrong port", -1, u1.getPort()); + assertEquals("6 returns a wrong file", "/file.java", u1.getFile()); + assertNull("6 returns a wrong anchor", u1.getRef()); + + uf = new URL("file://www.apache.org/testing/foobaz"); + u1f = new URL(uf, "/file.java"); + assertEquals("6f returns a wrong protocol", "file", u1f.getProtocol()); + assertEquals("6f returns a wrong host", "www.apache.org", u1f.getHost()); + assertEquals("6f returns a wrong port", -1, u1f.getPort()); + assertEquals("6f returns a wrong file", "/file.java", u1f.getFile()); + assertNull("6f returns a wrong anchor", u1f.getRef()); + + u = new URL("http://www.apache.org:8000/testing/foobaz"); + u1 = new URL(u, "/file.java"); + assertEquals("7 returns a wrong protocol", "http", u1.getProtocol()); + assertEquals("7 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("7 returns a wrong port", 8000, u1.getPort()); + assertEquals("7 returns a wrong file", "/file.java", u1.getFile()); + assertNull("7 returns a wrong anchor", u1.getRef()); + + u = new URL("http://www.apache.org/index.html"); + u1 = new URL(u, "#bar"); + assertEquals("8 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("8 returns a wrong file", "/index.html", u1.getFile()); + assertEquals("8 returns a wrong anchor", "bar", u1.getRef()); + + u = new URL("http://www.apache.org/index.html#foo"); + u1 = new URL(u, "http:#bar"); + assertEquals("9 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("9 returns a wrong file", "/index.html", u1.getFile()); + assertEquals("9 returns a wrong anchor", "bar", u1.getRef()); + + u = new URL("http://www.apache.org/index.html"); + u1 = new URL(u, ""); + assertEquals("10 returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("10 returns a wrong file", "/index.html", u1.getFile()); + assertNull("10 returns a wrong anchor", u1.getRef()); + + uf = new URL("file://www.apache.org/index.html"); + u1f = new URL(uf, ""); + assertEquals("10f returns a wrong host", "www.apache.org", u1.getHost()); + assertEquals("10f returns a wrong file", "/index.html", u1.getFile()); + assertNull("10f returns a wrong anchor", u1.getRef()); + + u = new URL("http://www.apache.org/index.html"); + u1 = new URL(u, "http://www.apache.org"); + assertEquals("11 returns a wrong host", "www.apache.org", u1.getHost()); + assertTrue("11 returns a wrong file", u1.getFile().equals("")); + assertNull("11 returns a wrong anchor", u1.getRef()); + + // test for question mark processing + u = new URL("http://www.foo.com/d0/d1/d2/cgi-bin?foo=bar/baz"); + + // test for relative file and out of bound "/../" processing + u1 = new URL(u, "../dir1/./dir2/../file.java"); + assertTrue("A) returns a wrong file: " + u1.getFile(), u1.getFile().equals("/d0/d1/dir1/file.java")); + + // test for absolute and relative file processing + u1 = new URL(u, "/../dir1/./dir2/../file.java"); + assertEquals("B) returns a wrong file", "/../dir1/./dir2/../file.java", u1.getFile()); + + try { + // u should raise a MalFormedURLException because u, the context is + // null + u = null; + u1 = new URL(u, "file.java"); + fail("didn't throw the expected MalFormedURLException"); + } catch (MalformedURLException e) { + // valid + } + + // Regression test for HARMONY-3258 + // testing jar context url with relative file + try { + // check that relative path with null context is not canonicalized + String spec = "jar:file:/a!/b/../d"; + URL ctx = null; + u = new URL(ctx, spec); + assertEquals("1 Wrong file (jar protocol, relative path)", spec, u.toString()); + + spec = "../d"; + ctx = new URL("jar:file:/a!/b"); + u = new URL(ctx, spec); + assertEquals("2 Wrong file (jar protocol, relative path)", "file:/a!/d", u.getFile()); + + spec = "../d"; + ctx = new URL("jar:file:/a!/b/c"); + u = new URL(ctx, spec); + assertEquals("3 Wrong file (jar protocol, relative path)", "file:/a!/d", u.getFile()); + + spec = "../d"; + ctx = new URL("jar:file:/a!/b/c/d"); + u = new URL(ctx, spec); + assertEquals("4 Wrong file (jar protocol, relative path)", "file:/a!/b/d", u.getFile()); + + // added the real example + spec = "../pdf/PDF.settings"; + ctx = new URL("jar:file:/C:/Program%20Files/Netbeans-5.5/ide7/" + + "modules/org-netbeans-modules-utilities.jar!/org/netbeans/modules/utilities/Layer.xml"); + u = new URL(ctx, spec); + assertEquals( + "5 Wrong file (jar protocol, relative path)", + "file:/C:/Program%20Files/Netbeans-5.5/ide7/" + + "modules/org-netbeans-modules-utilities.jar!/org/netbeans/modules/pdf/PDF.settings", + u.getFile()); + } catch (MalformedURLException e) { + fail("Testing jar protocol, relative path failed: " + e); + } + } + + @Test + public void test_ConstructorLjava_lang_StringLjava_lang_StringLjava_lang_String() + throws MalformedURLException { + + u = new URL("http", "www.yahoo.com", "test.html#foo"); + assertEquals("http", u.getProtocol()); + assertEquals("www.yahoo.com", u.getHost()); + assertEquals(-1, u.getPort()); + assertEquals("test.html", u.getFile()); + assertEquals("foo", u.getRef()); + + // Strange behavior in reference, the hostname contains a ':' so it gets + // wrapped in '[', ']' + URL testURL = new URL("http", "www.apache.org:8080", "test.html#anch"); + assertEquals("wrong protocol", "http", testURL.getProtocol()); + assertEquals("wrong host", "[www.apache.org:8080]", testURL.getHost()); + assertEquals("wrong port", -1, testURL.getPort()); + assertEquals("wrong file", "test.html", testURL.getFile()); + assertEquals("wrong anchor", "anch", testURL.getRef()); + } + + @Test + public void test_ConstructorLjava_lang_StringLjava_lang_StringILjava_lang_String() throws MalformedURLException { + u = new URL("http", "www.yahoo.com", 8080, "test.html#foo"); + assertEquals("SSIS returns a wrong protocol", "http", u.getProtocol()); + assertEquals("SSIS returns a wrong host", "www.yahoo.com", u.getHost()); + assertEquals("SSIS returns a wrong port", 8080, u.getPort()); + assertEquals("SSIS returns a wrong file", "test.html", u.getFile()); + assertTrue("SSIS returns a wrong anchor: " + u.getRef(), u.getRef().equals("foo")); + + // Regression for HARMONY-83 + new URL("http", "apache.org", 123456789, "file"); + try { + new URL("http", "apache.org", -123, "file"); + fail("Assert 0: Negative port should throw exception"); + } catch (MalformedURLException e) { + // expected + } + + } + + @Test + public void test_ConstructorLjava_lang_StringLjava_lang_StringILjava_lang_StringLjava_net_URLStreamHandler() + throws Exception { + // Test for method java.net.URL(java.lang.String, java.lang.String, int, + // java.lang.String, java.net.URLStreamHandler) + u = new URL("http", "www.yahoo.com", 8080, "test.html#foo", null); + assertEquals("SSISH1 returns a wrong protocol", "http", u.getProtocol()); + assertEquals("SSISH1 returns a wrong host", "www.yahoo.com", u.getHost()); + assertEquals("SSISH1 returns a wrong port", 8080, u.getPort()); + assertEquals("SSISH1 returns a wrong file", "test.html", u.getFile()); + assertTrue("SSISH1 returns a wrong anchor: " + u.getRef(), u.getRef().equals("foo")); + } + + @Test + public void test_equalsLjava_lang_Object() throws MalformedURLException { + u = new URL("http://www.apache.org:8080/dir::23??????????test.html"); + u1 = new URL("http://www.apache.org:8080/dir::23??????????test.html"); + assertTrue("A) equals returns false for two identical URLs", u + .equals(u1)); + assertTrue("return true for null comparison", !u1.equals(null)); + u = new URL("ftp://www.apache.org:8080/dir::23??????????test.html"); + assertTrue("Returned true for non-equal URLs", !u.equals(u1)); + + // Regression for HARMONY-6556 + u = new URL("file", null, 0, "/test.txt"); + u1 = new URL("file", null, 0, "/test.txt"); + assertEquals(u, u1); + + u = new URL("file", "first.invalid", 0, "/test.txt"); + u1 = new URL("file", "second.invalid", 0, "/test.txt"); + assertFalse(u.equals(u1)); + + u = new URL("file", "harmony.apache.org", 0, "/test.txt"); + u1 = new URL("file", "www.apache.org", 0, "/test.txt"); + assertEquals(u, u1); + } + + @Test + public void test_sameFileLjava_net_URL() throws Exception { + // Test for method boolean java.net.URL.sameFile(java.net.URL) + u = new URL("http://www.yahoo.com"); + u1 = new URL("http", "www.yahoo.com", ""); + assertTrue("Should be the same1", u.sameFile(u1)); + u = new URL("http://www.yahoo.com/dir1/dir2/test.html#anchor1"); + u1 = new URL("http://www.yahoo.com/dir1/dir2/test.html#anchor2"); + assertTrue("Should be the same ", u.sameFile(u1)); + + // regression test for Harmony-1040 + u = new URL("file", null, -1, "/d:/somedir/"); + u1 = new URL("file:/d:/somedir/"); + assertFalse(u.sameFile(u1)); + + // regression test for Harmony-2136 + URL url1 = new URL("file:///anyfile"); + URL url2 = new URL("file://localhost/anyfile"); + assertTrue(url1.sameFile(url2)); + + url1 = new URL("http:///anyfile"); + url2 = new URL("http://localhost/anyfile"); + assertFalse(url1.sameFile(url2)); + + url1 = new URL("ftp:///anyfile"); + url2 = new URL("ftp://localhost/anyfile"); + assertFalse(url1.sameFile(url2)); + + url1 = new URL("jar:file:///anyfile.jar!/"); + url2 = new URL("jar:file://localhost/anyfile.jar!/"); + assertFalse(url1.sameFile(url2)); + } + + @Test + public void test_toString() { + // Test for method java.lang.String java.net.URL.toString() + try { + u1 = new URL("http://www.yahoo2.com:9999"); + u = new URL("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"); + assertEquals( + "a) Does not return the right url string", + "http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1", + u.toString()); + assertEquals("b) Does not return the right url string", + "http://www.yahoo2.com:9999", u1.toString()); + assertTrue("c) Does not return the right url string", u + .equals(new URL(u.toString()))); + } catch (Exception e) { + // Do nothing + } + } + + @Test + public void test_toExternalForm() { + try { + u1 = new URL("http://www.yahoo2.com:9999"); + u = new URL("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"); + assertEquals( + "a) Does not return the right url string", + "http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1", + u.toString()); + assertEquals("b) Does not return the right url string", "http://www.yahoo2.com:9999", u1.toString()); + assertTrue("c) Does not return the right url string", u.equals(new URL(u.toString()))); + + u = new URL("http:index"); + assertEquals("2 wrong external form", "http:index", u.toExternalForm()); + + u = new URL("http", null, "index"); + assertEquals("2 wrong external form", "http:index", u.toExternalForm()); + } catch (Exception e) { + // Do nothing + } + } + + @Test + public void test_getFile() throws Exception { + // Test for method java.lang.String java.net.URL.getFile() + u = new URL("http", "www.yahoo.com:8080", 1233, "test/!@$%^&*/test.html#foo"); + assertEquals("returns a wrong file", "test/!@$%^&*/test.html", u.getFile()); + u = new URL("http", "www.yahoo.com:8080", 1233, ""); + assertTrue("returns a wrong file", u.getFile().equals("")); + } + + @Test + public void test_getHost() throws MalformedURLException { + // Regression for HARMONY-60 + String ipv6Host = "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210"; + URL url = new URL("http", ipv6Host, -1, "myfile"); + assertEquals("[" + ipv6Host + "]", url.getHost()); + } + + @Test + public void test_getPort() throws Exception { + // Test for method int java.net.URL.getPort() + u = new URL("http://member12.c++.com:9999"); + assertTrue("return wrong port number " + u.getPort(), u.getPort() == 9999); + u = new URL("http://member12.c++.com:9999/"); + assertEquals("return wrong port number", 9999, u.getPort()); + } + + @Test + public void test_getDefaultPort() throws MalformedURLException { + u = new URL("http://member12.c++.com:9999"); + assertEquals(80, u.getDefaultPort()); + u = new URL("ftp://member12.c++.com:9999/"); + assertEquals(21, u.getDefaultPort()); + } + + @Test + public void test_getProtocol() throws Exception { + // Test for method java.lang.String java.net.URL.getProtocol() + u = new URL("http://www.yahoo2.com:9999"); + assertTrue("u returns a wrong protocol: " + u.getProtocol(), u.getProtocol().equals("http")); + } + + @Test + public void test_getRef() { + // Test for method java.lang.String java.net.URL.getRef() + try { + u1 = new URL("http://www.yahoo2.com:9999"); + u = new URL("http://www.yahoo1.com:8080/dir1/dir2/test.cgi?point1.html#anchor1"); + assertEquals("returns a wrong anchor1", "anchor1", u.getRef()); + assertNull("returns a wrong anchor2", u1.getRef()); + u1 = new URL("http://www.yahoo2.com#ref"); + assertEquals("returns a wrong anchor3", "ref", u1.getRef()); + u1 = new URL("http://www.yahoo2.com/file#ref1#ref2"); + assertEquals("returns a wrong anchor4", "ref1#ref2", u1.getRef()); + } catch (MalformedURLException e) { + fail("Incorrect URL format : " + e.getMessage()); + } + } + + @Test + public void test_getAuthority() throws MalformedURLException { + URL testURL = new URL("http", "hostname", 80, "/java?q1#ref"); + assertEquals("hostname:80", testURL.getAuthority()); + assertEquals("hostname", testURL.getHost()); + assertNull(testURL.getUserInfo()); + assertEquals("/java?q1", testURL.getFile()); + assertEquals("/java", testURL.getPath()); + assertEquals("q1", testURL.getQuery()); + assertEquals("ref", testURL.getRef()); + + testURL = new URL("http", "u:p@home", 80, "/java?q1#ref"); + assertEquals("[u:p@home]:80", testURL.getAuthority()); + assertEquals("[u:p@home]", testURL.getHost()); + assertNull(testURL.getUserInfo()); + assertEquals("/java?q1", testURL.getFile()); + assertEquals("/java", testURL.getPath()); + assertEquals("q1", testURL.getQuery()); + assertEquals("ref", testURL.getRef()); + + testURL = new URL("http", "home", -1, "/java"); + assertEquals("wrong authority2", "home", testURL.getAuthority()); + assertNull("wrong userInfo2", testURL.getUserInfo()); + assertEquals("wrong host2", "home", testURL.getHost()); + assertEquals("wrong file2", "/java", testURL.getFile()); + assertEquals("wrong path2", "/java", testURL.getPath()); + assertNull("wrong query2", testURL.getQuery()); + assertNull("wrong ref2", testURL.getRef()); + } + + @Test + public void test_toURI() throws Exception { + u = new URL("http://www.apache.org"); + URI uri = u.toURI(); + assertTrue(u.equals(uri.toURL())); + } + + @Test + public void test_ConstructorLnullLjava_lang_StringILjava_lang_String() throws Exception { + // Regression for HARMONY-1131 + try { + new URL(null, "1", 0, "file"); + fail("NullPointerException expected, but nothing was thrown!"); + } catch (NullPointerException e) { + // Expected NullPointerException + } + } + + @Test + public void test_ConstructorLnullLjava_lang_StringLjava_lang_String() throws Exception { + // Regression for HARMONY-1131 + try { + new URL(null, "1", "file"); + fail("NullPointerException expected, but nothing was thrown!"); + } catch (NullPointerException e) { + // Expected NullPointerException + } + } + + @Test + public void test_toExternalForm_Absolute() throws MalformedURLException { + String strURL = "http://localhost?name=value"; + URL url = new URL(strURL); + assertEquals(strURL, url.toExternalForm()); + + strURL = "http://localhost?name=value/age=12"; + url = new URL(strURL); + assertEquals(strURL, url.toExternalForm()); + } + + @Test + public void test_toExternalForm_Relative() throws MalformedURLException { + String strURL = "http://a/b/c/d;p?q"; + String ref = "?y"; + URL url = new URL(new URL(strURL), ref); + assertEquals("http://a/b/c/?y", url.toExternalForm()); + } + + // Regression test for HARMONY-6254 + + // Bogus handler forces file part of URL to be null + static class MyHandler2 extends URLStreamHandler { + + @Override + protected URLConnection openConnection(URL arg0) throws IOException { + return null; + } + + @Override + protected void setURL(URL u, String protocol, String host, int port, + String authority, String userInfo, String file, String query, + String ref) { + super.setURL(u, protocol, host, port, authority, userInfo, + (String) null, query, ref); + } + } + + @Test + public void test_toExternalForm_Null() throws IOException { + URLStreamHandler myHandler = new MyHandler2(); + URL url = new URL(null, "foobar://example.com/foobar", myHandler); + String s = url.toExternalForm(); + assertEquals("Got wrong URL external form", "foobar://example.com", s); + } + + static class MyURLStreamHandler extends URLStreamHandler { + + @Override + protected URLConnection openConnection(URL arg0) throws IOException { + return null; + } + + public void parse(URL url, String spec, int start, int end) { + parseURL(url, spec, start, end); + } + } + + static class MyURLStreamHandlerFactory implements URLStreamHandlerFactory { + + public static MyURLStreamHandler handler = new MyURLStreamHandler(); + + @Override + public URLStreamHandler createURLStreamHandler(String arg0) { + handler = new MyURLStreamHandler(); + return handler; + } + + } + + @Test + public void test_URLStreamHandler_parseURL() throws MalformedURLException { + URL url = new URL("http://localhost"); + MyURLStreamHandler handler = MyURLStreamHandlerFactory.handler; + try { + handler.parse(url, "//", 0, Integer.MIN_VALUE); + fail("Should throw SIOOBE."); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + try { + handler.parse(url, "1234//", 4, Integer.MIN_VALUE); + fail("Should throw SIOOBE."); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + try { + handler.parse(url, "1", -1, 0); + fail("Should throw SIOOBE."); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + try { + handler.parse(url, "1", 3, 2); + fail("Should throw SecurityException."); + } catch (SecurityException e) { + // expected; + } + + try { + handler.parse(url, "11", 1, Integer.MIN_VALUE); + fail("Should throw SecurityException."); + } catch (SecurityException e) { + // expected; + } + + // Regression tests for HARMONY-6499 + try { + handler.parse(url, "any", 10, Integer.MIN_VALUE); + fail("Should throw StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + + try { + handler.parse(url, "any", 10, Integer.MIN_VALUE + 1); + fail("Should throw StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + + try { + handler.parse(url, "any", Integer.MIN_VALUE, Integer.MIN_VALUE); + fail("Should throw StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + + try { + handler.parse(url, "any", Integer.MIN_VALUE, 2); + fail("Should throw StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + + try { + handler.parse(url, "any", -1, 2); + fail("Should throw StringIndexOutOfBoundsException"); + } catch (StringIndexOutOfBoundsException e) { + // expected; + } + + try { + handler.parse(url, "any", -1, -1); + fail("Should throw SecurityException"); + } catch (SecurityException e) { + // expected; + } + } +}