mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-24 00:00:01 -04:00
1422 lines
56 KiB
Java
Executable File
1422 lines
56 KiB
Java
Executable File
package com.twelvemonkeys.net;
|
|
|
|
import com.twelvemonkeys.io.FileUtil;
|
|
import com.twelvemonkeys.lang.StringUtil;
|
|
import com.twelvemonkeys.lang.DateUtil;
|
|
import com.twelvemonkeys.util.CollectionUtil;
|
|
|
|
import java.io.*;
|
|
import java.net.*;
|
|
import java.text.DateFormat;
|
|
import java.text.ParseException;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Utility class with network related methods.
|
|
*
|
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
* @author last modified by $Author: haku $
|
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/NetUtil.java#2 $
|
|
*/
|
|
public final class NetUtil {
|
|
|
|
private final static String VERSION_ID = "NetUtil/2.1";
|
|
|
|
private static Authenticator sAuthenticator = null;
|
|
|
|
private final static int BUF_SIZE = 8192;
|
|
private final static String HTTP = "http://";
|
|
private final static String HTTPS = "https://";
|
|
|
|
/**
|
|
* Field HTTP_PROTOCOL
|
|
*/
|
|
public final static String HTTP_PROTOCOL = "http";
|
|
|
|
/**
|
|
* Field HTTPS_PROTOCOL
|
|
*/
|
|
public final static String HTTPS_PROTOCOL = "https";
|
|
|
|
/**
|
|
* Field HTTP_GET
|
|
*/
|
|
public final static String HTTP_GET = "GET";
|
|
|
|
/**
|
|
* Field HTTP_POST
|
|
*/
|
|
public final static String HTTP_POST = "POST";
|
|
|
|
/**
|
|
* Field HTTP_HEAD
|
|
*/
|
|
public final static String HTTP_HEAD = "HEAD";
|
|
|
|
/**
|
|
* Field HTTP_OPTIONS
|
|
*/
|
|
public final static String HTTP_OPTIONS = "OPTIONS";
|
|
|
|
/**
|
|
* Field HTTP_PUT
|
|
*/
|
|
public final static String HTTP_PUT = "PUT";
|
|
|
|
/**
|
|
* Field HTTP_DELETE
|
|
*/
|
|
public final static String HTTP_DELETE = "DELETE";
|
|
|
|
/**
|
|
* Field HTTP_TRACE
|
|
*/
|
|
public final static String HTTP_TRACE = "TRACE";
|
|
|
|
/**
|
|
* RFC 1123 date format, as reccomended by RFC 2616 (HTTP/1.1), sec 3.3
|
|
* NOTE: All date formats are private, to ensure synchronized access.
|
|
*/
|
|
private static final SimpleDateFormat HTTP_RFC1123_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
|
|
static {
|
|
HTTP_RFC1123_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
}
|
|
|
|
/**
|
|
* RFC 850 date format, (allmost) as described in RFC 2616 (HTTP/1.1), sec 3.3
|
|
* USE FOR PARSING ONLY (format is not 100% correct, to be more robust).
|
|
*/
|
|
private static final SimpleDateFormat HTTP_RFC850_FORMAT = new SimpleDateFormat("EEE, dd-MMM-yy HH:mm:ss z", Locale.US);
|
|
/**
|
|
* ANSI C asctime() date format, (allmost) as described in RFC 2616 (HTTP/1.1), sec 3.3.
|
|
* USE FOR PARSING ONLY (format is not 100% correct, to be more robust).
|
|
*/
|
|
private static final SimpleDateFormat HTTP_ASCTIME_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss yy", Locale.US);
|
|
|
|
private static long sNext50YearWindowChange = DateUtil.currentTimeDay();
|
|
static {
|
|
HTTP_RFC850_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
HTTP_ASCTIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
|
|
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.3:
|
|
// - HTTP/1.1 clients and caches SHOULD assume that an RFC-850 date
|
|
// which appears to be more than 50 years in the future is in fact
|
|
// in the past (this helps solve the "year 2000" problem).
|
|
update50YearWindowIfNeeded();
|
|
}
|
|
|
|
private static void update50YearWindowIfNeeded() {
|
|
// Avoid class synchronization
|
|
long next = sNext50YearWindowChange;
|
|
|
|
if (next < System.currentTimeMillis()) {
|
|
// Next check in one day
|
|
next += DateUtil.DAY;
|
|
sNext50YearWindowChange = next;
|
|
|
|
Date startDate = new Date(next - (50l * DateUtil.CALENDAR_YEAR));
|
|
//System.out.println("next test: " + new Date(next) + ", 50 year start: " + startDate);
|
|
synchronized (HTTP_RFC850_FORMAT) {
|
|
HTTP_RFC850_FORMAT.set2DigitYearStart(startDate);
|
|
}
|
|
synchronized (HTTP_ASCTIME_FORMAT) {
|
|
HTTP_ASCTIME_FORMAT.set2DigitYearStart(startDate);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a NetUtil.
|
|
* This class has only static methods and members, and should not be
|
|
* instantiated.
|
|
*/
|
|
private NetUtil() {
|
|
}
|
|
|
|
public static void main1(String[] args) {
|
|
String timeStr = (args.length > 0 && !StringUtil.isNumber(args[0])) ? args[0] : null;
|
|
|
|
long time = args.length > 0 ?
|
|
(timeStr != null ? parseHTTPDate(timeStr) : Long.parseLong(args[0]))
|
|
: System.currentTimeMillis();
|
|
System.out.println(timeStr + " --> " + time + " --> " + formatHTTPDate(time));
|
|
}
|
|
|
|
/**
|
|
* Main method, reads data from a URL and, optionally, writes it to stdout or a file.
|
|
* @param pArgs command line arguemnts
|
|
* @throws java.io.IOException if an I/O exception occurs
|
|
*/
|
|
public static void main(String[] pArgs) throws IOException {
|
|
// params:
|
|
int timeout = 0;
|
|
boolean followRedirects = true;
|
|
boolean debugHeaders = false;
|
|
String requestPropertiesFile = null;
|
|
String requestHeaders = null;
|
|
String postData = null;
|
|
File putData = null;
|
|
int argIdx = 0;
|
|
boolean errArgs = false;
|
|
boolean writeToFile = false;
|
|
boolean writeToStdOut = false;
|
|
String outFileName = null;
|
|
|
|
while ((argIdx < pArgs.length) && (pArgs[argIdx].charAt(0) == '-') && (pArgs[argIdx].length() >= 2)) {
|
|
if ((pArgs[argIdx].charAt(1) == 't') || pArgs[argIdx].equals("--timeout")) {
|
|
argIdx++;
|
|
try {
|
|
timeout = Integer.parseInt(pArgs[argIdx++]);
|
|
}
|
|
catch (NumberFormatException nfe) {
|
|
errArgs = true;
|
|
break;
|
|
}
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--debugheaders")) {
|
|
debugHeaders = true;
|
|
argIdx++;
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'n') || pArgs[argIdx].equals("--nofollowredirects")) {
|
|
followRedirects = false;
|
|
argIdx++;
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'r') || pArgs[argIdx].equals("--requestproperties")) {
|
|
argIdx++;
|
|
requestPropertiesFile = pArgs[argIdx++];
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--postdata")) {
|
|
argIdx++;
|
|
postData = pArgs[argIdx++];
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'u') || pArgs[argIdx].equals("--putdata")) {
|
|
argIdx++;
|
|
putData = new File(pArgs[argIdx++]);
|
|
if (!putData.exists()) {
|
|
errArgs = true;
|
|
break;
|
|
}
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--header")) {
|
|
argIdx++;
|
|
requestHeaders = pArgs[argIdx++];
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'f') || pArgs[argIdx].equals("--file")) {
|
|
argIdx++;
|
|
writeToFile = true;
|
|
|
|
// Get optional file name
|
|
if (!((argIdx >= (pArgs.length - 1)) || (pArgs[argIdx].charAt(0) == '-'))) {
|
|
outFileName = pArgs[argIdx++];
|
|
}
|
|
}
|
|
else if ((pArgs[argIdx].charAt(1) == 'o') || pArgs[argIdx].equals("--output")) {
|
|
argIdx++;
|
|
writeToStdOut = true;
|
|
}
|
|
else {
|
|
System.err.println("Unknown option \"" + pArgs[argIdx++] + "\"");
|
|
}
|
|
}
|
|
if (errArgs || (pArgs.length < (argIdx + 1))) {
|
|
System.err.println("Usage: java NetUtil [-f|--file [<file name>]] [-d|--debugheaders] [-h|--header <header data>] [-p|--postdata <URL-encoded postdata>] [-u|--putdata <file name>] [-r|--requestProperties <properties file>] [-t|--timeout <miliseconds>] [-n|--nofollowredirects] fromUrl");
|
|
System.exit(5);
|
|
}
|
|
String url = pArgs[argIdx/*++*/];
|
|
|
|
// DONE ARGS
|
|
// Get request properties
|
|
Properties requestProperties = new Properties();
|
|
|
|
if (requestPropertiesFile != null) {
|
|
|
|
// Just read, no exception handling...
|
|
requestProperties.load(new FileInputStream(new File(requestPropertiesFile)));
|
|
}
|
|
if (requestHeaders != null) {
|
|
|
|
// Get request headers
|
|
String[] headerPairs = StringUtil.toStringArray(requestHeaders, ",");
|
|
|
|
for (String headerPair : headerPairs) {
|
|
String[] pair = StringUtil.toStringArray(headerPair, ":");
|
|
String key = (pair.length > 0)
|
|
? pair[0].trim()
|
|
: null;
|
|
String value = (pair.length > 1)
|
|
? pair[1].trim()
|
|
: "";
|
|
|
|
if (key != null) {
|
|
requestProperties.setProperty(key, value);
|
|
}
|
|
}
|
|
}
|
|
java.net.HttpURLConnection conn;
|
|
|
|
// Create connection
|
|
URL reqURL = getURLAndSetAuthorization(url, requestProperties);
|
|
|
|
conn = createHttpURLConnection(reqURL, requestProperties, followRedirects, timeout);
|
|
|
|
// POST
|
|
if (postData != null) {
|
|
// HTTP POST method
|
|
conn.setRequestMethod(HTTP_POST);
|
|
|
|
// Set entity headers
|
|
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
|
conn.setRequestProperty("Content-Length", String.valueOf(postData.length()));
|
|
conn.setRequestProperty("Content-Encoding", "ISO-8859-1");
|
|
|
|
// Get outputstream (this is where the connect actually happens)
|
|
OutputStream os = conn.getOutputStream();
|
|
|
|
System.err.println("OutputStream: " + os.getClass().getName() + "@" + System.identityHashCode(os));
|
|
OutputStreamWriter writer = new OutputStreamWriter(os, "ISO-8859-1");
|
|
|
|
// Write post data to the stream
|
|
writer.write(postData);
|
|
writer.write("\r\n");
|
|
|
|
//writer.flush();
|
|
writer.close(); // Does this close the underlying stream?
|
|
}
|
|
// PUT
|
|
else if (putData != null) {
|
|
// HTTP PUT method
|
|
conn.setRequestMethod(HTTP_PUT);
|
|
|
|
// Set entity headers
|
|
//conn.setRequestProperty("Content-Type", "???");
|
|
// TODO: Set Content-Type to correct type?
|
|
// TODO: Set content-encoding? Or can binary data be sent directly?
|
|
conn.setRequestProperty("Content-Length", String.valueOf(putData.length()));
|
|
|
|
// Get outputstream (this is where the connect actually happens)
|
|
OutputStream os = conn.getOutputStream();
|
|
|
|
System.err.println("OutputStream: " + os.getClass().getName() + "@" + System.identityHashCode(os));
|
|
|
|
// Write put data to the stream
|
|
FileUtil.copy(new FileInputStream(putData), os);
|
|
|
|
os.close();
|
|
}
|
|
|
|
//
|
|
InputStream is;
|
|
|
|
if (conn.getResponseCode() == 200) {
|
|
|
|
// Connect and get stream
|
|
is = conn.getInputStream();
|
|
}
|
|
else {
|
|
is = conn.getErrorStream();
|
|
}
|
|
|
|
//
|
|
if (debugHeaders) {
|
|
System.err.println("Request (debug):");
|
|
System.err.println(conn.getClass());
|
|
System.err.println("Response (debug):");
|
|
|
|
// Headerfield 0 is response code
|
|
System.err.println(conn.getHeaderField(0));
|
|
|
|
// Loop from 1, as headerFieldKey(0) == null...
|
|
for (int i = 1; ; i++) {
|
|
String key = conn.getHeaderFieldKey(i);
|
|
|
|
// Seems to be the way to loop through them all...
|
|
if (key == null) {
|
|
break;
|
|
}
|
|
System.err.println(key + ": " + conn.getHeaderField(key));
|
|
}
|
|
}
|
|
|
|
// Create output file if specified
|
|
OutputStream os;
|
|
|
|
if (writeToFile) {
|
|
if (outFileName == null) {
|
|
outFileName = reqURL.getFile();
|
|
if (StringUtil.isEmpty(outFileName)) {
|
|
outFileName = conn.getHeaderField("Location");
|
|
if (StringUtil.isEmpty(outFileName)) {
|
|
outFileName = "index";
|
|
|
|
// Find a suitable extension
|
|
// TODO: Replace with MIME-type util with MIME/file ext mapping
|
|
String ext = conn.getContentType();
|
|
|
|
if (!StringUtil.isEmpty(ext)) {
|
|
int idx = ext.lastIndexOf('/');
|
|
|
|
if (idx >= 0) {
|
|
ext = ext.substring(idx + 1);
|
|
}
|
|
idx = ext.indexOf(';');
|
|
if (idx >= 0) {
|
|
ext = ext.substring(0, idx);
|
|
}
|
|
outFileName += "." + ext;
|
|
}
|
|
}
|
|
}
|
|
int idx = outFileName.lastIndexOf('/');
|
|
|
|
if (idx >= 0) {
|
|
outFileName = outFileName.substring(idx + 1);
|
|
}
|
|
idx = outFileName.indexOf('?');
|
|
if (idx >= 0) {
|
|
outFileName = outFileName.substring(0, idx);
|
|
}
|
|
}
|
|
File outFile = new File(outFileName);
|
|
|
|
if (!outFile.createNewFile()) {
|
|
if (outFile.exists()) {
|
|
System.err.println("Cannot write to file " + outFile.getAbsolutePath() + ", file allready exists.");
|
|
}
|
|
else {
|
|
System.err.println("Cannot write to file " + outFile.getAbsolutePath() + ", check write permissions.");
|
|
}
|
|
System.exit(5);
|
|
}
|
|
os = new FileOutputStream(outFile);
|
|
}
|
|
else if (writeToStdOut) {
|
|
os = System.out;
|
|
}
|
|
else {
|
|
os = null;
|
|
}
|
|
|
|
// Get data.
|
|
if ((writeToFile || writeToStdOut) && is != null) {
|
|
FileUtil.copy(is, os);
|
|
}
|
|
|
|
/*
|
|
Hashtable postData = new Hashtable();
|
|
postData.put("SearchText", "condition");
|
|
|
|
try {
|
|
InputStream in = getInputStreamHttpPost(pArgs[argIdx], postData,
|
|
props, true, 0);
|
|
out = new FileOutputStream(file);
|
|
FileUtil.copy(in, out);
|
|
}
|
|
catch (Exception e) {
|
|
System.err.println("Error: " + e);
|
|
e.printStackTrace(System.err);
|
|
continue;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
public static class Cookie {
|
|
String mName = null;
|
|
String mValue = null;
|
|
|
|
public Cookie(String pName, String pValue) {
|
|
mName = pName;
|
|
mValue = pValue;
|
|
}
|
|
|
|
public String toString() {
|
|
return mName + "=" + mValue;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// Just a way to set cookies..
|
|
if (pCookies != null) {
|
|
String cookieStr = "";
|
|
for (int i = 0; i < pCookies.length; i++)
|
|
cookieStr += ((i == pCookies.length) ? pCookies[i].toString()
|
|
: pCookies[i].toString() + ";");
|
|
|
|
// System.out.println("Cookie: " + cookieStr);
|
|
|
|
conn.setRequestProperty("Cookie", cookieStr);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Test if the given URL is using HTTP protocol.
|
|
*
|
|
* @param pURL the url to condition
|
|
* @return true if the protocol is HTTP.
|
|
*/
|
|
public static boolean isHttpURL(String pURL) {
|
|
return ((pURL != null) && pURL.startsWith(HTTP));
|
|
}
|
|
|
|
/**
|
|
* Test if the given URL is using HTTP protocol.
|
|
*
|
|
* @param pURL the url to condition
|
|
* @return true if the protocol is HTTP.
|
|
*/
|
|
public static boolean isHttpURL(URL pURL) {
|
|
return ((pURL != null) && pURL.getProtocol().equals("http"));
|
|
}
|
|
|
|
/**
|
|
* Gets the content from a given URL, and returns it as a byte array.
|
|
* Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>NOTE: If you supply a username and password for HTTP
|
|
* authentication, this method uses the java.net.Authenticator's static
|
|
* {@code setDefault()} method, that can only be set ONCE. This
|
|
* means that if the default Authenticator is allready set, this method
|
|
* will fail.
|
|
* It also means if any other piece of code tries to register a new default
|
|
* Authenticator within the current VM, it will fail.</SMALL>
|
|
*
|
|
* @param pURL A String containing the URL, on the form
|
|
* <CODE>[http://][<username>:<password>@]servername[/file.ext]</CODE>
|
|
* where everything in brackets are optional.
|
|
* @return a byte array with the URL contents. If an error occurs, the
|
|
* returned array may be zero-length, but not null.
|
|
* @throws MalformedURLException if the urlName parameter is not a valid
|
|
* URL. Note that the protocol cannot be anything but HTTP.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see java.net.Authenticator
|
|
* @see SimpleAuthenticator
|
|
*/
|
|
public static byte[] getBytesHttp(String pURL) throws IOException {
|
|
return getBytesHttp(pURL, 0);
|
|
}
|
|
|
|
/**
|
|
* Gets the content from a given URL, and returns it as a byte array.
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @return a byte array with the URL contents. If an error occurs, the
|
|
* returned array may be zero-length, but not null.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getBytesHttp(String)
|
|
*/
|
|
public static byte[] getBytesHttp(URL pURL) throws IOException {
|
|
return getBytesHttp(pURL, 0);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>NOTE: If you supply a username and password for HTTP
|
|
* authentication, this method uses the java.net.Authenticator's static
|
|
* {@code setDefault()} method, that can only be set ONCE. This
|
|
* means that if the default Authenticator is allready set, this method
|
|
* will fail.
|
|
* It also means if any other piece of code tries to register a new default
|
|
* Authenticator within the current VM, it will fail.</SMALL>
|
|
*
|
|
* @param pURL A String containing the URL, on the form
|
|
* <CODE>[http://][<username>:<password>@]servername[/file.ext]</CODE>
|
|
* where everything in brackets are optional.
|
|
* @return an input stream that reads from the connection created by the
|
|
* given URL.
|
|
* @throws MalformedURLException if the urlName parameter specifies an
|
|
* unknown protocol, or does not form a valid URL.
|
|
* Note that the protocol cannot be anything but HTTP.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see java.net.Authenticator
|
|
* @see SimpleAuthenticator
|
|
*/
|
|
public static InputStream getInputStreamHttp(String pURL) throws IOException {
|
|
return getInputStreamHttp(pURL, 0);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL.
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @return an input stream that reads from the connection created by the
|
|
* given URL.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getInputStreamHttp(String)
|
|
*/
|
|
public static InputStream getInputStreamHttp(URL pURL) throws IOException {
|
|
return getInputStreamHttp(pURL, 0);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws MalformedURLException if the url parameter specifies an
|
|
* unknown protocol, or does not form a valid URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getInputStreamHttp(URL,int)
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static InputStream getInputStreamHttp(String pURL, int pTimeout) throws IOException {
|
|
return getInputStreamHttp(pURL, null, true, pTimeout);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pProperties the request header properties.
|
|
* @param pFollowRedirects specifying wether redirects should be followed.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws MalformedURLException if the url parameter specifies an
|
|
* unknown protocol, or does not form a valid URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getInputStreamHttp(URL,int)
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static InputStream getInputStreamHttp(final String pURL, final Properties pProperties, final boolean pFollowRedirects, final int pTimeout)
|
|
throws IOException {
|
|
|
|
// Make sure we have properties
|
|
Properties properties = pProperties != null ? pProperties : new Properties();
|
|
|
|
//URL url = getURLAndRegisterPassword(pURL);
|
|
URL url = getURLAndSetAuthorization(pURL, properties);
|
|
|
|
//unregisterPassword(url);
|
|
return getInputStreamHttp(url, properties, pFollowRedirects, pTimeout);
|
|
}
|
|
|
|
/**
|
|
* Registers the password from the URL string, and returns the URL object.
|
|
*
|
|
* @param pURL the string representation of the URL, possibly including authorization part
|
|
* @param pProperties the
|
|
* @return the URL created from {@code pURL}.
|
|
* @throws java.net.MalformedURLException if there's a syntax error in {@code pURL}
|
|
*/
|
|
private static URL getURLAndSetAuthorization(final String pURL, final Properties pProperties) throws MalformedURLException {
|
|
String url = pURL;
|
|
// Split user/password away from url
|
|
String userPass = null;
|
|
String protocolPrefix = HTTP;
|
|
int httpIdx = url.indexOf(HTTPS);
|
|
|
|
if (httpIdx >= 0) {
|
|
protocolPrefix = HTTPS;
|
|
url = url.substring(httpIdx + HTTPS.length());
|
|
}
|
|
else {
|
|
httpIdx = url.indexOf(HTTP);
|
|
if (httpIdx >= 0) {
|
|
url = url.substring(httpIdx + HTTP.length());
|
|
}
|
|
}
|
|
|
|
// Get authorization part
|
|
int atIdx = url.indexOf("@");
|
|
|
|
if (atIdx >= 0) {
|
|
userPass = url.substring(0, atIdx);
|
|
url = url.substring(atIdx + 1);
|
|
}
|
|
|
|
// Set authorization if user/password is present
|
|
if (userPass != null) {
|
|
// System.out.println("Setting password ("+ userPass + ")!");
|
|
pProperties.setProperty("Authorization", "Basic " + BASE64.encode(userPass.getBytes()));
|
|
}
|
|
|
|
// Return URL
|
|
return new URL(protocolPrefix + url);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see com.twelvemonkeys.net.HttpURLConnection
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.net.HttpURLConnection
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static InputStream getInputStreamHttp(URL pURL, int pTimeout) throws IOException {
|
|
return getInputStreamHttp(pURL, null, true, pTimeout);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pProperties the request header properties.
|
|
* @param pFollowRedirects specifying wether redirects should be followed.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getInputStreamHttp(URL,int)
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static InputStream getInputStreamHttp(URL pURL, Properties pProperties, boolean pFollowRedirects, int pTimeout)
|
|
throws IOException {
|
|
|
|
// Open the connection, and get the stream
|
|
java.net.HttpURLConnection conn = createHttpURLConnection(pURL, pProperties, pFollowRedirects, pTimeout);
|
|
|
|
// HTTP GET method
|
|
conn.setRequestMethod(HTTP_GET);
|
|
|
|
// This is where the connect happens
|
|
InputStream is = conn.getInputStream();
|
|
|
|
// We only accept the 200 OK message
|
|
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
|
throw new IOException("The request gave the response: " + conn.getResponseCode() + ": " + conn.getResponseMessage());
|
|
}
|
|
return is;
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pPostData the post data.
|
|
* @param pProperties the request header properties.
|
|
* @param pFollowRedirects specifying wether redirects should be followed.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws MalformedURLException if the url parameter specifies an
|
|
* unknown protocol, or does not form a valid URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
*/
|
|
public static InputStream getInputStreamHttpPost(String pURL, Map pPostData, Properties pProperties, boolean pFollowRedirects, int pTimeout)
|
|
throws IOException {
|
|
|
|
pProperties = pProperties != null ? pProperties : new Properties();
|
|
|
|
//URL url = getURLAndRegisterPassword(pURL);
|
|
URL url = getURLAndSetAuthorization(pURL, pProperties);
|
|
|
|
//unregisterPassword(url);
|
|
return getInputStreamHttpPost(url, pPostData, pProperties, pFollowRedirects, pTimeout);
|
|
}
|
|
|
|
/**
|
|
* Gets the InputStream from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised. This
|
|
* might happen BEFORE OR AFTER this method returns, as the HTTP headers
|
|
* will be read and parsed from the InputStream before this method returns,
|
|
* while further read operations on the returned InputStream might be
|
|
* performed at a later stage.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pPostData the post data.
|
|
* @param pProperties the request header properties.
|
|
* @param pFollowRedirects specifying wether redirects should be followed.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
*/
|
|
public static InputStream getInputStreamHttpPost(URL pURL, Map pPostData, Properties pProperties, boolean pFollowRedirects, int pTimeout)
|
|
throws IOException {
|
|
// Open the connection, and get the stream
|
|
java.net.HttpURLConnection conn = createHttpURLConnection(pURL, pProperties, pFollowRedirects, pTimeout);
|
|
|
|
// HTTP POST method
|
|
conn.setRequestMethod(HTTP_POST);
|
|
|
|
// Iterate over and create post data string
|
|
StringBuilder postStr = new StringBuilder();
|
|
|
|
if (pPostData != null) {
|
|
Iterator data = pPostData.entrySet().iterator();
|
|
|
|
while (data.hasNext()) {
|
|
Map.Entry entry = (Map.Entry) data.next();
|
|
|
|
// Properties key/values can be safely cast to strings
|
|
// Encode the string
|
|
postStr.append(URLEncoder.encode((String) entry.getKey(), "UTF-8"));
|
|
postStr.append('=');
|
|
postStr.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
|
|
|
|
if (data.hasNext()) {
|
|
postStr.append('&');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set entity headers
|
|
String encoding = conn.getRequestProperty("Content-Encoding");
|
|
if (StringUtil.isEmpty(encoding)) {
|
|
encoding = "UTF-8";
|
|
}
|
|
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
|
conn.setRequestProperty("Content-Length", String.valueOf(postStr.length()));
|
|
conn.setRequestProperty("Content-Encoding", encoding);
|
|
|
|
// Get outputstream (this is where the connect actually happens)
|
|
OutputStream os = conn.getOutputStream();
|
|
OutputStreamWriter writer = new OutputStreamWriter(os, encoding);
|
|
|
|
// Write post data to the stream
|
|
writer.write(postStr.toString());
|
|
writer.write("\r\n");
|
|
writer.close(); // Does this close the underlying stream?
|
|
|
|
// Get the inputstream
|
|
InputStream is = conn.getInputStream();
|
|
|
|
// We only accept the 200 OK message
|
|
// TODO: Accept all 200 messages, like ACCEPTED, CREATED or NO_CONTENT?
|
|
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
|
throw new IOException("The request gave the response: " + conn.getResponseCode() + ": " + conn.getResponseMessage());
|
|
}
|
|
return is;
|
|
}
|
|
|
|
/**
|
|
* Creates a HTTP connection to the given URL.
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pProperties connection properties.
|
|
* @param pFollowRedirects specifies whether we should follow redirects.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return a HttpURLConnection
|
|
* @throws UnknownHostException if the hostname in the URL cannot be found.
|
|
* @throws IOException if an I/O exception occurs.
|
|
*/
|
|
public static java.net.HttpURLConnection createHttpURLConnection(URL pURL, Properties pProperties, boolean pFollowRedirects, int pTimeout)
|
|
throws IOException {
|
|
|
|
// Open the connection, and get the stream
|
|
java.net.HttpURLConnection conn;
|
|
|
|
if (pTimeout > 0) {
|
|
// Supports timeout
|
|
conn = new com.twelvemonkeys.net.HttpURLConnection(pURL, pTimeout);
|
|
}
|
|
else {
|
|
// Faster, more compatible
|
|
conn = (java.net.HttpURLConnection) pURL.openConnection();
|
|
}
|
|
|
|
// Set user agent
|
|
if ((pProperties == null) || !pProperties.containsKey("User-Agent")) {
|
|
conn.setRequestProperty("User-Agent",
|
|
VERSION_ID
|
|
+ " (" + System.getProperty("os.name") + "/" + System.getProperty("os.version") + "; "
|
|
+ System.getProperty("os.arch") + "; "
|
|
+ System.getProperty("java.vm.name") + "/" + System.getProperty("java.vm.version") + ")");
|
|
}
|
|
|
|
// Set request properties
|
|
if (pProperties != null) {
|
|
for (Map.Entry<Object, Object> entry : pProperties.entrySet()) {
|
|
// Properties key/values can be safely cast to strings
|
|
conn.setRequestProperty((String) entry.getKey(), entry.getValue().toString());
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Breaks with JRE1.2?
|
|
conn.setInstanceFollowRedirects(pFollowRedirects);
|
|
}
|
|
catch (LinkageError le) {
|
|
// This is the best we can do...
|
|
java.net.HttpURLConnection.setFollowRedirects(pFollowRedirects);
|
|
System.err.println("You are using an old Java Spec, consider upgrading.");
|
|
System.err.println("java.net.HttpURLConnection.setInstanceFollowRedirects(" + pFollowRedirects + ") failed.");
|
|
|
|
//le.printStackTrace(System.err);
|
|
}
|
|
|
|
conn.setDoInput(true);
|
|
conn.setDoOutput(true);
|
|
|
|
//conn.setUseCaches(true);
|
|
return conn;
|
|
}
|
|
|
|
/**
|
|
* This is a hack to get around the protected constructors in
|
|
* HttpURLConnection, should maybe consider registering and do things
|
|
* properly...
|
|
*/
|
|
|
|
/*
|
|
private static class TimedHttpURLConnection
|
|
extends com.twelvemonkeys.net.HttpURLConnection {
|
|
TimedHttpURLConnection(URL pURL, int pTimeout) {
|
|
super(pURL, pTimeout);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Gets the content from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout. Supports basic HTTP
|
|
* authentication, using a URL string similar to most browsers.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return a byte array that is read from the socket connection, created
|
|
* from the given URL.
|
|
* @throws MalformedURLException if the url parameter specifies an
|
|
* unknown protocol, or does not form a valid URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getBytesHttp(URL,int)
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static byte[] getBytesHttp(String pURL, int pTimeout) throws IOException {
|
|
// Get the input stream from the url
|
|
InputStream in = new BufferedInputStream(getInputStreamHttp(pURL, pTimeout), BUF_SIZE * 2);
|
|
|
|
// Get all the bytes in loop
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
int count;
|
|
byte[] buffer = new byte[BUF_SIZE];
|
|
|
|
try {
|
|
while ((count = in.read(buffer)) != -1) {
|
|
// NOTE: According to the J2SE API doc, read(byte[]) will read
|
|
// at least 1 byte, or return -1, if end-of-file is reached.
|
|
bytes.write(buffer, 0, count);
|
|
}
|
|
}
|
|
finally {
|
|
|
|
// Close the buffer
|
|
in.close();
|
|
}
|
|
return bytes.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Gets the content from a given URL, with the given timeout.
|
|
* The timeout must be > 0. A timeout of zero is interpreted as an
|
|
* infinite timeout.
|
|
* <P/>
|
|
* <SMALL>Implementation note: If the timeout parameter is greater than 0,
|
|
* this method uses my own implementation of
|
|
* java.net.HttpURLConnection, that uses plain sockets, to create an
|
|
* HTTP connection to the given URL. The {@code read} methods called
|
|
* on the returned InputStream, will block only for the specified timeout.
|
|
* If the timeout expires, a java.io.InterruptedIOException is raised.
|
|
* <BR/>
|
|
* </SMALL>
|
|
*
|
|
* @param pURL the URL to get.
|
|
* @param pTimeout the specified timeout, in milliseconds.
|
|
* @return an input stream that reads from the socket connection, created
|
|
* from the given URL.
|
|
* @throws UnknownHostException if the IP address for the given URL cannot
|
|
* be resolved.
|
|
* @throws FileNotFoundException if there is no file at the given URL.
|
|
* @throws IOException if an error occurs during transfer.
|
|
* @see #getInputStreamHttp(URL,int)
|
|
* @see com.twelvemonkeys.net.HttpURLConnection
|
|
* @see java.net.Socket
|
|
* @see java.net.Socket#setSoTimeout(int) setSoTimeout
|
|
* @see java.net.HttpURLConnection
|
|
* @see java.io.InterruptedIOException
|
|
* @see <A href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC 2616</A>
|
|
*/
|
|
public static byte[] getBytesHttp(URL pURL, int pTimeout) throws IOException {
|
|
// Get the input stream from the url
|
|
InputStream in = new BufferedInputStream(getInputStreamHttp(pURL, pTimeout), BUF_SIZE * 2);
|
|
|
|
// Get all the bytes in loop
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
int count;
|
|
byte[] buffer = new byte[BUF_SIZE];
|
|
|
|
try {
|
|
while ((count = in.read(buffer)) != -1) {
|
|
// NOTE: According to the J2SE API doc, read(byte[]) will read
|
|
// at least 1 byte, or return -1, if end-of-file is reached.
|
|
bytes.write(buffer, 0, count);
|
|
}
|
|
}
|
|
finally {
|
|
|
|
// Close the buffer
|
|
in.close();
|
|
}
|
|
return bytes.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Unregisters the password asscociated with this URL
|
|
*/
|
|
|
|
/*
|
|
private static void unregisterPassword(URL pURL) {
|
|
Authenticator auth = registerAuthenticator();
|
|
if (auth != null && auth instanceof SimpleAuthenticator)
|
|
((SimpleAuthenticator) auth)
|
|
.unregisterPasswordAuthentication(pURL);
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Registers the password from the URL string, and returns the URL object.
|
|
*/
|
|
|
|
/*
|
|
private static URL getURLAndRegisterPassword(String pURL)
|
|
throws MalformedURLException
|
|
{
|
|
// Split user/password away from url
|
|
String userPass = null;
|
|
String protocolPrefix = HTTP;
|
|
|
|
int httpIdx = pURL.indexOf(HTTPS);
|
|
if (httpIdx >= 0) {
|
|
protocolPrefix = HTTPS;
|
|
pURL = pURL.substring(httpIdx + HTTPS.length());
|
|
}
|
|
else {
|
|
httpIdx = pURL.indexOf(HTTP);
|
|
if (httpIdx >= 0)
|
|
pURL = pURL.substring(httpIdx + HTTP.length());
|
|
}
|
|
|
|
int atIdx = pURL.indexOf("@");
|
|
if (atIdx >= 0) {
|
|
userPass = pURL.substring(0, atIdx);
|
|
pURL = pURL.substring(atIdx + 1);
|
|
}
|
|
|
|
// Set URL
|
|
URL url = new URL(protocolPrefix + pURL);
|
|
|
|
// Set Authenticator if user/password is present
|
|
if (userPass != null) {
|
|
// System.out.println("Setting password ("+ userPass + ")!");
|
|
|
|
int colIdx = userPass.indexOf(":");
|
|
if (colIdx < 0)
|
|
throw new MalformedURLException("Error in username/password!");
|
|
|
|
String userName = userPass.substring(0, colIdx);
|
|
String passWord = userPass.substring(colIdx + 1);
|
|
|
|
// Try to register the authenticator
|
|
// System.out.println("Trying to register authenticator!");
|
|
Authenticator auth = registerAuthenticator();
|
|
|
|
// System.out.println("Got authenticator " + auth + ".");
|
|
|
|
// Register our username/password with it
|
|
if (auth != null && auth instanceof SimpleAuthenticator) {
|
|
((SimpleAuthenticator) auth)
|
|
.registerPasswordAuthentication(url,
|
|
new PasswordAuthentication(userName,
|
|
passWord.toCharArray()));
|
|
}
|
|
else {
|
|
// Not supported!
|
|
throw new RuntimeException("Could not register PasswordAuthentication");
|
|
}
|
|
}
|
|
|
|
return url;
|
|
}
|
|
*/
|
|
|
|
/**
|
|
* Registers the Authenticator given in the system property
|
|
* {@code java.net.Authenticator}, or the default implementation
|
|
* ({@code com.twelvemonkeys.net.SimpleAuthenticator}).
|
|
* <P/>
|
|
* BUG: What if authenticator has allready been set outside this class?
|
|
*
|
|
* @return The Authenticator created and set as default, or null, if it
|
|
* was not set as the default. However, there is no (clean) way to
|
|
* be sure the authenticator was set (the SimpleAuthenticator uses
|
|
* a hack to get around this), so it might be possible that the
|
|
* returned authenticator was not set as default...
|
|
* @see Authenticator#setDefault(Authenticator)
|
|
* @see SimpleAuthenticator
|
|
*/
|
|
public synchronized static Authenticator registerAuthenticator() {
|
|
if (sAuthenticator != null) {
|
|
return sAuthenticator;
|
|
}
|
|
|
|
// Get the system property
|
|
String authenticatorName = System.getProperty("java.net.Authenticator");
|
|
|
|
// Try to get the Authenticator from the system property
|
|
if (authenticatorName != null) {
|
|
try {
|
|
Class authenticatorClass = Class.forName(authenticatorName);
|
|
|
|
sAuthenticator = (Authenticator) authenticatorClass.newInstance();
|
|
}
|
|
catch (ClassNotFoundException cnfe) {
|
|
// We should maybe rethrow this?
|
|
}
|
|
catch (InstantiationException ie) {
|
|
// Ignore
|
|
}
|
|
catch (IllegalAccessException iae) {
|
|
// Ignore
|
|
}
|
|
}
|
|
|
|
// Get the default authenticator
|
|
if (sAuthenticator == null) {
|
|
sAuthenticator = SimpleAuthenticator.getInstance();
|
|
}
|
|
|
|
// Register authenticator as default
|
|
Authenticator.setDefault(sAuthenticator);
|
|
return sAuthenticator;
|
|
}
|
|
|
|
/**
|
|
* Creates the InetAddress object from the given URL.
|
|
* Equivalent to calling {@code InetAddress.getByName(URL.getHost())}
|
|
* except that it returns null, instead of throwing UnknownHostException.
|
|
*
|
|
* @param pURL the URL to look up.
|
|
* @return the createad InetAddress, or null if the host was unknown.
|
|
* @see java.net.InetAddress
|
|
* @see java.net.URL
|
|
*/
|
|
public static InetAddress createInetAddressFromURL(URL pURL) {
|
|
try {
|
|
return InetAddress.getByName(pURL.getHost());
|
|
}
|
|
catch (UnknownHostException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an URL from the given InetAddress object, using the given
|
|
* protocol.
|
|
* Equivalent to calling
|
|
* {@code new URL(protocol, InetAddress.getHostName(), "")}
|
|
* except that it returns null, instead of throwing MalformedURLException.
|
|
*
|
|
* @param pIP the IP address to look up
|
|
* @param pProtocol the protocol to use in the new URL
|
|
* @return the created URL or null, if the URL could not be created.
|
|
* @see java.net.URL
|
|
* @see java.net.InetAddress
|
|
*/
|
|
public static URL createURLFromInetAddress(InetAddress pIP, String pProtocol) {
|
|
try {
|
|
return new URL(pProtocol, pIP.getHostName(), "");
|
|
}
|
|
catch (MalformedURLException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an URL from the given InetAddress object, using HTTP protocol.
|
|
* Equivalent to calling
|
|
* {@code new URL("http", InetAddress.getHostName(), "")}
|
|
* except that it returns null, instead of throwing MalformedURLException.
|
|
*
|
|
* @param pIP the IP address to look up
|
|
* @return the created URL or null, if the URL could not be created.
|
|
* @see java.net.URL
|
|
* @see java.net.InetAddress
|
|
*/
|
|
public static URL createURLFromInetAddress(InetAddress pIP) {
|
|
return createURLFromInetAddress(pIP, HTTP);
|
|
}
|
|
|
|
/*
|
|
* TODO: Benchmark!
|
|
*/
|
|
static byte[] getBytesHttpOld(String pURL) throws IOException {
|
|
// Get the input stream from the url
|
|
InputStream in = new BufferedInputStream(getInputStreamHttp(pURL), BUF_SIZE * 2);
|
|
|
|
// Get all the bytes in loop
|
|
byte[] bytes = new byte[0];
|
|
int count;
|
|
byte[] buffer = new byte[BUF_SIZE];
|
|
|
|
try {
|
|
while ((count = in.read(buffer)) != -1) {
|
|
|
|
// NOTE: According to the J2SE API doc, read(byte[]) will read
|
|
// at least 1 byte, or return -1, if end-of-file is reached.
|
|
bytes = (byte[]) CollectionUtil.mergeArrays(bytes, 0, bytes.length, buffer, 0, count);
|
|
}
|
|
}
|
|
finally {
|
|
|
|
// Close the buffer
|
|
in.close();
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
/**
|
|
* Formats the time to a HTTP date, using the RFC 1123 format, as described
|
|
* in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3"
|
|
* >RFC 2616 (HTTP/1.1), sec. 3.3</a>.
|
|
*
|
|
* @param pTime the time
|
|
* @return a {@code String} representation of the time
|
|
*/
|
|
public static String formatHTTPDate(long pTime) {
|
|
return formatHTTPDate(new Date(pTime));
|
|
}
|
|
|
|
/**
|
|
* Formats the time to a HTTP date, using the RFC 1123 format, as described
|
|
* in <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3"
|
|
* >RFC 2616 (HTTP/1.1), sec. 3.3</a>.
|
|
*
|
|
* @param pTime the time
|
|
* @return a {@code String} representation of the time
|
|
*/
|
|
public static String formatHTTPDate(Date pTime) {
|
|
synchronized (HTTP_RFC1123_FORMAT) {
|
|
return HTTP_RFC1123_FORMAT.format(pTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses a HTTP date string into a {@code long} representing milliseconds
|
|
* since January 1, 1970 GMT.
|
|
* <p>
|
|
* Use this method with headers that contain dates, such as
|
|
* {@code If-Modified-Since} or {@code Last-Modified}.
|
|
* <p>
|
|
* The date string may be in either RFC 1123, RFC 850 or ANSI C asctime()
|
|
* format, as described in
|
|
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3"
|
|
* >RFC 2616 (HTTP/1.1), sec. 3.3</a>
|
|
*
|
|
* @param pDate the date to parse
|
|
*
|
|
* @return a {@code long} value representing the date, expressed as the
|
|
* number of milliseconds since January 1, 1970 GMT,
|
|
* @throws NumberFormatException if the date parameter is not parseable.
|
|
* @throws IllegalArgumentException if the date paramter is {@code null}
|
|
*/
|
|
public static long parseHTTPDate(String pDate) throws NumberFormatException {
|
|
return parseHTTPDateImpl(pDate).getTime();
|
|
}
|
|
|
|
/**
|
|
* ParseHTTPDate implementation
|
|
*
|
|
* @param pDate the date string to parse
|
|
*
|
|
* @return a {@code Date}
|
|
* @throws NumberFormatException if the date parameter is not parseable.
|
|
* @throws IllegalArgumentException if the date paramter is {@code null}
|
|
*/
|
|
private static Date parseHTTPDateImpl(final String pDate) throws NumberFormatException {
|
|
if (pDate == null) {
|
|
throw new IllegalArgumentException("date == null");
|
|
}
|
|
|
|
if (StringUtil.isEmpty(pDate)) {
|
|
throw new NumberFormatException("Invalid HTTP date: \"" + pDate + "\"");
|
|
}
|
|
|
|
DateFormat format;
|
|
|
|
if (pDate.indexOf('-') >= 0) {
|
|
format = HTTP_RFC850_FORMAT;
|
|
update50YearWindowIfNeeded();
|
|
}
|
|
else if (pDate.indexOf(',') < 0) {
|
|
format = HTTP_ASCTIME_FORMAT;
|
|
update50YearWindowIfNeeded();
|
|
}
|
|
else {
|
|
format = HTTP_RFC1123_FORMAT;
|
|
// NOTE: RFC1123 always uses 4-digit years
|
|
}
|
|
|
|
Date date;
|
|
try {
|
|
//noinspection SynchronizationOnLocalVariableOrMethodParameter
|
|
synchronized (format) {
|
|
date = format.parse(pDate);
|
|
}
|
|
}
|
|
catch (ParseException e) {
|
|
NumberFormatException nfe = new NumberFormatException("Invalid HTTP date: \"" + pDate + "\"");
|
|
nfe.initCause(e);
|
|
throw nfe;
|
|
}
|
|
|
|
if (date == null) {
|
|
throw new NumberFormatException("Invalid HTTP date: \"" + pDate + "\"");
|
|
}
|
|
|
|
return date;
|
|
}
|
|
} |