/*
* @(#)HttpUrl.java 1.16 02/07/24 @(#)
*
* Copyright (c) 2001-2002 Sun Microsystems, Inc. All rights reserved.
* PROPRIETARY/CONFIDENTIAL
* Use is subject to license terms.
*/
package com.sun.midp.io;
import java.io.IOException;
/**
* A parsed HTTP (or subclass of) URL. Based on RFC 2396.
* <p>
* Handles IPv6 hosts, check host[0] for a "[".
* Can be used for relative URL's that do not have authorities.
* Can be used for FTP URL's that do not have the username and passwords.
* <p>
* Any elements not specified are represented by null, except a
* non-specified port, which is represented by a -1.
*/
public class HttpUrl {
/** Scheme of the URL or null. */
public String scheme;
/** Authority (host [port]) of the URL. */
public String authority;
/** Path of the URL or null. */
public String path;
/** Query of the URL or null. */
public String query;
/** Fragment of the URL or null. */
public String fragment;
/** hHst of the authority or null. */
public String host;
/** Port of the authority or -1 for not specified. */
public int port = -1;
/** Machine of the host or null. */
public String machine;
/** Domain of the host or null. */
public String domain;
/**
* Construct a HttpUrl.
*
* @param url HTTP URL to parse
*
* @exception IllegalArgumentException if there is a space in the URL or
* the port is not numeric
*/
public HttpUrl(String url) {
int afterScheme = 0;
int length;
int endOfScheme;
if (url == null) {
return;
}
length = url.length();
if (length == 0) {
return;
}
// ":" can mark a the scheme in a absolute URL which has a "//".
endOfScheme = url.indexOf(':');
if (endOfScheme != -1) {
if (endOfScheme == length - 1) {
// just a scheme
scheme = url.substring(0, endOfScheme);
return;
}
if (endOfScheme < length - 2 &&
url.charAt(endOfScheme + 1) == '/' &&
url.charAt(endOfScheme + 2) == '/') {
// found "://", get the scheme
scheme = url.substring(0, endOfScheme);
afterScheme = endOfScheme + 1;
}
}
parseAfterScheme(url, afterScheme, length);
}
/**
* Construct a HttpUrl from a scheme and partial HTTP URL.
*
* @param theScheme the protocol component of an HTTP URL
* @param partialUrl HTTP URL to parse
*
* @exception IllegalArgumentException if there is a space in the URL or
* the port is not numeric
*/
public HttpUrl(String theScheme, String partialUrl) {
int length;
scheme = theScheme;
if (partialUrl == null) {
return;
}
length = partialUrl.length();
if (length == 0) {
return;
}
parseAfterScheme(partialUrl, 0, length);
}
/**
* Parse the part of the HTTP URL after the scheme.
*
* @param url the part of the HTTP URL after the ":" of the scheme
* @param afterScheme index of the first char after the scheme
* @param length length of the url
*
* @exception IllegalArgumentException if there is a space in the URL or
* the port is not numeric
*/
private void parseAfterScheme(String url, int afterScheme, int length) {
int start;
int startOfAuthority;
int endOfUrl;
int endOfAuthority;
int endOfPath;
int endOfQuery;
int endOfHost;
int startOfPort;
int endOfPort;
int startOfDomain;
if (url.indexOf(' ') != -1) {
throw new IllegalArgumentException("Space character in URL");
}
endOfUrl = length;
endOfAuthority = endOfUrl;
endOfPath = endOfUrl;
endOfQuery = endOfUrl;
if (url.startsWith("//", afterScheme)) {
// do not include the "//"
startOfAuthority = afterScheme + 2;
} else {
// no authority, the path starts at 0 and may not begin with a "/"
startOfAuthority = afterScheme;
}
/*
* Since all of the elements after the authority are optional
* and they can contain the delimiter of the element before it.
* Work backwards since we know the end of the last item and will
* know the end of the next item when find the start of the current
* item.
*/
start = url.indexOf('#', startOfAuthority);
if (start != -1) {
endOfAuthority = start;
endOfPath = start;
endOfQuery = start;
// do not include the "#"
start++;
// do not parse an empty fragment
if (start < endOfUrl) {
fragment = url.substring(start, endOfUrl);
}
}
start = url.indexOf('?', startOfAuthority);
if (start != -1 && start < endOfQuery) {
endOfAuthority = start;
endOfPath = start;
// do not include the "?"
start++;
// do not parse an empty query
if (start < endOfQuery) {
query = url.substring(start, endOfQuery);
}
}
if (startOfAuthority == afterScheme) {
// no authority, the path starts after scheme
start = afterScheme;
} else {
// this is not relative URL so the path must begin with "/"
start = url.indexOf('/', startOfAuthority);
}
// do not parse an empty path
if (start != -1 && start < endOfPath) {
endOfAuthority = start;
path = url.substring(start, endOfPath);
}
if (startOfAuthority >= endOfAuthority) {
return;
}
authority = url.substring(startOfAuthority, endOfAuthority);
endOfPort = authority.length();
// get the port first, to find the end of the host
// IPv6 address have brackets around them and can have ":"'s
start = authority.indexOf(']');
if (start == -1) {
startOfPort = authority.indexOf(':');
} else {
startOfPort = authority.indexOf(':', start);
}
if (startOfPort != -1) {
endOfHost = startOfPort;
// do not include the ":"
startOfPort++;
// do not try parse an empty port
if (startOfPort < endOfPort) {
try {
port = Integer.parseInt(authority.substring(
startOfPort,
endOfPort));
if (port <= 0) {
throw new
IllegalArgumentException("invalid port format");
}
if (port == 0 || port > 0xFFFF) {
throw new IllegalArgumentException(
"port out of legal range");
}
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException("invalid port format");
}
}
} else {
endOfHost = endOfPort;
}
// there could be a port but no host
if (endOfHost < 1) {
return;
}
// get the host
host = authority.substring(0, endOfHost);
// find the machine and domain, if not host is not an IP address
if (Character.isDigit(host.charAt(0)) || host.charAt(0) == '[') {
return;
}
startOfDomain = host.indexOf('.');
if (startOfDomain != -1) {
// do not include the "."
domain = host.substring(startOfDomain + 1, host.length());
machine = host.substring(0, startOfDomain);
} else {
machine = host;
}
}
/**
* Adds a base URL to this URL if this URL is a relative one.
* Afterwards this URL will be an absolute URL.
*
* @param baseUrl an absolute URL
*
* @exception IllegalArgumentException if there is a space in the URL or
* the port is not numeric
* @exception IOException if an I/O error occurs processing the URL
*/
public void addBaseUrl(String baseUrl) throws IOException {
addBaseUrl(new HttpUrl(baseUrl));
}
/**
* Adds a base URL to this URL if this URL is a relative one.
* Afterwards this URL will be an absolute URL.
*
* @param baseUrl a parsed absolute URL
*/
public void addBaseUrl(HttpUrl baseUrl) {
String basePath;
if (authority != null) {
return;
}
scheme = baseUrl.scheme;
authority = baseUrl.authority;
if (path == null) {
path = baseUrl.path;
return;
}
if (path.charAt(0) == '/' || baseUrl.path == null ||
baseUrl.path.charAt(0) != '/') {
return;
}
// find the base path
basePath = baseUrl.path.substring(0, baseUrl.path.lastIndexOf('/'));
path = basePath + '/' + path;
}
/**
* Converts this URL into a string.
*
* @return string representation of this URL
*/
public String toString() {
StringBuffer url = new StringBuffer();
if (scheme != null) {
url.append(scheme);
url.append(':');
}
if (authority != null) {
url.append('/');
url.append('/');
url.append(authority);
}
if (path != null) {
url.append(path);
}
if (query != null) {
url.append('?');
url.append(query);
}
if (fragment != null) {
url.append('#');
url.append(fragment);
}
return url.toString();
}
}
|