/* * 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. */ package java.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Collections; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Map; /** * A connection to a URL for reading or writing. For HTTP connections, see * {@link HttpURLConnection} for documentation of HTTP-specific features. * *
For example, to retrieve {@code * ftp://mirror.csclub.uwaterloo.ca/index.html}:
{@code * URL url = new URL("ftp://mirror.csclub.uwaterloo.ca/index.html"); * URLConnection urlConnection = url.openConnection(); * InputStream in = new BufferedInputStream(urlConnection.getInputStream()); * try { * readStream(in); * } finally { * in.close(); * } * }* *
{@code URLConnection} must be configured before it has connected to the * remote resource. Instances of {@code URLConnection} are not reusable: you * must use a different instance for each connection to a resource. * *
By default, FTP connections will be made using {@code anonymous} as * the username and the empty string as the password. Specify alternate * usernames and passwords in the URL: {@code * ftp://username:password@host/path}. *
Some implementations (notably {@code HttpURLConnection}) include a mapping * for the null key; in HTTP's case, this maps to the HTTP status line and is * treated as being at position 0 when indexing into the header fields. * * @param pos * the field position of the response header. * @return the value of the field at position {@code pos}. */ public String getHeaderField(int pos) { return null; } /** * Returns an unmodifiable map of the response-header fields and values. The * response-header field names are the key values of the map. The map values * are lists of header field values associated with a particular key name. * *
Some implementations (notably {@code HttpURLConnection}) include a mapping
* for the null key; in HTTP's case, this maps to the HTTP status line and is
* treated as being at position 0 when indexing into the header fields.
*
* @return the response-header representing generic map.
* @since 1.4
*/
public Map Some implementations (notably {@code HttpURLConnection}) include a mapping
* for the null key; in HTTP's case, this maps to the HTTP status line and is
* treated as being at position 0 when indexing into the header fields.
*
* @param key
* the name of the header field.
* @return the value of the header field.
*/
public String getHeaderField(String key) {
return null;
}
/**
* Returns the specified header value as a date in milliseconds since January
* 1, 1970 GMT. Returns the {@code defaultValue} if no such header field
* could be found.
*
* @param field
* the header field name whose value is needed.
* @param defaultValue
* the default value if no field has been found.
* @return the value of the specified header field as a date in
* milliseconds.
*/
@SuppressWarnings("deprecation")
public long getHeaderFieldDate(String field, long defaultValue) {
String date = getHeaderField(field);
if (date == null) {
return defaultValue;
}
try {
return Date.parse(date); // TODO: use HttpDate.parse()
} catch (Exception e) {
return defaultValue;
}
}
/**
* Returns the specified header value as a number. Returns the {@code
* defaultValue} if no such header field could be found or the value could
* not be parsed as an {@code int}.
*
* @param field
* the header field name whose value is needed.
* @param defaultValue
* the default value if no field has been found.
* @return the value of the specified header field as a number.
*/
public int getHeaderFieldInt(String field, int defaultValue) {
try {
return Integer.parseInt(getHeaderField(field));
} catch (NumberFormatException e) {
return defaultValue;
}
}
/**
* Returns the name of the header field at the given position {@code posn} or
* {@code null} if there are fewer than {@code posn} fields. The base
* implementation of this method returns always {@code null}.
*
* Some implementations (notably {@code HttpURLConnection}) include a mapping
* for the null key; in HTTP's case, this maps to the HTTP status line and is
* treated as being at position 0 when indexing into the header fields.
*
* @param posn
* the position of the header field which has to be returned.
* @return the header field name at the given position.
*/
public String getHeaderFieldKey(int posn) {
return null;
}
/**
* Returns the point of time since when the data must be modified to be
* transmitted. Some protocols transmit data only if it has been modified
* more recently than a particular time.
*
* @return the time in milliseconds since January 1, 1970 GMT.
* @see #ifModifiedSince
*/
public long getIfModifiedSince() {
return ifModifiedSince;
}
/**
* Returns an {@code InputStream} for reading data from the resource pointed by
* this {@code URLConnection}. It throws an UnknownServiceException by
* default. This method must be overridden by its subclasses.
*
* @return the InputStream to read data from.
* @throws IOException
* if no InputStream could be created.
*/
public InputStream getInputStream() throws IOException {
throw new UnknownServiceException("Does not support writing to the input stream");
}
/**
* Returns the value of the response header field {@code last-modified} or
* {@code 0} if this value is not set.
*
* @return the value of the {@code last-modified} header field.
*/
public long getLastModified() {
if (lastModified != -1) {
return lastModified;
}
return lastModified = getHeaderFieldDate("Last-Modified", 0);
}
/**
* Returns an {@code OutputStream} for writing data to this {@code
* URLConnection}. It throws an {@code UnknownServiceException} by default.
* This method must be overridden by its subclasses.
*
* @return the OutputStream to write data.
* @throws IOException
* if no OutputStream could be created.
*/
public OutputStream getOutputStream() throws IOException {
throw new UnknownServiceException("Does not support writing to the output stream");
}
/**
* Returns a {@code Permission} object representing all needed permissions to
* open this connection. The returned permission object depends on the state
* of the connection and will be {@code null} if no permissions are
* necessary. By default, this method returns {@code AllPermission}.
* Subclasses should overwrite this method to return an appropriate
* permission object.
*
* @return the permission object representing the needed permissions to open
* this connection.
* @throws IOException
* if an I/O error occurs while creating the permission object.
*/
public java.security.Permission getPermission() throws IOException {
return new java.security.AllPermission();
}
/**
* Returns the value of the request header property specified by {code field}
* or {@code null} if there is no field with this name. The base
* implementation of this method returns always {@code null}.
*
* @param field
* the name of the request header property.
* @return the value of the property.
* @throws IllegalStateException
* if the connection has been already established.
*/
public String getRequestProperty(String field) {
checkNotConnected();
return null;
}
/**
* Returns the URL represented by this {@code URLConnection}.
*
* @return the URL of this connection.
*/
public URL getURL() {
return url;
}
/**
* Returns the value of the flag which specifies whether this {@code
* URLConnection} allows to use caches.
*
* @return {@code true} if using caches is allowed, {@code false} otherwise.
*/
public boolean getUseCaches() {
return useCaches;
}
/**
* Determines the MIME-type of the given resource {@code url} by resolving
* the filename extension with the internal FileNameMap. Any fragment
* identifier is removed before processing.
*
* @param url
* the URL with the filename to get the MIME type.
* @return the guessed content type or {@code null} if the type could not be
* determined.
*/
public static String guessContentTypeFromName(String url) {
return getFileNameMap().getContentTypeFor(url);
}
/**
* Determines the MIME-type of the resource represented by the input stream
* {@code is} by reading its first few characters.
*
* @param is
* the resource representing input stream to determine the
* content type.
* @return the guessed content type or {@code null} if the type could not be
* determined.
* @throws IOException
* if an I/O error occurs while reading from the input stream.
*/
public static String guessContentTypeFromStream(InputStream is) throws IOException {
if (!is.markSupported()) {
return null;
}
// Look ahead up to 64 bytes for the longest encoded header
is.mark(64);
byte[] bytes = new byte[64];
int length = is.read(bytes);
is.reset();
// If there is no data from the input stream, we can't determine content type.
if (length == -1) {
return null;
}
// Check for Unicode BOM encoding indicators
String encoding = "US-ASCII";
int start = 0;
if (length > 1) {
if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE)) {
encoding = "UTF-16LE";
start = 2;
length -= length & 1;
}
if ((bytes[0] == (byte) 0xFE) && (bytes[1] == (byte) 0xFF)) {
encoding = "UTF-16BE";
start = 2;
length -= length & 1;
}
if (length > 2) {
if ((bytes[0] == (byte) 0xEF) && (bytes[1] == (byte) 0xBB)
&& (bytes[2] == (byte) 0xBF)) {
encoding = "UTF-8";
start = 3;
}
if (length > 3) {
if ((bytes[0] == (byte) 0x00) && (bytes[1] == (byte) 0x00)
&& (bytes[2] == (byte) 0xFE)
&& (bytes[3] == (byte) 0xFF)) {
encoding = "UTF-32BE";
start = 4;
length -= length & 3;
}
if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE)
&& (bytes[2] == (byte) 0x00)
&& (bytes[3] == (byte) 0x00)) {
encoding = "UTF-32LE";
start = 4;
length -= length & 3;
}
}
}
}
String header = new String(bytes, start, length - start, encoding);
// Check binary types
if (header.startsWith("PK")) {
return "application/zip";
}
if (header.startsWith("GI")) {
return "image/gif";
}
// Check text types
String textHeader = header.trim().toUpperCase(Locale.US);
if (textHeader.startsWith("Warning: if the hostname resolves to multiple IP
* addresses, this client will try each in RFC 3484 order. If
* connecting to each of these addresses fails, multiple timeouts will
* elapse before the connect attempt throws an exception. Host names that
* support both IPv6 and IPv4 always have at least 2 IP addresses.
*
* @throws IllegalArgumentException if {@code timeoutMillis < 0}.
*/
public void setConnectTimeout(int timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis < 0");
}
this.connectTimeout = timeoutMillis;
}
/**
* Returns the connect timeout in milliseconds. (See {#setConnectTimeout}.)
*/
public int getConnectTimeout() {
return connectTimeout;
}
/**
* Sets the maximum time to wait for an input stream read to complete before
* giving up. Reading will fail with a {@link SocketTimeoutException} if the
* timeout elapses before data becomes available. The default value of
* {@code 0} disables read timeouts; read attempts will block indefinitely.
*
* @param timeoutMillis the read timeout in milliseconds. Non-negative.
*/
public void setReadTimeout(int timeoutMillis) {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis < 0");
}
this.readTimeout = timeoutMillis;
}
/**
* Returns the read timeout in milliseconds, or {@code 0} if reads never
* timeout.
*/
public int getReadTimeout() {
return readTimeout;
}
/**
* Returns the string representation containing the name of this class and
* the URL.
*
* @return the string representation of this {@code URLConnection} instance.
*/
@Override
public String toString() {
return getClass().getName() + ":" + url.toString();
}
static class DefaultContentHandler extends java.net.ContentHandler {
@Override
public Object getContent(URLConnection u) throws IOException {
return u.getInputStream();
}
}
}