All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twelvemonkeys.net.NetUtil Maven / Gradle / Ivy

There is a newer version: 2.3
Show newest version
package com.twelvemonkeys.net;

import com.twelvemonkeys.io.FileUtil;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.DateUtil;
import com.twelvemonkeys.util.BASE64;
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 net related methods.
 *
 * @author Harald Kuhr
 * @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#1 $
 */
public final class NetUtil {

    private final static String VERSION_ID = "NetUtil/1.12";
    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.
     */
    public static void main(String[] pArgs) throws MalformedURLException, 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 []] [-d|--debugheaders] [-h|--header 
] [-p|--postdata ] [-u|--putdata ] [-r|--requestProperties ] [-t|--timeout ] [-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 (int i = 0; i < headerPairs.length; i++) { String headerPair = headerPairs[i]; 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. *

* NOTE: If you supply a username and password for HTTP * authentication, this method uses the java.net.Authenticator's static * 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. * * @param pURL A String containing the URL, on the form * [http://][:@]servername[/file.ext] * 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 MalformedURLException, FileNotFoundException, 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 FileNotFoundException, IOException { return getBytesHttp(pURL, 0); } /** * Gets the InputStream from a given URL. Supports basic HTTP * authentication, using a URL string similar to most browsers. *

* NOTE: If you supply a username and password for HTTP * authentication, this method uses the java.net.Authenticator's static * 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. * * @param pURL A String containing the URL, on the form * [http://][:@]servername[/file.ext] * 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 MalformedURLException, FileNotFoundException, 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 FileNotFoundException, 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. *

* 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 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. *
*
* * @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 RFC 2616 */ public static InputStream getInputStreamHttp(String pURL, int pTimeout) throws MalformedURLException, UnknownHostException, 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. *

* 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 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. *
*
* * @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 RFC 2616 */ public static InputStream getInputStreamHttp(String pURL, Properties pProperties, boolean pFollowRedirects, int pTimeout) throws MalformedURLException, UnknownHostException, IOException { // Make sure we have properties pProperties = pProperties != null ? pProperties : new Properties(); //URL url = getURLAndRegisterPassword(pURL); URL url = getURLAndSetAuthorization(pURL, pProperties); //unregisterPassword(url); return getInputStreamHttp(url, pProperties, pFollowRedirects, pTimeout); } /** * Registers the password from the URL string, and returns the URL object. */ private static URL getURLAndSetAuthorization(String pURL, Properties pProperties) 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()); } } // Get authorization part int atIdx = pURL.indexOf("@"); if (atIdx >= 0) { userPass = pURL.substring(0, atIdx); pURL = pURL.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 + pURL); } /** * 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. *

* 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 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. *
*
* * @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 RFC 2616 */ public static InputStream getInputStreamHttp(URL pURL, int pTimeout) throws UnknownHostException, 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. *

* 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 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. *
*
* * @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 RFC 2616 */ public static InputStream getInputStreamHttp(URL pURL, Properties pProperties, boolean pFollowRedirects, int pTimeout) throws UnknownHostException, 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. *

* 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 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. *
*
* * @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 UnknownHostException, 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. *

* 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 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. *
*
* * @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 UnknownHostException, 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 * @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 UnknownHostException, 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) { Iterator props = pProperties.entrySet().iterator(); while (props.hasNext()) { Map.Entry entry = (Map.Entry) props.next(); // 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 (java.lang.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. *

* 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 read methods called * on the returned InputStream, will block only for the specified timeout. * If the timeout expires, a java.io.InterruptedIOException is raised. *
*
* * @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 RFC 2616 */ public static byte[] getBytesHttp(String pURL, int pTimeout) throws MalformedURLException, UnknownHostException, 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. *

* 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 read methods called * on the returned InputStream, will block only for the specified timeout. * If the timeout expires, a java.io.InterruptedIOException is raised. *
*
* * @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 RFC 2616 */ public static byte[] getBytesHttp(URL pURL, int pTimeout) throws UnknownHostException, 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 * java.net.Authenticator, or the default implementation * (com.twelvemonkeys.net.SimpleAuthenticator). *

* 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 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 * 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 * 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 MalformedURLException, FileNotFoundException, 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 RFC 2616 (HTTP/1.1), sec. 3.3. * * @param pTime the time * @return a 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 RFC 2616 (HTTP/1.1), sec. 3.3. * * @param pTime the time * @return a 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 long representing milliseconds * since January 1, 1970 GMT. *

* Use this method with headers that contain dates, such as * If-Modified-Since or Last-Modified. *

* The date string may be in either RFC 1123, RFC 850 or ANSI C asctime() * format, as described in * RFC 2616 (HTTP/1.1), sec. 3.3 * * @param pDate the date to parse * * @return a 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 null */ public static long parseHTTPDate(String pDate) throws NumberFormatException { return parseHTTPDateImpl(pDate).getTime(); } /** * ParseHTTPDate implementation * * @param pDate * * @return a Date * @throws NumberFormatException if the date parameter is not parseable. * @throws IllegalArgumentException if the date paramter is null */ private static Date parseHTTPDateImpl(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 { 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; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy