diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java b/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java
new file mode 100644
index 00000000..ba670646
--- /dev/null
+++ b/common/common-io/src/main/java/com/twelvemonkeys/net/HTTPUtil.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2013, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.net;
+
+import com.twelvemonkeys.lang.DateUtil;
+import com.twelvemonkeys.lang.StringUtil;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * HTTPUtil
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: HTTPUtil.java,v 1.0 08.09.13 13:57 haraldk Exp$
+ */
+public class HTTPUtil {
+ /**
+ * RFC 1123 date format, as recommended 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, (almost) 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, (almost) 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);
+ }
+ }
+ }
+
+ /**
+ * 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 {@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 RFC 2616 (HTTP/1.1), sec. 3.3.
+ *
+ * @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.
+ *
+ * Use this method with headers that contain dates, such as
+ * {@code If-Modified-Since} or {@code 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 {@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;
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java b/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java
new file mode 100644
index 00000000..9bb83f53
--- /dev/null
+++ b/common/common-io/src/test/java/com/twelvemonkeys/net/HTTPUtilTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2013, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.net;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * HTTPUtilTest
+ *
+ * @author Harald Kuhr
+ * @author last modified by $Author: haraldk$
+ * @version $Id: HTTPUtilTest.java,v 1.0 08.09.13 13:57 haraldk Exp$
+ */
+public class HTTPUtilTest {
+ @Test
+ public void testParseHTTPDateRFC1123() {
+ long time = HTTPUtil.parseHTTPDate("Sun, 06 Nov 1994 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+
+ time = HTTPUtil.parseHTTPDate("Sunday, 06 Nov 1994 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+ }
+
+ @Test
+ public void testParseHTTPDateRFC850() {
+ long time = HTTPUtil.parseHTTPDate("Sunday, 06-Nov-1994 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+
+ time = HTTPUtil.parseHTTPDate("Sun, 06-Nov-94 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+
+ // NOTE: This test will fail some time, around 2044,
+ // as the 50 year window will slide...
+ time = HTTPUtil.parseHTTPDate("Sunday, 06-Nov-94 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+
+ time = HTTPUtil.parseHTTPDate("Sun, 06-Nov-94 08:49:37 GMT");
+ assertEquals(784111777000l, time);
+ }
+
+ @Test
+ public void testParseHTTPDateAsctime() {
+ long time = HTTPUtil.parseHTTPDate("Sun Nov 6 08:49:37 1994");
+ assertEquals(784111777000l, time);
+
+ time = HTTPUtil.parseHTTPDate("Sun Nov 6 08:49:37 94");
+ assertEquals(784111777000l, time);
+ }
+
+ @Test
+ public void testFormatHTTPDateRFC1123() {
+ long time = 784111777000l;
+ assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", HTTPUtil.formatHTTPDate(time));
+ }
+}
diff --git a/common/common-io/src/test/java/com/twelvemonkeys/net/NetUtilTestCase.java b/common/common-io/src/test/java/com/twelvemonkeys/net/NetUtilTestCase.java
deleted file mode 100755
index f4875444..00000000
--- a/common/common-io/src/test/java/com/twelvemonkeys/net/NetUtilTestCase.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.twelvemonkeys.net;
-
-import junit.framework.TestCase;
-
-/**
- * NetUtilTestCase
- *
- *
- *
- *
- * @author Harald Kuhr
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/net/NetUtilTestCase.java#1 $
- */
-public class NetUtilTestCase extends TestCase {
- public void setUp() throws Exception {
- super.setUp();
- }
-
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
- public void testParseHTTPDateRFC1123() {
- long time = NetUtil.parseHTTPDate("Sun, 06 Nov 1994 08:49:37 GMT");
- assertEquals(784111777000l, time);
-
- time = NetUtil.parseHTTPDate("Sunday, 06 Nov 1994 08:49:37 GMT");
- assertEquals(784111777000l, time);
- }
-
- public void testParseHTTPDateRFC850() {
- long time = NetUtil.parseHTTPDate("Sunday, 06-Nov-1994 08:49:37 GMT");
- assertEquals(784111777000l, time);
-
- time = NetUtil.parseHTTPDate("Sun, 06-Nov-94 08:49:37 GMT");
- assertEquals(784111777000l, time);
-
- // NOTE: This test will fail some time, around 2044,
- // as the 50 year window will slide...
- time = NetUtil.parseHTTPDate("Sunday, 06-Nov-94 08:49:37 GMT");
- assertEquals(784111777000l, time);
-
- time = NetUtil.parseHTTPDate("Sun, 06-Nov-94 08:49:37 GMT");
- assertEquals(784111777000l, time);
- }
-
- public void testParseHTTPDateAsctime() {
- long time = NetUtil.parseHTTPDate("Sun Nov 6 08:49:37 1994");
- assertEquals(784111777000l, time);
-
- time = NetUtil.parseHTTPDate("Sun Nov 6 08:49:37 94");
- assertEquals(784111777000l, time);
- }
-
- public void testFormatHTTPDateRFC1123() {
- long time = 784111777000l;
- assertEquals("Sun, 06 Nov 1994 08:49:37 GMT", NetUtil.formatHTTPDate(time));
- }
-}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java
similarity index 97%
rename from common/common-io/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java
rename to sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java
index 6a3f993f..21171d22 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java
+++ b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/AuthenticatorFilter.java
@@ -1,46 +1,45 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.net;
-
-import java.net.*;
-
-/**
- * Interface for filtering Authenticator requests, used by the
- * SimpleAuthenticator.
- *
- * @see SimpleAuthenticator
- * @see java.net.Authenticator
- *
- * @author Harald Kuhr
- * @version 1.0
- */
-public interface AuthenticatorFilter {
- public boolean accept(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme);
-
-}
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.net;
+
+import java.net.*;
+
+/**
+ * Interface for filtering Authenticator requests, used by the
+ * SimpleAuthenticator.
+ *
+ * @see SimpleAuthenticator
+ * @see java.net.Authenticator
+ *
+ * @author Harald Kuhr
+ * @version 1.0
+ */
+public interface AuthenticatorFilter {
+ public boolean accept(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme);
+}
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/BASE64.java b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/BASE64.java
similarity index 97%
rename from common/common-io/src/main/java/com/twelvemonkeys/net/BASE64.java
rename to sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/BASE64.java
index 0ebbd67d..4d4346f8 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/BASE64.java
+++ b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/BASE64.java
@@ -1,144 +1,143 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.net;
-
-import com.twelvemonkeys.io.*;
-import com.twelvemonkeys.io.enc.Base64Decoder;
-import com.twelvemonkeys.io.enc.DecoderStream;
-
-import java.io.*;
-
-
-/**
- * This class does BASE64 encoding (and decoding).
- *
- * @author unascribed
- * @author last modified by $Author: haku $
- * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/BASE64.java#1 $
- * @deprecated Use {@link com.twelvemonkeys.io.enc.Base64Encoder}/{@link Base64Decoder} instead
- */
-class BASE64 {
-
- /**
- * This array maps the characters to their 6 bit values
- */
- private final static char[] PEM_ARRAY = {
- //0 1 2 3 4 5 6 7
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
- 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
- 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
- '4', '5', '6', '7', '8', '9', '+', '/' // 7
- };
-
- /**
- * Encodes the input data using the standard base64 encoding scheme.
- *
- * @param pData the bytes to encode to base64
- * @return a string with base64 encoded data
- */
- public static String encode(byte[] pData) {
- int offset = 0;
- int len;
- StringBuilder buf = new StringBuilder();
-
- while ((pData.length - offset) > 0) {
- byte a, b, c;
- if ((pData.length - offset) > 2) {
- len = 3;
- }
- else {
- len = pData.length - offset;
- }
-
- switch (len) {
- case 1:
- a = pData[offset];
- b = 0;
- buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
- buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
- buf.append('=');
- buf.append('=');
- offset++;
- break;
- case 2:
- a = pData[offset];
- b = pData[offset + 1];
- c = 0;
- buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
- buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
- buf.append(PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
- buf.append('=');
- offset += offset + 2; // ???
- break;
- default:
- a = pData[offset];
- b = pData[offset + 1];
- c = pData[offset + 2];
- buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
- buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
- buf.append(PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
- buf.append(PEM_ARRAY[c & 0x3F]);
- offset = offset + 3;
- break;
- }
-
- }
- return buf.toString();
- }
-
- public static byte[] decode(String pData) throws IOException {
- InputStream in = new DecoderStream(new ByteArrayInputStream(pData.getBytes()), new Base64Decoder());
- ByteArrayOutputStream bytes = new FastByteArrayOutputStream(pData.length() * 3);
- FileUtil.copy(in, bytes);
-
- return bytes.toByteArray();
- }
-
- //private final static sun.misc.BASE64Decoder DECODER = new sun.misc.BASE64Decoder();
-
- public static void main(String[] pArgs) throws IOException {
- if (pArgs.length == 1) {
- System.out.println(encode(pArgs[0].getBytes()));
- }
- else
- if (pArgs.length == 2 && ("-d".equals(pArgs[0]) || "--decode".equals(pArgs[0])))
- {
- System.out.println(new String(decode(pArgs[1])));
- }
- else {
- System.err.println("BASE64 [ -d | --decode ] arg");
- System.err.println("Encodes or decodes a given string");
- System.exit(5);
- }
- }
+/*
+ * Copyright (c) 2008, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name "TwelveMonkeys" nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.twelvemonkeys.net;
+
+import com.twelvemonkeys.io.*;
+import com.twelvemonkeys.io.enc.Base64Decoder;
+import com.twelvemonkeys.io.enc.DecoderStream;
+
+import java.io.*;
+
+
+/**
+ * This class does BASE64 encoding (and decoding).
+ *
+ * @author unascribed
+ * @author last modified by $Author: haku $
+ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/BASE64.java#1 $
+ * @deprecated Use {@link com.twelvemonkeys.io.enc.Base64Encoder}/{@link Base64Decoder} instead
+ */
+class BASE64 {
+ /**
+ * This array maps the characters to their 6 bit values
+ */
+ private final static char[] PEM_ARRAY = {
+ //0 1 2 3 4 5 6 7
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
+ '4', '5', '6', '7', '8', '9', '+', '/' // 7
+ };
+
+ /**
+ * Encodes the input data using the standard base64 encoding scheme.
+ *
+ * @param pData the bytes to encode to base64
+ * @return a string with base64 encoded data
+ */
+ public static String encode(byte[] pData) {
+ int offset = 0;
+ int len;
+ StringBuilder buf = new StringBuilder();
+
+ while ((pData.length - offset) > 0) {
+ byte a, b, c;
+ if ((pData.length - offset) > 2) {
+ len = 3;
+ }
+ else {
+ len = pData.length - offset;
+ }
+
+ switch (len) {
+ case 1:
+ a = pData[offset];
+ b = 0;
+ buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
+ buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
+ buf.append('=');
+ buf.append('=');
+ offset++;
+ break;
+ case 2:
+ a = pData[offset];
+ b = pData[offset + 1];
+ c = 0;
+ buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
+ buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
+ buf.append(PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
+ buf.append('=');
+ offset += offset + 2; // ???
+ break;
+ default:
+ a = pData[offset];
+ b = pData[offset + 1];
+ c = pData[offset + 2];
+ buf.append(PEM_ARRAY[(a >>> 2) & 0x3F]);
+ buf.append(PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
+ buf.append(PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
+ buf.append(PEM_ARRAY[c & 0x3F]);
+ offset = offset + 3;
+ break;
+ }
+
+ }
+ return buf.toString();
+ }
+
+ public static byte[] decode(String pData) throws IOException {
+ InputStream in = new DecoderStream(new ByteArrayInputStream(pData.getBytes()), new Base64Decoder());
+ ByteArrayOutputStream bytes = new FastByteArrayOutputStream(pData.length() * 3);
+ FileUtil.copy(in, bytes);
+
+ return bytes.toByteArray();
+ }
+
+ //private final static sun.misc.BASE64Decoder DECODER = new sun.misc.BASE64Decoder();
+
+ public static void main(String[] pArgs) throws IOException {
+ if (pArgs.length == 1) {
+ System.out.println(encode(pArgs[0].getBytes()));
+ }
+ else
+ if (pArgs.length == 2 && ("-d".equals(pArgs[0]) || "--decode".equals(pArgs[0])))
+ {
+ System.out.println(new String(decode(pArgs[1])));
+ }
+ else {
+ System.err.println("BASE64 [ -d | --decode ] arg");
+ System.err.println("Encodes or decodes a given string");
+ System.exit(5);
+ }
+ }
}
\ No newline at end of file
diff --git a/common/common-io/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java
similarity index 97%
rename from common/common-io/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java
rename to sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java
index bd337235..62119c2b 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java
+++ b/sandbox/sandbox-common/src/main/java/com/twelvemonkeys/net/HttpURLConnection.java
@@ -1,1102 +1,1101 @@
-/*
- * Copyright (c) 2008, Harald Kuhr
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name "TwelveMonkeys" nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.twelvemonkeys.net;
-
-import com.twelvemonkeys.lang.StringUtil;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-/**
- * A URLConnection with support for HTTP-specific features. See
- * the spec for details.
- * This version also supports read and connect timeouts, making it more useful
- * for clients with limitted time.
- *
- * Note that the timeouts are created on the socket level, and that
- *
- * Note: This class should now work as expected, but it need more testing before
- * it can enter production release.
- *
- * --.k
- *
- * @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/HttpURLConnection.java#1 $
- * @todo Write JUnit TestCase
- * @todo ConnectionMananger!
- * @see RFC 2616
- */
-public class HttpURLConnection extends java.net.HttpURLConnection {
-
- /**
- * HTTP Status-Code 307: Temporary Redirect
- */
- public final static int HTTP_REDIRECT = 307;
- private final static int HTTP_DEFAULT_PORT = 80;
- private final static String HTTP_HEADER_END = "\r\n\r\n";
- private static final String HEADER_WWW_AUTH = "WWW-Authenticate";
- private final static int BUF_SIZE = 8192;
- private int maxRedirects = (System.getProperty("http.maxRedirects") != null)
- ? Integer.parseInt(System.getProperty("http.maxRedirects"))
- : 20;
- protected int timeout = -1;
- protected int connectTimeout = -1;
- private Socket socket = null;
- protected InputStream errorStream = null;
- protected InputStream inputStream = null;
- protected OutputStream outputStream = null;
- private String[] responseHeaders = null;
- protected Properties responseHeaderFields = null;
- protected Properties requestProperties = new Properties();
-
- /**
- * Creates a HttpURLConnection.
- *
- * @param pURL the URL to connect to.
- */
- protected HttpURLConnection(URL pURL) {
- this(pURL, 0, 0);
- }
-
- /**
- * Creates a HttpURLConnection with a given read and connect timeout.
- * A timeout value of zero is interpreted as an
- * infinite timeout.
- *
- * @param pURL the URL to connect to.
- * @param pTimeout the maximum time the socket will block for read
- * and connect operations.
- */
- protected HttpURLConnection(URL pURL, int pTimeout) {
- this(pURL, pTimeout, pTimeout);
- }
-
- /**
- * Creates a HttpURLConnection with a given read and connect timeout.
- * A timeout value of zero is interpreted as an
- * infinite timeout.
- *
- * @param pURL the URL to connect to.
- * @param pTimeout the maximum time the socket will block for read
- * operations.
- * @param pConnectTimeout the maximum time the socket will block for
- * connection.
- */
- protected HttpURLConnection(URL pURL, int pTimeout, int pConnectTimeout) {
- super(pURL);
- setTimeout(pTimeout);
- connectTimeout = pConnectTimeout;
- }
-
- /**
- * Sets the general request property. If a property with the key already
- * exists, overwrite its value with the new value.
- *
- *
NOTE: HTTP requires all request properties which can
- * legally have multiple instances with the same key
- * to use a comma-seperated list syntax which enables multiple
- * properties to be appended into a single property.
- *
- * @param pKey the keyword by which the request is known
- * (e.g., "{@code accept}").
- * @param pValue the value associated with it.
- * @see #getRequestProperty(java.lang.String)
- */
- public void setRequestProperty(String pKey, String pValue) {
- if (connected) {
- throw new IllegalAccessError("Already connected");
- }
- String oldValue = requestProperties.getProperty(pKey);
-
- if (oldValue == null) {
- requestProperties.setProperty(pKey, pValue);
- }
- else {
- requestProperties.setProperty(pKey, oldValue + ", " + pValue);
- }
- }
-
- /**
- * Returns the value of the named general request property for this
- * connection.
- *
- * @param pKey the keyword by which the request is known (e.g., "accept").
- * @return the value of the named general request property for this
- * connection.
- * @see #setRequestProperty(java.lang.String, java.lang.String)
- */
- public String getRequestProperty(String pKey) {
- if (connected) {
- throw new IllegalAccessError("Already connected");
- }
- return requestProperties.getProperty(pKey);
- }
-
- /**
- * Gets HTTP response status from responses like:
- *
- * Extracts the ints 200 and 401 respectively.
- * Returns -1 if none can be discerned
- * from the response (i.e., the response is not valid HTTP).
- *
- *
- *
- * @return the HTTP Status-Code
- * @throws IOException if an error occurred connecting to the server.
- */
- public int getResponseCode() throws IOException {
- if (responseCode != -1) {
- return responseCode;
- }
-
- // Make sure we've gotten the headers
- getInputStream();
- String resp = getHeaderField(0);
-
- // should have no leading/trailing LWS
- // expedite the typical case by assuming it has the
- // form "HTTP/1.x 2XX "
- int ind;
-
- try {
- ind = resp.indexOf(' ');
- while (resp.charAt(ind) == ' ') {
- ind++;
- }
- responseCode = Integer.parseInt(resp.substring(ind, ind + 3));
- responseMessage = resp.substring(ind + 4).trim();
- return responseCode;
- }
- catch (Exception e) {
- return responseCode;
- }
- }
-
- /**
- * Returns the name of the specified header field.
- *
- * @param pName the name of a header field.
- * @return the value of the named header field, or {@code null}
- * if there is no such field in the header.
- */
- public String getHeaderField(String pName) {
- return responseHeaderFields.getProperty(StringUtil.toLowerCase(pName));
- }
-
- /**
- * Returns the value for the {@code n}th header field.
- * It returns {@code null} if there are fewer than
- * {@code n} fields.
- *
- * This method can be used in conjunction with the
- * {@code getHeaderFieldKey} method to iterate through all
- * the headers in the message.
- *
- * @param pIndex an index.
- * @return the value of the {@code n}th header field.
- * @see java.net.URLConnection#getHeaderFieldKey(int)
- */
- public String getHeaderField(int pIndex) {
- // TODO: getInputStream() first, to make sure we have header fields
- if (pIndex >= responseHeaders.length) {
- return null;
- }
- String field = responseHeaders[pIndex];
-
- // pIndex == 0, means the response code etc (i.e. "HTTP/1.1 200 OK").
- if ((pIndex == 0) || (field == null)) {
- return field;
- }
- int idx = field.indexOf(':');
-
- return ((idx > 0)
- ? field.substring(idx).trim()
- : ""); // TODO: "" or null?
- }
-
- /**
- * Returns the key for the {@code n}th header field.
- *
- * @param pIndex an index.
- * @return the key for the {@code n}th header field,
- * or {@code null} if there are fewer than {@code n}
- * fields.
- */
- public String getHeaderFieldKey(int pIndex) {
- // TODO: getInputStream() first, to make sure we have header fields
- if (pIndex >= responseHeaders.length) {
- return null;
- }
- String field = responseHeaders[pIndex];
-
- if (StringUtil.isEmpty(field)) {
- return null;
- }
- int idx = field.indexOf(':');
-
- return StringUtil.toLowerCase(((idx > 0)
- ? field.substring(0, idx)
- : field));
- }
-
- /**
- * Sets the read timeout for the undelying socket.
- * A timeout of zero is interpreted as an
- * infinite timeout.
- *
- * @param pTimeout the maximum time the socket will block for read
- * operations, in milliseconds.
- */
- public void setTimeout(int pTimeout) {
- if (pTimeout < 0) { // Must be positive
- throw new IllegalArgumentException("Timeout must be positive.");
- }
- timeout = pTimeout;
- if (socket != null) {
- try {
- socket.setSoTimeout(pTimeout);
- }
- catch (SocketException se) {
- // Not much to do about that...
- }
- }
- }
-
- /**
- * Gets the read timeout for the undelying socket.
- *
- * @return the maximum time the socket will block for read operations, in
- * milliseconds.
- * The default value is zero, which is interpreted as an
- * infinite timeout.
- */
- public int getTimeout() {
-
- try {
- return ((socket != null)
- ? socket.getSoTimeout()
- : timeout);
- }
- catch (SocketException se) {
- return timeout;
- }
- }
-
- /**
- * Returns an input stream that reads from this open connection.
- *
- * @return an input stream that reads from this open connection.
- * @throws IOException if an I/O error occurs while
- * creating the input stream.
- */
- public synchronized InputStream getInputStream() throws IOException {
- if (!connected) {
- connect();
- }
-
- // Nothing to return
- if (responseCode == HTTP_NOT_FOUND) {
- throw new FileNotFoundException(url.toString());
- }
- int length;
-
- if (inputStream == null) {
- return null;
- }
-
- // "De-chunk" the output stream
- else if ("chunked".equalsIgnoreCase(getHeaderField("Transfer-Encoding"))) {
- if (!(inputStream instanceof ChunkedInputStream)) {
- inputStream = new ChunkedInputStream(inputStream);
- }
- }
-
- // Make sure we don't wait forever, if the content-length is known
- else if ((length = getHeaderFieldInt("Content-Length", -1)) >= 0) {
- if (!(inputStream instanceof FixedLengthInputStream)) {
- inputStream = new FixedLengthInputStream(inputStream, length);
- }
- }
- return inputStream;
- }
-
- /**
- * Returns an output stream that writes to this connection.
- *
- * @return an output stream that writes to this connection.
- * @throws IOException if an I/O error occurs while
- * creating the output stream.
- */
- public synchronized OutputStream getOutputStream() throws IOException {
-
- if (!connected) {
- connect();
- }
- return outputStream;
- }
-
- /**
- * Indicates that other requests to the server
- * are unlikely in the near future. Calling disconnect()
- * should not imply that this HttpURLConnection
- * instance can be reused for other requests.
- */
- public void disconnect() {
- if (socket != null) {
- try {
- socket.close();
- }
- catch (IOException ioe) {
-
- // Does not matter, I guess.
- }
- socket = null;
- }
- connected = false;
- }
-
- /**
- * Internal connect method.
- */
- private void connect(final URL pURL, PasswordAuthentication pAuth, String pAuthType, int pRetries) throws IOException {
- // Find correct port
- final int port = (pURL.getPort() > 0)
- ? pURL.getPort()
- : HTTP_DEFAULT_PORT;
-
- // Create socket if we don't have one
- if (socket == null) {
- //socket = new Socket(pURL.getHost(), port); // Blocks...
- socket = createSocket(pURL, port, connectTimeout);
- socket.setSoTimeout(timeout);
- }
-
- // Get Socket output stream
- OutputStream os = socket.getOutputStream();
-
- // Connect using HTTP
- writeRequestHeaders(os, pURL, method, requestProperties, usingProxy(), pAuth, pAuthType);
-
- // Get response input stream
- InputStream sis = socket.getInputStream();
- BufferedInputStream is = new BufferedInputStream(sis);
-
- // Detatch reponse headers from reponse input stream
- InputStream header = detatchResponseHeader(is);
-
- // Parse headers and set response code/message
- responseHeaders = parseResponseHeader(header);
- responseHeaderFields = parseHeaderFields(responseHeaders);
-
- //System.err.println("Headers fields:");
- //responseHeaderFields.list(System.err);
- // Test HTTP response code, to see if further action is needed
- switch (getResponseCode()) {
- case HTTP_OK:
- // 200 OK
- inputStream = is;
- errorStream = null;
- break;
-
- /*
- case HTTP_PROXY_AUTH:
- // 407 Proxy Authentication Required
- */
- case HTTP_UNAUTHORIZED:
- // 401 Unauthorized
- // Set authorization and try again.. Slightly more compatible
- responseCode = -1;
-
- // IS THIS REDIRECTION??
- //if (instanceFollowRedirects) { ???
- String auth = getHeaderField(HEADER_WWW_AUTH);
-
- // Missing WWW-Authenticate header for 401 response is an error
- if (StringUtil.isEmpty(auth)) {
- throw new ProtocolException("Missing \"" + HEADER_WWW_AUTH + "\" header for response: 401 " + responseMessage);
- }
-
- // Get real mehtod from WWW-Authenticate header
- int SP = auth.indexOf(" ");
- String method;
- String realm = null;
-
- if (SP >= 0) {
- method = auth.substring(0, SP);
- if (auth.length() >= SP + 7) {
- realm = auth.substring(SP + 7); // " realm=".lenght() == 7
- }
-
- // else no realm
- }
- else {
- // Default mehtod is Basic
- method = SimpleAuthenticator.BASIC;
- }
-
- // Get PasswordAuthentication
- PasswordAuthentication pa = Authenticator.requestPasswordAuthentication(NetUtil.createInetAddressFromURL(pURL), port,
- pURL.getProtocol(), realm, method);
-
- // Avoid infinite loop
- if (pRetries++ <= 0) {
- throw new ProtocolException("Server redirected too many times (" + maxRedirects + ") (Authentication required: " + auth + ")"); // This is what sun.net.www.protocol.http.HttpURLConnection does
- }
- else if (pa != null) {
- connect(pURL, pa, method, pRetries);
- }
- break;
- case HTTP_MOVED_PERM:
- // 301 Moved Permanently
- case HTTP_MOVED_TEMP:
- // 302 Found
- case HTTP_SEE_OTHER:
- // 303 See Other
- /*
- case HTTP_USE_PROXY:
- // 305 Use Proxy
- // How do we handle this?
- */
- case HTTP_REDIRECT:
- // 307 Temporary Redirect
- //System.err.println("Redirecting " + getResponseCode());
- if (instanceFollowRedirects) {
- // Redirect
- responseCode = -1; // Because of the java.net.URLConnection
-
- // getResponseCode implementation...
- // ---
- // I think redirects must be get?
- //setRequestMethod("GET");
- // ---
- String location = getHeaderField("Location");
- URL newLoc = new URL(pURL, location);
-
- // Test if we can reuse the Socket
- if (!(newLoc.getAuthority().equals(pURL.getAuthority()) && (newLoc.getPort() == pURL.getPort()))) {
- socket.close(); // Close the socket, won't need it anymore
- socket = null;
- }
- if (location != null) {
- //System.err.println("Redirecting to " + location);
- // Avoid infinite loop
- if (--pRetries <= 0) {
- throw new ProtocolException("Server redirected too many times (5)");
- }
- else {
- connect(newLoc, pAuth, pAuthType, pRetries);
- }
- }
- break;
- }
-
- // ...else, fall through default (if no Location: header)
- default :
- // Not 200 OK, or any of the redirect responses
- // Probably an error...
- errorStream = is;
- inputStream = null;
- }
-
- // --- Need rethinking...
- // No further questions, let the Socket wait forever (until the server
- // closes the connection)
- //socket.setSoTimeout(0);
- // Probably not... The timeout should only kick if the read BLOCKS.
- // Shutdown output, meaning any writes to the outputstream below will
- // probably fail...
- //socket.shutdownOutput();
- // Not a good idea at all... POSTs need the outputstream to send the
- // form-data.
- // --- /Need rethinking.
- outputStream = os;
- }
-
- private static interface SocketConnector extends Runnable {
-
- /**
- * Method getSocket
- *
- * @return the socket
- * @throws IOException
- */
- public Socket getSocket() throws IOException;
- }
-
- /**
- * Creates a socket to the given URL and port, with the given connect
- * timeout. If the socket waits more than the given timout to connect,
- * an ConnectException is thrown.
- *
- * @param pURL the URL to connect to
- * @param pPort the port to connect to
- * @param pConnectTimeout the connect timeout
- * @return the created Socket.
- * @throws ConnectException if the connection is refused or otherwise
- * times out.
- * @throws UnknownHostException if the IP address of the host could not be
- * determined.
- * @throws IOException if an I/O error occurs when creating the socket.
- * @todo Move this code to a SocetImpl or similar?
- * @see Socket#Socket(String,int)
- */
- private Socket createSocket(final URL pURL, final int pPort, int pConnectTimeout) throws IOException {
- Socket socket;
- final Object current = this;
- SocketConnector connector;
- Thread t = new Thread(connector = new SocketConnector() {
-
- private IOException mConnectException = null;
- private Socket mLocalSocket = null;
-
- public Socket getSocket() throws IOException {
-
- if (mConnectException != null) {
- throw mConnectException;
- }
- return mLocalSocket;
- }
-
- // Run method
- public void run() {
-
- try {
- mLocalSocket = new Socket(pURL.getHost(), pPort); // Blocks...
- }
- catch (IOException ioe) {
-
- // Store this exception for later
- mConnectException = ioe;
- }
-
- // Signal that we are done
- synchronized (current) {
- current.notify();
- }
- }
- });
-
- t.start();
-
- // Wait for connect
- synchronized (this) {
- try {
-
- /// Only wait if thread is alive!
- if (t.isAlive()) {
- if (pConnectTimeout > 0) {
- wait(pConnectTimeout);
- }
- else {
- wait();
- }
- }
- }
- catch (InterruptedException ie) {
-
- // Continue excecution on interrupt? Hmmm..
- }
- }
-
- // Throw exception if the socket didn't connect fast enough
- if ((socket = connector.getSocket()) == null) {
- throw new ConnectException("Socket connect timed out!");
- }
- return socket;
- }
-
- /**
- * Opens a communications link to the resource referenced by this
- * URL, if such a connection has not already been established.
- *
- * If the {@code connect} method is called when the connection
- * has already been opened (indicated by the {@code connected}
- * field having the value {@code true}), the call is ignored.
- *
- * URLConnection objects go through two phases: first they are
- * created, then they are connected. After being created, and
- * before being connected, various options can be specified
- * (e.g., doInput and UseCaches). After connecting, it is an
- * error to try to set them. Operations that depend on being
- * connected, like getContentLength, will implicitly perform the
- * connection, if necessary.
- *
- * @throws IOException if an I/O error occurs while opening the
- * connection.
- * @see java.net.URLConnection#connected
- * @see RFC 2616
- */
- public void connect() throws IOException {
- if (connected) {
- return; // Ignore
- }
- connected = true;
- connect(url, null, null, maxRedirects);
- }
-
- /**
- * TODO: Proxy support is still missing.
- *
- * @return this method returns false, as proxy suport is not implemented.
- */
- public boolean usingProxy() {
- return false;
- }
-
- /**
- * Writes the HTTP request headers, for HTTP GET method.
- *
- * @see RFC 2616
- */
- private static void writeRequestHeaders(OutputStream pOut, URL pURL, String pMethod, Properties pProps, boolean pUsingProxy,
- PasswordAuthentication pAuth, String pAuthType) {
- PrintWriter out = new PrintWriter(pOut, true); // autoFlush
-
- if (!pUsingProxy) {
- out.println(pMethod + " " + (!StringUtil.isEmpty(pURL.getPath())
- ? pURL.getPath()
- : "/") + ((pURL.getQuery() != null)
- ? "?" + pURL.getQuery()
- : "") + " HTTP/1.1"); // HTTP/1.1
-
- // out.println("Connection: close"); // No persistent connections yet
-
- /*
- System.err.println(pMethod + " "
- + (!StringUtil.isEmpty(pURL.getPath()) ? pURL.getPath() : "/")
- + (pURL.getQuery() != null ? "?" + pURL.getQuery() : "")
- + " HTTP/1.1"); // HTTP/1.1
- */
-
- // Authority (Host: HTTP/1.1 field, but seems to work for HTTP/1.0)
- out.println("Host: " + pURL.getHost() + ((pURL.getPort() != -1)
- ? ":" + pURL.getPort()
- : ""));
-
- /*
- System.err.println("Host: " + pURL.getHost()
- + (pURL.getPort() != -1 ? ":" + pURL.getPort() : ""));
- */
- }
- else {
-
- ////-- PROXY (absolute) VERSION
- out.println(pMethod + " " + pURL.getProtocol() + "://" + pURL.getHost() + ((pURL.getPort() != -1)
- ? ":" + pURL.getPort()
- : "") + pURL.getPath() + ((pURL.getQuery() != null)
- ? "?" + pURL.getQuery()
- : "") + " HTTP/1.1");
- }
-
- // Check if we have authentication
- if (pAuth != null) {
-
- // If found, set Authorization header
- byte[] userPass = (pAuth.getUserName() + ":" + new String(pAuth.getPassword())).getBytes();
-
- // "Authorization" ":" credentials
- out.println("Authorization: " + pAuthType + " " + BASE64.encode(userPass));
-
- /*
- System.err.println("Authorization: " + pAuthType + " "
- + BASE64.encode(userPass));
- */
- }
-
- // Iterate over properties
-
- for (Map.Entry