summaryrefslogtreecommitdiff
path: root/libjava/classpath/gnu/java/net/protocol/http
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/gnu/java/net/protocol/http')
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Authenticator.java59
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java107
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java123
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java172
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Cookie.java160
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/CookieManager.java65
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Credentials.java88
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java681
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java441
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java688
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Handler.java73
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Headers.java369
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Request.java915
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java69
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/Response.java185
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/ResponseBodyReader.java70
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java57
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java140
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/event/ConnectionEvent.java81
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/event/ConnectionListener.java58
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/event/RequestEvent.java107
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/event/RequestListener.java70
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/event/package.html46
-rw-r--r--libjava/classpath/gnu/java/net/protocol/http/package.html76
24 files changed, 4900 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java b/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java
new file mode 100644
index 00000000000..0d7c9881956
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Authenticator.java
@@ -0,0 +1,59 @@
+/* Authenticator.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Callback interface for managing authentication.
+ * @see Request#setAuthenticator
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface Authenticator
+{
+
+ /**
+ * Returns the credentials to supply for the given realm.
+ * @param realm the authentication realm
+ * @param attempt zero on first authentication attempt, increments on each
+ * unsuccessful attempt
+ */
+ Credentials getCredentials(String realm, int attempt);
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java
new file mode 100644
index 00000000000..35ad2bccf45
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java
@@ -0,0 +1,107 @@
+/* ByteArrayRequestBodyWriter.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * A simple request body writer using a byte array.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class ByteArrayRequestBodyWriter
+ implements RequestBodyWriter
+{
+
+ /**
+ * The content.
+ */
+ protected byte[] content;
+
+ /**
+ * The position within the content at which the next read will occur.
+ */
+ protected int pos;
+
+ /**
+ * Constructs a new byte array request body writer with the specified
+ * content.
+ * @param content the content buffer
+ */
+ public ByteArrayRequestBodyWriter(byte[] content)
+ {
+ this.content = content;
+ pos = 0;
+ }
+
+ /**
+ * Returns the total number of bytes that will be written in a single pass
+ * by this writer.
+ */
+ public int getContentLength()
+ {
+ return content.length;
+ }
+
+ /**
+ * Initialises the writer.
+ * This will be called before each pass.
+ */
+ public void reset()
+ {
+ pos = 0;
+ }
+
+ /**
+ * Writes body content to the supplied buffer.
+ * @param buffer the content buffer
+ * @return the number of bytes written
+ */
+ public int write(byte[] buffer)
+ {
+ int len = content.length - pos;
+ len = (buffer.length < len) ? buffer.length : len;
+ if (len > -1)
+ {
+ System.arraycopy(content, pos, buffer, 0, len);
+ pos += len;
+ }
+ return len;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java
new file mode 100644
index 00000000000..680e45d3e49
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java
@@ -0,0 +1,123 @@
+/* Authenticator.java --ByteArrayResponseBodyReader.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Simple response body reader that stores content in a byte array.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class ByteArrayResponseBodyReader
+ implements ResponseBodyReader
+{
+
+ /**
+ * The content.
+ */
+ protected byte[] content;
+
+ /**
+ * The position in the content at which the next write will occur.
+ */
+ protected int pos;
+
+ /**
+ * The length of the buffer.
+ */
+ protected int len;
+
+ /**
+ * Constructs a new byte array response body reader.
+ */
+ public ByteArrayResponseBodyReader()
+ {
+ this(4096);
+ }
+
+ /**
+ * Constructs a new byte array response body reader with the specified
+ * initial buffer size.
+ * @param size the initial buffer size
+ */
+ public ByteArrayResponseBodyReader(int size)
+ {
+ content = new byte[size];
+ pos = len = 0;
+ }
+
+ /**
+ * This reader accepts all responses.
+ */
+ public boolean accept(Request request, Response response)
+ {
+ return true;
+ }
+
+ public void read(byte[] buffer, int offset, int length)
+ {
+ int l = length - offset;
+ if (pos + l > content.length)
+ {
+ byte[] tmp = new byte[content.length * 2];
+ System.arraycopy(content, 0, tmp, 0, pos);
+ content = tmp;
+ }
+ System.arraycopy(buffer, offset, content, pos, l);
+ pos += l;
+ len = pos;
+ }
+
+ public void close()
+ {
+ pos = 0;
+ }
+
+ /**
+ * Retrieves the content of this reader as a byte array.
+ * The size of the returned array is the number of bytes read.
+ */
+ public byte[] toByteArray()
+ {
+ byte[] ret = new byte[len];
+ System.arraycopy(content, 0, ret, 0, len);
+ return ret;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java
new file mode 100644
index 00000000000..c0706b70840
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/ChunkedInputStream.java
@@ -0,0 +1,172 @@
+/* ChunkedInputStream.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.ProtocolException;
+
+/**
+ * Input stream wrapper for the "chunked" transfer-coding.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class ChunkedInputStream
+ extends FilterInputStream
+{
+
+ private static final byte CR = 0x0d;
+ private static final byte LF = 0x0a;
+
+ int size;
+ int count;
+ boolean meta;
+ boolean eof;
+ Headers headers;
+
+ /**
+ * Constructor.
+ * @param in the response socket input stream
+ * @param headers the headers to receive additional header lines
+ */
+ public ChunkedInputStream(InputStream in, Headers headers)
+ {
+ super(in);
+ this.headers = headers;
+ size = -1;
+ count = 0;
+ meta = true;
+ }
+
+ public int read()
+ throws IOException
+ {
+ byte[] buf = new byte[1];
+ int len = read(buf, 0, 1);
+ if (len == -1)
+ {
+ return -1;
+ }
+ int ret = (int) buf[0];
+ if (ret < 0)
+ {
+ ret += 0x100;
+ }
+ return ret;
+ }
+
+ public int read(byte[] buffer)
+ throws IOException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+ public int read(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ if (eof)
+ {
+ return -1;
+ }
+ if (meta)
+ {
+ // Read chunk header
+ int c, last = 0;
+ boolean seenSemi = false;
+ StringBuffer buf = new StringBuffer();
+ do
+ {
+ c = in.read();
+ if (c == 0x3b) // ;
+ {
+ seenSemi = true;
+ }
+ else if (c == 0x0a && last == 0x0d) // CRLF
+ {
+ size = Integer.parseInt(buf.toString(), 16);
+ break;
+ }
+ else if (!seenSemi && c >= 0x30)
+ {
+ buf.append ((char) c);
+ }
+ last = c;
+ }
+ while(c != -1);
+ count = 0;
+ meta = false;
+ }
+ if (size == 0)
+ {
+ // Read trailer
+ headers.parse(in);
+ eof = true;
+ return -1;
+ }
+ else
+ {
+ int diff = length - offset;
+ int max = size - count;
+ max = (diff < max) ? diff : max;
+ int len = (max > 0) ? in.read(buffer, offset, max) : 0;
+ count += len;
+ if (count == size)
+ {
+ // Read CRLF
+ int c1 = in.read();
+ int c2 = in.read();
+ if (c1 == -1 && c2 == -1)
+ {
+ // EOF before CRLF: bad, but ignore
+ eof = true;
+ return -1;
+ }
+ if (c1 != 0x0d || c2 != 0x0a)
+ {
+ throw new ProtocolException("expecting CRLF: " + c1 + "," + c2);
+ }
+ meta = true;
+ }
+ return len;
+ }
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Cookie.java b/libjava/classpath/gnu/java/net/protocol/http/Cookie.java
new file mode 100644
index 00000000000..45e2f733ff3
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Cookie.java
@@ -0,0 +1,160 @@
+/* Cookie.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.util.Date;
+
+/**
+ * An HTTP cookie, as specified in RFC 2109.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Cookie
+{
+
+ /**
+ * The name of the cookie.
+ */
+ protected final String name;
+
+ /**
+ * The value of the cookie.
+ */
+ protected final String value;
+
+ /**
+ * Optional documentation of the intended use of the cookie.
+ */
+ protected final String comment;
+
+ /**
+ * The domain for which the cookie is valid.
+ */
+ protected final String domain;
+
+ /**
+ * Optional subset of URL paths within the domain for which the cookie is
+ * valid.
+ */
+ protected final String path;
+
+ /**
+ * Indicates that the user-agent should only use secure means to transmit
+ * this cookie to the server.
+ */
+ protected final boolean secure;
+
+ /**
+ * The date at which this cookie expires.
+ */
+ protected final Date expires;
+
+ public Cookie(String name, String value, String comment, String domain,
+ String path, boolean secure, Date expires)
+ {
+ this.name = name;
+ this.value = value;
+ this.comment = comment;
+ this.domain = domain;
+ this.path = path;
+ this.secure = secure;
+ this.expires = expires;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public String getComment()
+ {
+ return comment;
+ }
+
+ public String getDomain()
+ {
+ return domain;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ public Date getExpiryDate()
+ {
+ return expires;
+ }
+
+ public String toString()
+ {
+ return toString(true, true);
+ }
+
+ public String toString(boolean showPath, boolean showDomain)
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append(name);
+ buf.append('=');
+ buf.append(value);
+ if (showPath)
+ {
+ buf.append("; $Path=");
+ buf.append(path);
+ }
+ if (showDomain)
+ {
+ buf.append("; $Domain=");
+ buf.append(domain);
+ }
+ return buf.toString();
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java b/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java
new file mode 100644
index 00000000000..cc1225c497d
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/CookieManager.java
@@ -0,0 +1,65 @@
+/* CookieManager.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Cookie manager interface.
+ * If an application wants to handle cookies, they should implement this
+ * interface and register the instance with each HTTPConnection they use.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface CookieManager
+{
+
+ /**
+ * Stores a cookie in the cookie manager.
+ * @param cookie the cookie to store
+ */
+ void setCookie(Cookie cookie);
+
+ /**
+ * Retrieves the cookies matching the specified criteria.
+ * @param host the host name
+ * @param secure whether the connection is secure
+ * @param path the path to access
+ */
+ Cookie[] getCookies(String host, boolean secure, String path);
+
+}
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Credentials.java b/libjava/classpath/gnu/java/net/protocol/http/Credentials.java
new file mode 100644
index 00000000000..9e5fcd172f5
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Credentials.java
@@ -0,0 +1,88 @@
+/* Credentials.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Represents a username/password combination that can be used to
+ * authenticate to an HTTP server.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Credentials
+{
+
+ /**
+ * The username.
+ */
+ private String username;
+
+ /**
+ * The password.
+ */
+ private String password;
+
+ /**
+ * Constructor.
+ * @param username the username
+ * @param password the password
+ */
+ public Credentials(String username, String password)
+ {
+ this.username = username;
+ this.password = password;
+ }
+
+ /**
+ * Returns the username.
+ */
+ public String getUsername()
+ {
+ return username;
+ }
+
+ /**
+ * Returns the password.
+ */
+ public String getPassword()
+ {
+ return password;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java
new file mode 100644
index 00000000000..6d9f447a2ac
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPConnection.java
@@ -0,0 +1,681 @@
+/* HTTPConnection.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import gnu.classpath.Configuration;
+import gnu.classpath.SystemProperties;
+import gnu.java.net.EmptyX509TrustManager;
+import gnu.java.net.protocol.http.event.ConnectionEvent;
+import gnu.java.net.protocol.http.event.ConnectionListener;
+import gnu.java.net.protocol.http.event.RequestEvent;
+import gnu.java.net.protocol.http.event.RequestListener;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+
+/**
+ * A connection to an HTTP server.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class HTTPConnection
+{
+
+ /**
+ * The default HTTP port.
+ */
+ public static final int HTTP_PORT = 80;
+
+ /**
+ * The default HTTPS port.
+ */
+ public static final int HTTPS_PORT = 443;
+
+ private static final String userAgent = SystemProperties.getProperty("http.agent");
+
+ /**
+ * The host name of the server to connect to.
+ */
+ protected final String hostname;
+
+ /**
+ * The port to connect to.
+ */
+ protected final int port;
+
+ /**
+ * Whether the connection should use transport level security (HTTPS).
+ */
+ protected final boolean secure;
+
+ /**
+ * The connection timeout for connecting the underlying socket.
+ */
+ protected final int connectionTimeout;
+
+ /**
+ * The read timeout for reads on the underlying socket.
+ */
+ protected final int timeout;
+
+ /**
+ * The host name of the proxy to connect to.
+ */
+ protected String proxyHostname;
+
+ /**
+ * The port on the proxy to connect to.
+ */
+ protected int proxyPort;
+
+ /**
+ * The major version of HTTP supported by this client.
+ */
+ protected int majorVersion;
+
+ /**
+ * The minor version of HTTP supported by this client.
+ */
+ protected int minorVersion;
+
+ private final List connectionListeners;
+ private final List requestListeners;
+ private final List handshakeCompletedListeners;
+
+ /**
+ * The socket this connection communicates on.
+ */
+ protected Socket socket;
+
+ /**
+ * The SSL socket factory to use.
+ */
+ private SSLSocketFactory sslSocketFactory;
+
+ /**
+ * The socket input stream.
+ */
+ protected InputStream in;
+
+ /**
+ * The socket output stream.
+ */
+ protected OutputStream out;
+
+ /**
+ * Nonce values seen by this connection.
+ */
+ private Map nonceCounts;
+
+ /**
+ * The cookie manager for this connection.
+ */
+ protected CookieManager cookieManager;
+
+ /**
+ * Creates a new HTTP connection.
+ * @param hostname the name of the host to connect to
+ */
+ public HTTPConnection(String hostname)
+ {
+ this(hostname, HTTP_PORT, false, 0, 0);
+ }
+
+ /**
+ * Creates a new HTTP or HTTPS connection.
+ * @param hostname the name of the host to connect to
+ * @param secure whether to use a secure connection
+ */
+ public HTTPConnection(String hostname, boolean secure)
+ {
+ this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, 0, 0);
+ }
+
+ /**
+ * Creates a new HTTP or HTTPS connection on the specified port.
+ * @param hostname the name of the host to connect to
+ * @param secure whether to use a secure connection
+ * @param connectionTimeout the connection timeout
+ * @param timeout the socket read timeout
+ */
+ public HTTPConnection(String hostname, boolean secure,
+ int connectionTimeout, int timeout)
+ {
+ this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure,
+ connectionTimeout, timeout);
+ }
+
+ /**
+ * Creates a new HTTP connection on the specified port.
+ * @param hostname the name of the host to connect to
+ * @param port the port on the host to connect to
+ */
+ public HTTPConnection(String hostname, int port)
+ {
+ this(hostname, port, false, 0, 0);
+ }
+
+ /**
+ * Creates a new HTTP or HTTPS connection on the specified port.
+ * @param hostname the name of the host to connect to
+ * @param port the port on the host to connect to
+ * @param secure whether to use a secure connection
+ */
+ public HTTPConnection(String hostname, int port, boolean secure)
+ {
+ this(hostname, port, secure, 0, 0);
+ }
+
+ /**
+ * Creates a new HTTP or HTTPS connection on the specified port.
+ * @param hostname the name of the host to connect to
+ * @param port the port on the host to connect to
+ * @param secure whether to use a secure connection
+ * @param connectionTimeout the connection timeout
+ * @param timeout the socket read timeout
+ */
+ public HTTPConnection(String hostname, int port, boolean secure,
+ int connectionTimeout, int timeout)
+ {
+ this.hostname = hostname;
+ this.port = port;
+ this.secure = secure;
+ this.connectionTimeout = connectionTimeout;
+ this.timeout = timeout;
+ majorVersion = minorVersion = 1;
+ connectionListeners = new ArrayList(4);
+ requestListeners = new ArrayList(4);
+ handshakeCompletedListeners = new ArrayList(2);
+ }
+
+ /**
+ * Returns the name of the host to connect to.
+ */
+ public String getHostName()
+ {
+ return hostname;
+ }
+
+ /**
+ * Returns the port on the host to connect to.
+ */
+ public int getPort()
+ {
+ return port;
+ }
+
+ /**
+ * Indicates whether to use a secure connection or not.
+ */
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ /**
+ * Returns the HTTP version string supported by this connection.
+ * @see #version
+ */
+ public String getVersion()
+ {
+ return "HTTP/" + majorVersion + '.' + minorVersion;
+ }
+
+ /**
+ * Sets the HTTP version supported by this connection.
+ * @param majorVersion the major version
+ * @param minorVersion the minor version
+ */
+ public void setVersion(int majorVersion, int minorVersion)
+ {
+ if (majorVersion != 1)
+ {
+ throw new IllegalArgumentException("major version not supported: " +
+ majorVersion);
+ }
+ if (minorVersion < 0 || minorVersion > 1)
+ {
+ throw new IllegalArgumentException("minor version not supported: " +
+ minorVersion);
+ }
+ this.majorVersion = majorVersion;
+ this.minorVersion = minorVersion;
+ }
+
+ /**
+ * Directs this connection to use the specified proxy.
+ * @param hostname the proxy host name
+ * @param port the port on the proxy to connect to
+ */
+ public void setProxy(String hostname, int port)
+ {
+ proxyHostname = hostname;
+ proxyPort = port;
+ }
+
+ /**
+ * Indicates whether this connection is using an HTTP proxy.
+ */
+ public boolean isUsingProxy()
+ {
+ return (proxyHostname != null && proxyPort > 0);
+ }
+
+ /**
+ * Sets the cookie manager to use for this connection.
+ * @param cookieManager the cookie manager
+ */
+ public void setCookieManager(CookieManager cookieManager)
+ {
+ this.cookieManager = cookieManager;
+ }
+
+ /**
+ * Returns the cookie manager in use for this connection.
+ */
+ public CookieManager getCookieManager()
+ {
+ return cookieManager;
+ }
+
+ /**
+ * Creates a new request using this connection.
+ * @param method the HTTP method to invoke
+ * @param path the URI-escaped RFC2396 <code>abs_path</code> with
+ * optional query part
+ */
+ public Request newRequest(String method, String path)
+ {
+ if (method == null || method.length() == 0)
+ {
+ throw new IllegalArgumentException("method must have non-zero length");
+ }
+ if (path == null || path.length() == 0)
+ {
+ path = "/";
+ }
+ Request ret = new Request(this, method, path);
+ if ((secure && port != HTTPS_PORT) ||
+ (!secure && port != HTTP_PORT))
+ {
+ ret.setHeader("Host", hostname + ":" + port);
+ }
+ else
+ {
+ ret.setHeader("Host", hostname);
+ }
+ ret.setHeader("User-Agent", userAgent);
+ ret.setHeader("Connection", "keep-alive");
+ ret.setHeader("Accept-Encoding",
+ "chunked;q=1.0, gzip;q=0.9, deflate;q=0.8, " +
+ "identity;q=0.6, *;q=0");
+ if (cookieManager != null)
+ {
+ Cookie[] cookies = cookieManager.getCookies(hostname, secure, path);
+ if (cookies != null && cookies.length > 0)
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append("$Version=1");
+ for (int i = 0; i < cookies.length; i++)
+ {
+ buf.append(',');
+ buf.append(' ');
+ buf.append(cookies[i].toString());
+ }
+ ret.setHeader("Cookie", buf.toString());
+ }
+ }
+ fireRequestEvent(RequestEvent.REQUEST_CREATED, ret);
+ return ret;
+ }
+
+ /**
+ * Closes this connection.
+ */
+ public void close()
+ throws IOException
+ {
+ try
+ {
+ closeConnection();
+ }
+ finally
+ {
+ fireConnectionEvent(ConnectionEvent.CONNECTION_CLOSED);
+ }
+ }
+
+ /**
+ * Retrieves the socket associated with this connection.
+ * This creates the socket if necessary.
+ */
+ protected synchronized Socket getSocket()
+ throws IOException
+ {
+ if (socket == null)
+ {
+ String connectHostname = hostname;
+ int connectPort = port;
+ if (isUsingProxy())
+ {
+ connectHostname = proxyHostname;
+ connectPort = proxyPort;
+ }
+ socket = new Socket();
+ InetSocketAddress address =
+ new InetSocketAddress(connectHostname, connectPort);
+ if (connectionTimeout > 0)
+ {
+ socket.connect(address, connectionTimeout);
+ }
+ else
+ {
+ socket.connect(address);
+ }
+ if (timeout > 0)
+ {
+ socket.setSoTimeout(timeout);
+ }
+ if (secure)
+ {
+ try
+ {
+ SSLSocketFactory factory = getSSLSocketFactory();
+ SSLSocket ss =
+ (SSLSocket) factory.createSocket(socket, connectHostname,
+ connectPort, true);
+ String[] protocols = { "TLSv1", "SSLv3" };
+ ss.setEnabledProtocols(protocols);
+ ss.setUseClientMode(true);
+ synchronized (handshakeCompletedListeners)
+ {
+ if (!handshakeCompletedListeners.isEmpty())
+ {
+ for (Iterator i =
+ handshakeCompletedListeners.iterator();
+ i.hasNext(); )
+ {
+ HandshakeCompletedListener l =
+ (HandshakeCompletedListener) i.next();
+ ss.addHandshakeCompletedListener(l);
+ }
+ }
+ }
+ ss.startHandshake();
+ socket = ss;
+ }
+ catch (GeneralSecurityException e)
+ {
+ throw new IOException(e.getMessage());
+ }
+ }
+ in = socket.getInputStream();
+ in = new BufferedInputStream(in);
+ out = socket.getOutputStream();
+ out = new BufferedOutputStream(out);
+ }
+ return socket;
+ }
+
+ SSLSocketFactory getSSLSocketFactory()
+ throws GeneralSecurityException
+ {
+ if (sslSocketFactory == null)
+ {
+ TrustManager tm = new EmptyX509TrustManager();
+ SSLContext context = SSLContext.getInstance("SSL");
+ TrustManager[] trust = new TrustManager[] { tm };
+ context.init(null, trust, null);
+ sslSocketFactory = context.getSocketFactory();
+ }
+ return sslSocketFactory;
+ }
+
+ void setSSLSocketFactory(SSLSocketFactory factory)
+ {
+ sslSocketFactory = factory;
+ }
+
+ protected synchronized InputStream getInputStream()
+ throws IOException
+ {
+ if (socket == null)
+ {
+ getSocket();
+ }
+ return in;
+ }
+
+ protected synchronized OutputStream getOutputStream()
+ throws IOException
+ {
+ if (socket == null)
+ {
+ getSocket();
+ }
+ return out;
+ }
+
+ /**
+ * Closes the underlying socket, if any.
+ */
+ protected synchronized void closeConnection()
+ throws IOException
+ {
+ if (socket != null)
+ {
+ try
+ {
+ socket.close();
+ }
+ finally
+ {
+ socket = null;
+ }
+ }
+ }
+
+ /**
+ * Returns a URI representing the connection.
+ * This does not include any request path component.
+ */
+ protected String getURI()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append(secure ? "https://" : "http://");
+ buf.append(hostname);
+ if (secure)
+ {
+ if (port != HTTPConnection.HTTPS_PORT)
+ {
+ buf.append(':');
+ buf.append(port);
+ }
+ }
+ else
+ {
+ if (port != HTTPConnection.HTTP_PORT)
+ {
+ buf.append(':');
+ buf.append(port);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Get the number of times the specified nonce has been seen by this
+ * connection.
+ */
+ int getNonceCount(String nonce)
+ {
+ if (nonceCounts == null)
+ {
+ return 0;
+ }
+ return((Integer) nonceCounts.get(nonce)).intValue();
+ }
+
+ /**
+ * Increment the number of times the specified nonce has been seen.
+ */
+ void incrementNonce(String nonce)
+ {
+ int current = getNonceCount(nonce);
+ if (nonceCounts == null)
+ {
+ nonceCounts = new HashMap();
+ }
+ nonceCounts.put(nonce, new Integer(current + 1));
+ }
+
+ // -- Events --
+
+ public void addConnectionListener(ConnectionListener l)
+ {
+ synchronized (connectionListeners)
+ {
+ connectionListeners.add(l);
+ }
+ }
+
+ public void removeConnectionListener(ConnectionListener l)
+ {
+ synchronized (connectionListeners)
+ {
+ connectionListeners.remove(l);
+ }
+ }
+
+ protected void fireConnectionEvent(int type)
+ {
+ ConnectionEvent event = new ConnectionEvent(this, type);
+ ConnectionListener[] l = null;
+ synchronized (connectionListeners)
+ {
+ l = new ConnectionListener[connectionListeners.size()];
+ connectionListeners.toArray(l);
+ }
+ for (int i = 0; i < l.length; i++)
+ {
+ switch (type)
+ {
+ case ConnectionEvent.CONNECTION_CLOSED:
+ l[i].connectionClosed(event);
+ break;
+ }
+ }
+ }
+
+ public void addRequestListener(RequestListener l)
+ {
+ synchronized (requestListeners)
+ {
+ requestListeners.add(l);
+ }
+ }
+
+ public void removeRequestListener(RequestListener l)
+ {
+ synchronized (requestListeners)
+ {
+ requestListeners.remove(l);
+ }
+ }
+
+ protected void fireRequestEvent(int type, Request request)
+ {
+ RequestEvent event = new RequestEvent(this, type, request);
+ RequestListener[] l = null;
+ synchronized (requestListeners)
+ {
+ l = new RequestListener[requestListeners.size()];
+ requestListeners.toArray(l);
+ }
+ for (int i = 0; i < l.length; i++)
+ {
+ switch (type)
+ {
+ case RequestEvent.REQUEST_CREATED:
+ l[i].requestCreated(event);
+ break;
+ case RequestEvent.REQUEST_SENDING:
+ l[i].requestSent(event);
+ break;
+ case RequestEvent.REQUEST_SENT:
+ l[i].requestSent(event);
+ break;
+ }
+ }
+ }
+
+ void addHandshakeCompletedListener(HandshakeCompletedListener l)
+ {
+ synchronized (handshakeCompletedListeners)
+ {
+ handshakeCompletedListeners.add(l);
+ }
+ }
+ void removeHandshakeCompletedListener(HandshakeCompletedListener l)
+ {
+ synchronized (handshakeCompletedListeners)
+ {
+ handshakeCompletedListeners.remove(l);
+ }
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java
new file mode 100644
index 00000000000..2f59e43181c
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPDateFormat.java
@@ -0,0 +1,441 @@
+/* HTTPDateFormat.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * HTTP date formatter and parser.
+ * Formats dates according to RFC 822 (updated by RFC 1123).
+ * Parses dates according to the above, <i>or</i> RFC 1036, <i>or</i> the
+ * ANSI C <code>asctime()</code> format.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class HTTPDateFormat
+ extends DateFormat
+{
+
+ static final String[] DAYS_OF_WEEK = {
+ null, "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+
+ static final String[] MONTHS = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ public HTTPDateFormat()
+ {
+ calendar = new GregorianCalendar(TimeZone.getTimeZone ("GMT"));
+ numberFormat = new DecimalFormat();
+ }
+
+ /**
+ * Appends the textual value for the specified field to the given string
+ * buffer. This method should be avoided, use <code>format(Date)</code>
+ * instead.
+ * @param date the Date object
+ * @param buf the buffer to append to
+ * @param field the current field position
+ * @return the modified buffer
+ */
+ public StringBuffer format(Date date, StringBuffer buf,
+ FieldPosition field)
+ {
+ calendar.clear();
+ calendar.setTime(date);
+ buf.setLength(0);
+
+ // Day of week
+ buf.append(DAYS_OF_WEEK[calendar.get(Calendar.DAY_OF_WEEK)]);
+ buf.append(',');
+ buf.append(' ');
+
+ // Day of month
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ buf.append(Character.forDigit(day / 10, 10));
+ buf.append(Character.forDigit(day % 10, 10));
+ buf.append(' ');
+
+ // Month
+ buf.append(MONTHS[calendar.get(Calendar.MONTH)]);
+ buf.append(' ');
+
+ // Year
+ int year = calendar.get(Calendar.YEAR);
+ if (year < 1000)
+ {
+ buf.append('0');
+ if (year < 100)
+ {
+ buf.append('0');
+ if (year < 10)
+ {
+ buf.append('0');
+ }
+ }
+ }
+ buf.append(Integer.toString(year));
+ buf.append(' ');
+
+ // Hour
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ buf.append(Character.forDigit(hour / 10, 10));
+ buf.append(Character.forDigit(hour % 10, 10));
+ buf.append(':');
+
+ // Minute
+ int minute = calendar.get(Calendar.MINUTE);
+ buf.append(Character.forDigit(minute / 10, 10));
+ buf.append(Character.forDigit(minute % 10, 10));
+ buf.append(':');
+
+ // Second
+ int second = calendar.get(Calendar.SECOND);
+ buf.append(Character.forDigit(second / 10, 10));
+ buf.append(Character.forDigit(second % 10, 10));
+ buf.append(' ');
+
+ // Timezone
+ // Get time offset in minutes
+ int zoneOffset =(calendar.get(Calendar.ZONE_OFFSET) +
+ calendar.get(Calendar.DST_OFFSET)) / 60000;
+
+ // Apply + or - appropriately
+ if (zoneOffset < 0)
+ {
+ zoneOffset = -zoneOffset;
+ buf.append('-');
+ }
+ else
+ {
+ buf.append('+');
+ }
+
+ // Set the 2 2-char fields as specified above
+ int tzhours = zoneOffset / 60;
+ buf.append(Character.forDigit(tzhours / 10, 10));
+ buf.append(Character.forDigit(tzhours % 10, 10));
+ int tzminutes = zoneOffset % 60;
+ buf.append(Character.forDigit(tzminutes / 10, 10));
+ buf.append(Character.forDigit(tzminutes % 10, 10));
+
+ field.setBeginIndex(0);
+ field.setEndIndex(buf.length());
+ return buf;
+ }
+
+ /**
+ * Parses the given date in the current TimeZone.
+ * @param text the formatted date to be parsed
+ * @param pos the current parse position
+ */
+ public Date parse(String text, ParsePosition pos)
+ {
+ int date, month, year, hour, minute, second;
+ String monthText;
+ int start = 0, end = -1;
+ int len = text.length();
+ calendar.clear();
+ pos.setIndex(start);
+ try
+ {
+ // Advance to date
+ if (Character.isLetter(text.charAt(start)))
+ {
+ start = skipNonWhitespace(text, start);
+ }
+ // Determine mode
+ switch(start)
+ {
+ case 3:
+ // asctime
+ start = skipWhitespace(text, start);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ monthText = text.substring(start, end);
+ month = -1;
+ for (int i = 0; i < 12; i++)
+ {
+ if (MONTHS[i].equals(monthText))
+ {
+ month = i;
+ break;
+ }
+ }
+ if (month == -1)
+ {
+ pos.setErrorIndex(end);
+ return null;
+ }
+ // Advance to date
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ date = Integer.parseInt(text.substring(start, end));
+ // Advance to hour
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ hour = Integer.parseInt(text.substring(start, end));
+ // Advance to minute
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ minute = Integer.parseInt(text.substring(start, end));
+ // Advance to second
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ second = Integer.parseInt(text.substring(start, end));
+ // Advance to year
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ year = Integer.parseInt(text.substring(start, end));
+ break;
+ case 0:
+ case 4:
+ // rfc822
+ start = skipWhitespace(text, start);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ date = Integer.parseInt(text.substring(start, end));
+ // Advance to month
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ monthText = text.substring(start, end);
+ month = -1;
+ for (int i = 0; i < 12; i++)
+ {
+ if (MONTHS[i].equals(monthText))
+ {
+ month = i;
+ break;
+ }
+ }
+ if (month == -1)
+ {
+ pos.setErrorIndex(end);
+ return null;
+ }
+ // Advance to year
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ year = Integer.parseInt(text.substring(start, end));
+ // Advance to hour
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ hour = Integer.parseInt(text.substring(start, end));
+ // Advance to minute
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ minute = Integer.parseInt(text.substring(start, end));
+ // Advance to second
+ start = end + 1;
+ pos.setIndex(start);
+ end = start + 1;
+ while (end < len && !Character.isWhitespace(text.charAt(end)))
+ {
+ end++;
+ }
+ second = Integer.parseInt(text.substring(start, end));
+ break;
+ default:
+ // rfc850(obsolete)
+ start = skipWhitespace(text, start);
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, '-');
+ date = Integer.parseInt(text.substring(start, end));
+ // Advance to month
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, '-');
+ monthText = text.substring(start, end);
+ month = -1;
+ for (int i = 0; i < 12; i++)
+ {
+ if (MONTHS[i].equals(monthText))
+ {
+ month = i;
+ break;
+ }
+ }
+ if (month == -1)
+ {
+ pos.setErrorIndex(end);
+ return null;
+ }
+ // Advance to year
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipNonWhitespace(text, start + 1);
+ year = 1900 + Integer.parseInt(text.substring(start, end));
+ // Advance to hour
+ start = skipWhitespace(text, end + 1);
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ hour = Integer.parseInt(text.substring(start, end));
+ // Advance to minute
+ start = end + 1;
+ pos.setIndex(start);
+ end = skipTo(text, start + 1, ':');
+ minute = Integer.parseInt(text.substring(start, end));
+ // Advance to second
+ start = end + 1;
+ pos.setIndex(start);
+ end = start + 1;
+ while (end < len && !Character.isWhitespace(text.charAt(end)))
+ {
+ end++;
+ }
+ second = Integer.parseInt(text.substring(start, end));
+ }
+
+ calendar.set(Calendar.YEAR, year);
+ calendar.set(Calendar.MONTH, month);
+ calendar.set(Calendar.DAY_OF_MONTH, date);
+ calendar.set(Calendar.HOUR, hour);
+ calendar.set(Calendar.MINUTE, minute);
+ calendar.set(Calendar.SECOND, second);
+
+ if (end != len)
+ {
+ // Timezone
+ start = skipWhitespace(text, end + 1);
+ end = start + 1;
+ while (end < len && !Character.isWhitespace(text.charAt(end)))
+ {
+ end++;
+ }
+ char pm = text.charAt(start);
+ if (Character.isLetter(pm))
+ {
+ TimeZone tz =
+ TimeZone.getTimeZone(text.substring(start, end));
+ calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset());
+ }
+ else
+ {
+ int zoneOffset = 0;
+ zoneOffset += 600 * Character.digit(text.charAt(++start), 10);
+ zoneOffset += 60 * Character.digit(text.charAt(++start), 10);
+ zoneOffset += 10 * Character.digit(text.charAt(++start), 10);
+ zoneOffset += Character.digit(text.charAt(++start), 10);
+ zoneOffset *= 60000; // minutes -> ms
+ if ('-' == pm)
+ {
+ zoneOffset = -zoneOffset;
+ }
+ calendar.set(Calendar.ZONE_OFFSET, zoneOffset);
+ }
+ }
+ pos.setIndex(end);
+
+ return calendar.getTime();
+ }
+ catch (NumberFormatException e)
+ {
+ pos.setErrorIndex(Math.max(start, end));
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ pos.setErrorIndex(Math.max(start, end));
+ }
+ return null;
+ }
+
+ private int skipWhitespace(String text, int pos)
+ {
+ while(Character.isWhitespace(text.charAt(pos)))
+ {
+ pos++;
+ }
+ return pos;
+ }
+
+ private int skipNonWhitespace(String text, int pos)
+ {
+ while(!Character.isWhitespace(text.charAt(pos)))
+ {
+ pos++;
+ }
+ return pos;
+ }
+
+ private int skipTo(String text, int pos, char c)
+ {
+ while(text.charAt(pos) != c)
+ {
+ pos++;
+ }
+ return pos;
+ }
+
+ /**
+ * Don't allow setting the calendar.
+ */
+ public void setCalendar(Calendar newCalendar)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Don't allow setting the NumberFormat.
+ */
+ public void setNumberFormat(NumberFormat newNumberFormat)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java
new file mode 100644
index 00000000000..9f2055fe658
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java
@@ -0,0 +1,688 @@
+/* HTTPURLConnection.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * A URLConnection that uses the HTTPConnection class.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class HTTPURLConnection
+ extends HttpsURLConnection
+ implements HandshakeCompletedListener
+{
+
+ /**
+ * Pool of reusable connections, used if keepAlive is true.
+ */
+ private static final Map connectionPool = new LinkedHashMap();
+
+ /*
+ * The underlying connection.
+ */
+ private HTTPConnection connection;
+
+ // These are package private for use in anonymous inner classes.
+ String proxyHostname;
+ int proxyPort;
+ String agent;
+ boolean keepAlive;
+ int maxConnections;
+
+ private Request request;
+ private Headers requestHeaders;
+ private ByteArrayOutputStream requestSink;
+ private boolean requestMethodSetExplicitly;
+
+ private Response response;
+ private ByteArrayInputStream responseSink;
+ private ByteArrayInputStream errorSink;
+
+ private HandshakeCompletedEvent handshakeEvent;
+
+ /**
+ * Constructor.
+ * @param url the URL
+ */
+ public HTTPURLConnection(URL url)
+ throws IOException
+ {
+ super(url);
+ requestHeaders = new Headers();
+ AccessController.doPrivileged(this.new GetHTTPPropertiesAction());
+ }
+
+ class GetHTTPPropertiesAction
+ implements PrivilegedAction
+ {
+
+ public Object run()
+ {
+ proxyHostname = System.getProperty("http.proxyHost");
+ if (proxyHostname != null && proxyHostname.length() > 0)
+ {
+ String port = System.getProperty("http.proxyPort");
+ if (port != null && port.length() > 0)
+ {
+ proxyPort = Integer.parseInt(port);
+ }
+ else
+ {
+ proxyHostname = null;
+ proxyPort = -1;
+ }
+ }
+ agent = System.getProperty("http.agent");
+ String ka = System.getProperty("http.keepAlive");
+ keepAlive = !(ka != null && "false".equals(ka));
+ String mc = System.getProperty("http.maxConnections");
+ maxConnections = (mc != null && mc.length() > 0) ?
+ Math.max(Integer.parseInt(mc), 1) : 5;
+ return null;
+ }
+
+ }
+
+ public void connect()
+ throws IOException
+ {
+ if (connected)
+ {
+ return;
+ }
+ String protocol = url.getProtocol();
+ boolean secure = "https".equals(protocol);
+ String host = url.getHost();
+ int port = url.getPort();
+ if (port < 0)
+ {
+ port = secure ? HTTPConnection.HTTPS_PORT :
+ HTTPConnection.HTTP_PORT;
+ }
+ String file = url.getFile();
+ String username = url.getUserInfo();
+ String password = null;
+ if (username != null)
+ {
+ int ci = username.indexOf(':');
+ if (ci != -1)
+ {
+ password = username.substring(ci + 1);
+ username = username.substring(0, ci);
+ }
+ }
+ final Credentials creds = (username == null) ? null :
+ new Credentials (username, password);
+
+ boolean retry;
+ do
+ {
+ retry = false;
+ if (connection == null)
+ {
+ connection = getConnection(host, port, secure);
+ if (secure)
+ {
+ SSLSocketFactory factory = getSSLSocketFactory();
+ HostnameVerifier verifier = getHostnameVerifier();
+ if (factory != null)
+ {
+ connection.setSSLSocketFactory(factory);
+ }
+ connection.addHandshakeCompletedListener(this);
+ // TODO verifier
+ }
+ }
+ if (proxyHostname != null)
+ {
+ if (proxyPort < 0)
+ {
+ proxyPort = secure ? HTTPConnection.HTTPS_PORT :
+ HTTPConnection.HTTP_PORT;
+ }
+ connection.setProxy(proxyHostname, proxyPort);
+ }
+ request = connection.newRequest(method, file);
+ if (!keepAlive)
+ {
+ request.setHeader("Connection", "close");
+ }
+ if (agent != null)
+ {
+ request.setHeader("User-Agent", agent);
+ }
+ request.getHeaders().putAll(requestHeaders);
+ if (requestSink != null)
+ {
+ byte[] content = requestSink.toByteArray();
+ RequestBodyWriter writer = new ByteArrayRequestBodyWriter(content);
+ request.setRequestBodyWriter(writer);
+ }
+ ByteArrayResponseBodyReader reader = new ByteArrayResponseBodyReader();
+ request.setResponseBodyReader(reader);
+ if (creds != null)
+ {
+ request.setAuthenticator(new Authenticator() {
+ public Credentials getCredentials(String realm, int attempts)
+ {
+ return (attempts < 2) ? creds : null;
+ }
+ });
+ }
+ response = request.dispatch();
+ if (response.getCodeClass() == 3 && getInstanceFollowRedirects())
+ {
+ // Follow redirect
+ String location = response.getHeader("Location");
+ if (location != null)
+ {
+ String connectionUri = connection.getURI();
+ int start = connectionUri.length();
+ if (location.startsWith(connectionUri) &&
+ location.charAt(start) == '/')
+ {
+ file = location.substring(start);
+ retry = true;
+ }
+ else if (location.startsWith("http:"))
+ {
+ connection.close();
+ connection = null;
+ secure = false;
+ start = 7;
+ int end = location.indexOf('/', start);
+ host = location.substring(start, end);
+ int ci = host.lastIndexOf(':');
+ if (ci != -1)
+ {
+ port = Integer.parseInt(host.substring (ci + 1));
+ host = host.substring(0, ci);
+ }
+ else
+ {
+ port = HTTPConnection.HTTP_PORT;
+ }
+ file = location.substring(end);
+ retry = true;
+ }
+ else if (location.startsWith("https:"))
+ {
+ connection.close();
+ connection = null;
+ secure = true;
+ start = 8;
+ int end = location.indexOf('/', start);
+ host = location.substring(start, end);
+ int ci = host.lastIndexOf(':');
+ if (ci != -1)
+ {
+ port = Integer.parseInt(host.substring (ci + 1));
+ host = host.substring(0, ci);
+ }
+ else
+ {
+ port = HTTPConnection.HTTPS_PORT;
+ }
+ file = location.substring(end);
+ retry = true;
+ }
+ else if (location.length() > 0)
+ {
+ // Malformed absolute URI, treat as file part of URI
+ if (location.charAt(0) == '/')
+ {
+ // Absolute path
+ file = location;
+ }
+ else
+ {
+ // Relative path
+ int lsi = file.lastIndexOf('/');
+ file = (lsi == -1) ? "/" : file.substring(0, lsi + 1);
+ file += location;
+ }
+ retry = true;
+ }
+ }
+ }
+ else
+ {
+ responseSink = new ByteArrayInputStream(reader.toByteArray ());
+ if (response.getCode() == 404)
+ {
+ errorSink = responseSink;
+ throw new FileNotFoundException(url.toString());
+ }
+ }
+ }
+ while (retry);
+ connected = true;
+ }
+
+ /**
+ * Returns a connection, from the pool if necessary.
+ */
+ HTTPConnection getConnection(String host, int port, boolean secure)
+ throws IOException
+ {
+ HTTPConnection connection;
+ if (keepAlive)
+ {
+ StringBuffer buf = new StringBuffer(secure ? "https://" : "http://");
+ buf.append(Thread.currentThread().hashCode());
+ buf.append('@');
+ buf.append(host);
+ buf.append(':');
+ buf.append(port);
+ String key = buf.toString();
+ synchronized (connectionPool)
+ {
+ connection = (HTTPConnection) connectionPool.get(key);
+ if (connection == null)
+ {
+ connection = new HTTPConnection(host, port, secure);
+ // Good housekeeping
+ if (connectionPool.size() == maxConnections)
+ {
+ // maxConnections must always be >= 1
+ Object lru = connectionPool.keySet().iterator().next();
+ connectionPool.remove(lru);
+ }
+ connectionPool.put(key, connection);
+ }
+ }
+ }
+ else
+ {
+ connection = new HTTPConnection(host, port, secure);
+ }
+ return connection;
+ }
+
+ public void disconnect()
+ {
+ if (connection != null)
+ {
+ try
+ {
+ connection.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ public boolean usingProxy()
+ {
+ return (proxyHostname != null);
+ }
+
+ /**
+ * Overrides the corresponding method in HttpURLConnection to permit
+ * arbitrary methods, as long as they're valid ASCII alphabetic
+ * characters. This is to permit WebDAV and other HTTP extensions to
+ * function.
+ * @param method the method
+ */
+ public void setRequestMethod(String method)
+ throws ProtocolException
+ {
+ if (connected)
+ {
+ throw new ProtocolException("Already connected");
+ }
+ // Validate
+ method = method.toUpperCase();
+ int len = method.length();
+ if (len == 0)
+ {
+ throw new ProtocolException("Empty method name");
+ }
+ for (int i = 0; i < len; i++)
+ {
+ char c = method.charAt(i);
+ if (c < 0x41 || c > 0x5a)
+ {
+ throw new ProtocolException("Illegal character '" + c +
+ "' at index " + i);
+ }
+ }
+ // OK
+ this.method = method;
+ requestMethodSetExplicitly = true;
+ }
+
+ public String getRequestProperty(String key)
+ {
+ return requestHeaders.getValue(key);
+ }
+
+ public Map getRequestProperties()
+ {
+ return requestHeaders;
+ }
+
+ public void setRequestProperty(String key, String value)
+ {
+ requestHeaders.put(key, value);
+ }
+
+ public void addRequestProperty(String key, String value)
+ {
+ String old = requestHeaders.getValue(key);
+ if (old == null)
+ {
+ requestHeaders.put(key, value);
+ }
+ else
+ {
+ requestHeaders.put(key, old + "," + value);
+ }
+ }
+
+ public OutputStream getOutputStream()
+ throws IOException
+ {
+ if (connected)
+ {
+ throw new ProtocolException("Already connected");
+ }
+ if (!doOutput)
+ {
+ throw new ProtocolException("doOutput is false");
+ }
+ else if (!requestMethodSetExplicitly)
+ {
+ /*
+ * Silently change the method to POST if no method was set
+ * explicitly. This is due to broken applications depending on this
+ * behaviour (Apache XMLRPC for one).
+ */
+ method = "POST";
+ }
+ if (requestSink == null)
+ {
+ requestSink = new ByteArrayOutputStream();
+ }
+ return requestSink;
+ }
+
+ // -- Response --
+
+ public InputStream getInputStream()
+ throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+ if (!doInput)
+ {
+ throw new ProtocolException("doInput is false");
+ }
+ return responseSink;
+ }
+
+ public InputStream getErrorStream()
+ {
+ return errorSink;
+ }
+
+ public Map getHeaderFields()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ Map headers = response.getHeaders();
+ Map ret = new LinkedHashMap();
+ ret.put("", Collections.singletonList(getStatusLine(response)));
+ for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ ret.put(key, Collections.singletonList(value));
+ }
+ return ret;
+ }
+
+ String getStatusLine(Response response)
+ {
+ return "HTTP/" + response.getMajorVersion() +
+ "." + response.getMinorVersion() +
+ " " + response.getCode() +
+ " " + response.getMessage();
+ }
+
+ public String getHeaderField(int index)
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ if (index == 0)
+ {
+ return getStatusLine(response);
+ }
+ Iterator i = response.getHeaders().entrySet().iterator();
+ Map.Entry entry;
+ int count = 1;
+ do
+ {
+ if (!i.hasNext())
+ {
+ return null;
+ }
+ entry = (Map.Entry) i.next();
+ count++;
+ }
+ while (count <= index);
+ return (String) entry.getValue();
+ }
+
+ public String getHeaderFieldKey(int index)
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ if (index == 0)
+ {
+ return null;
+ }
+ Iterator i = response.getHeaders().entrySet().iterator();
+ Map.Entry entry;
+ int count = 1;
+ do
+ {
+ if (!i.hasNext())
+ {
+ return null;
+ }
+ entry = (Map.Entry) i.next();
+ count++;
+ }
+ while (count <= index);
+ return (String) entry.getKey();
+ }
+
+ public String getHeaderField(String name)
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+ }
+ return (String) response.getHeader(name);
+ }
+
+ public long getHeaderFieldDate(String name, long def)
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException e)
+ {
+ return def;
+ }
+ }
+ Date date = response.getDateHeader(name);
+ return (date == null) ? def : date.getTime();
+ }
+
+ public String getContentType()
+ {
+ return getHeaderField("Content-Type");
+ }
+
+ public int getResponseCode()
+ throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+ return response.getCode();
+ }
+
+ public String getResponseMessage()
+ throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+ return response.getMessage();
+ }
+
+ // -- HTTPS specific --
+
+ public String getCipherSuite()
+ {
+ if (!connected)
+ {
+ throw new IllegalStateException("not connected");
+ }
+ return handshakeEvent.getCipherSuite();
+ }
+
+ public Certificate[] getLocalCertificates()
+ {
+ if (!connected)
+ {
+ throw new IllegalStateException("not connected");
+ }
+ return handshakeEvent.getLocalCertificates();
+ }
+
+ public Certificate[] getServerCertificates()
+ throws SSLPeerUnverifiedException
+ {
+ if (!connected)
+ {
+ throw new IllegalStateException("not connected");
+ }
+ return handshakeEvent.getPeerCertificates();
+ }
+
+ // HandshakeCompletedListener
+
+ public void handshakeCompleted(HandshakeCompletedEvent event)
+ {
+ handshakeEvent = event;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Handler.java b/libjava/classpath/gnu/java/net/protocol/http/Handler.java
new file mode 100644
index 00000000000..64054251331
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Handler.java
@@ -0,0 +1,73 @@
+/* Handler.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * An HTTP URL stream handler.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Handler
+ extends URLStreamHandler
+{
+
+ /**
+ * Returns the default HTTP port (80).
+ */
+ protected int getDefaultPort()
+ {
+ return HTTPConnection.HTTP_PORT;
+ }
+
+ /**
+ * Returns an HTTPURLConnection for the given URL.
+ */
+ public URLConnection openConnection(URL url)
+ throws IOException
+ {
+ return new HTTPURLConnection(url);
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Headers.java b/libjava/classpath/gnu/java/net/protocol/http/Headers.java
new file mode 100644
index 00000000000..847ebefc1f6
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Headers.java
@@ -0,0 +1,369 @@
+/* Headers.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import gnu.java.net.LineInputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A collection of HTTP header names and associated values.
+ * Retrieval of values is case insensitive. An iteration over the keys
+ * returns the header names in the order they were received.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Headers
+ implements Map
+{
+
+ static final DateFormat dateFormat = new HTTPDateFormat();
+
+ static class Header
+ {
+
+ final String name;
+
+ Header(String name)
+ {
+ if (name == null || name.length() == 0)
+ {
+ throw new IllegalArgumentException(name);
+ }
+ this.name = name;
+ }
+
+ public int hashCode()
+ {
+ return name.toLowerCase().hashCode();
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other instanceof Header)
+ {
+ return ((Header) other).name.equalsIgnoreCase(name);
+ }
+ return false;
+ }
+
+ public String toString()
+ {
+ return name;
+ }
+
+ }
+
+ static class HeaderEntry
+ implements Map.Entry
+ {
+
+ final Map.Entry entry;
+
+ HeaderEntry(Map.Entry entry)
+ {
+ this.entry = entry;
+ }
+
+ public Object getKey()
+ {
+ return ((Header) entry.getKey()).name;
+ }
+
+ public Object getValue()
+ {
+ return entry.getValue();
+ }
+
+ public Object setValue(Object value)
+ {
+ return entry.setValue(value);
+ }
+
+ public int hashCode()
+ {
+ return entry.hashCode();
+ }
+
+ public boolean equals(Object other)
+ {
+ return entry.equals(other);
+ }
+
+ public String toString()
+ {
+ return getKey().toString() + "=" + getValue();
+ }
+
+ }
+
+ private LinkedHashMap headers;
+
+ public Headers()
+ {
+ headers = new LinkedHashMap();
+ }
+
+ public int size()
+ {
+ return headers.size();
+ }
+
+ public boolean isEmpty()
+ {
+ return headers.isEmpty();
+ }
+
+ public boolean containsKey(Object key)
+ {
+ return headers.containsKey(new Header((String) key));
+ }
+
+ public boolean containsValue(Object value)
+ {
+ return headers.containsValue(value);
+ }
+
+ public Object get(Object key)
+ {
+ return headers.get(new Header((String) key));
+ }
+
+ /**
+ * Returns the value of the specified header as a string.
+ */
+ public String getValue(String header)
+ {
+ return (String) headers.get(new Header(header));
+ }
+
+ /**
+ * Returns the value of the specified header as an integer,
+ * or -1 if the header is not present or not an integer.
+ */
+ public int getIntValue(String header)
+ {
+ String val = getValue(header);
+ if (val == null)
+ {
+ return -1;
+ }
+ try
+ {
+ return Integer.parseInt(val);
+ }
+ catch (NumberFormatException e)
+ {
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the value of the specified header as a date,
+ * or <code>null</code> if the header is not present or not a date.
+ */
+ public Date getDateValue(String header)
+ {
+ String val = getValue(header);
+ if (val == null)
+ {
+ return null;
+ }
+ try
+ {
+ return dateFormat.parse(val);
+ }
+ catch (ParseException e)
+ {
+ return null;
+ }
+ }
+
+ public Object put(Object key, Object value)
+ {
+ return headers.put(new Header((String) key), value);
+ }
+
+ public Object remove(Object key)
+ {
+ return headers.remove(new Header((String) key));
+ }
+
+ public void putAll(Map t)
+ {
+ for (Iterator i = t.keySet().iterator(); i.hasNext(); )
+ {
+ String key = (String) i.next();
+ String value = (String) t.get(key);
+ headers.put(new Header(key), value);
+ }
+ }
+
+ public void clear()
+ {
+ headers.clear();
+ }
+
+ public Set keySet()
+ {
+ Set keys = headers.keySet();
+ Set ret = new LinkedHashSet();
+ for (Iterator i = keys.iterator(); i.hasNext(); )
+ {
+ ret.add(((Header) i.next()).name);
+ }
+ return ret;
+ }
+
+ public Collection values()
+ {
+ return headers.values();
+ }
+
+ public Set entrySet()
+ {
+ Set entries = headers.entrySet();
+ Set ret = new LinkedHashSet();
+ for (Iterator i = entries.iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ ret.add(new HeaderEntry(entry));
+ }
+ return ret;
+ }
+
+ public boolean equals(Object other)
+ {
+ return headers.equals(other);
+ }
+
+ public int hashCode()
+ {
+ return headers.hashCode();
+ }
+
+ /**
+ * Parse the specified input stream, adding headers to this collection.
+ */
+ public void parse(InputStream in)
+ throws IOException
+ {
+ LineInputStream lin = (in instanceof LineInputStream) ?
+ (LineInputStream) in : new LineInputStream(in);
+
+ String name = null;
+ StringBuffer value = new StringBuffer();
+ while (true)
+ {
+ String line = lin.readLine();
+ if (line == null)
+ {
+ if (name != null)
+ {
+ addValue(name, value.toString());
+ }
+ break;
+ }
+ int len = line.length();
+ if (len < 2)
+ {
+ if (name != null)
+ {
+ addValue(name, value.toString());
+ }
+ break;
+ }
+ char c1 = line.charAt(0);
+ if (c1 == ' ' || c1 == '\t')
+ {
+ // Continuation
+ int last = len - 1;
+ if (line.charAt(last) != '\r')
+ ++last;
+ value.append(line.substring(0, last));
+ }
+ else
+ {
+ if (name != null)
+ {
+ addValue(name, value.toString());
+ }
+
+ int di = line.indexOf(':');
+ name = line.substring(0, di);
+ value.setLength(0);
+ do
+ {
+ di++;
+ }
+ while (di < len && line.charAt(di) == ' ');
+ int last = len - 1;
+ if (line.charAt(last) != '\r')
+ ++last;
+ value.append(line.substring(di, last));
+ }
+ }
+ }
+
+ private void addValue(String name, String value)
+ {
+ Header key = new Header(name);
+ String old = (String) headers.get(key);
+ if (old == null)
+ {
+ headers.put(key, value);
+ }
+ else
+ {
+ headers.put(key, old + ", " + value);
+ }
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Request.java b/libjava/classpath/gnu/java/net/protocol/http/Request.java
new file mode 100644
index 00000000000..21205e6bba8
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Request.java
@@ -0,0 +1,915 @@
+/* Request.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import gnu.java.net.BASE64;
+import gnu.java.net.LineInputStream;
+import gnu.java.net.protocol.http.event.RequestEvent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+
+/**
+ * A single HTTP request.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Request
+{
+
+ /**
+ * The connection context in which this request is invoked.
+ */
+ protected final HTTPConnection connection;
+
+ /**
+ * The HTTP method to invoke.
+ */
+ protected final String method;
+
+ /**
+ * The path identifying the resource.
+ * This string must conform to the abs_path definition given in RFC2396,
+ * with an optional "?query" part, and must be URI-escaped by the caller.
+ */
+ protected final String path;
+
+ /**
+ * The headers in this request.
+ */
+ protected final Headers requestHeaders;
+
+ /**
+ * The request body provider.
+ */
+ protected RequestBodyWriter requestBodyWriter;
+
+ /**
+ * Request body negotiation threshold for 100-continue expectations.
+ */
+ protected int requestBodyNegotiationThreshold;
+
+ /**
+ * The response body reader.
+ */
+ protected ResponseBodyReader responseBodyReader;
+
+ /**
+ * Map of response header handlers.
+ */
+ protected Map responseHeaderHandlers;
+
+ /**
+ * The authenticator.
+ */
+ protected Authenticator authenticator;
+
+ /**
+ * Whether this request has been dispatched yet.
+ */
+ private boolean dispatched;
+
+ /**
+ * Constructor for a new request.
+ * @param connection the connection context
+ * @param method the HTTP method
+ * @param path the resource path including query part
+ */
+ protected Request(HTTPConnection connection, String method,
+ String path)
+ {
+ this.connection = connection;
+ this.method = method;
+ this.path = path;
+ requestHeaders = new Headers();
+ responseHeaderHandlers = new HashMap();
+ requestBodyNegotiationThreshold = 4096;
+ }
+
+ /**
+ * Returns the connection associated with this request.
+ * @see #connection
+ */
+ public HTTPConnection getConnection()
+ {
+ return connection;
+ }
+
+ /**
+ * Returns the HTTP method to invoke.
+ * @see #method
+ */
+ public String getMethod()
+ {
+ return method;
+ }
+
+ /**
+ * Returns the resource path.
+ * @see #path
+ */
+ public String getPath()
+ {
+ return path;
+ }
+
+ /**
+ * Returns the full request-URI represented by this request, as specified
+ * by HTTP/1.1.
+ */
+ public String getRequestURI()
+ {
+ return connection.getURI() + path;
+ }
+
+ /**
+ * Returns the headers in this request.
+ */
+ public Headers getHeaders()
+ {
+ return requestHeaders;
+ }
+
+ /**
+ * Returns the value of the specified header in this request.
+ * @param name the header name
+ */
+ public String getHeader(String name)
+ {
+ return requestHeaders.getValue(name);
+ }
+
+ /**
+ * Returns the value of the specified header in this request as an integer.
+ * @param name the header name
+ */
+ public int getIntHeader(String name)
+ {
+ return requestHeaders.getIntValue(name);
+ }
+
+ /**
+ * Returns the value of the specified header in this request as a date.
+ * @param name the header name
+ */
+ public Date getDateHeader(String name)
+ {
+ return requestHeaders.getDateValue(name);
+ }
+
+ /**
+ * Sets the specified header in this request.
+ * @param name the header name
+ * @param value the header value
+ */
+ public void setHeader(String name, String value)
+ {
+ requestHeaders.put(name, value);
+ }
+
+ /**
+ * Convenience method to set the entire request body.
+ * @param requestBody the request body content
+ */
+ public void setRequestBody(byte[] requestBody)
+ {
+ setRequestBodyWriter(new ByteArrayRequestBodyWriter(requestBody));
+ }
+
+ /**
+ * Sets the request body provider.
+ * @param requestBodyWriter the handler used to obtain the request body
+ */
+ public void setRequestBodyWriter(RequestBodyWriter requestBodyWriter)
+ {
+ this.requestBodyWriter = requestBodyWriter;
+ }
+
+ /**
+ * Sets the response body reader.
+ * @param responseBodyReader the handler to receive notifications of
+ * response body content
+ */
+ public void setResponseBodyReader(ResponseBodyReader responseBodyReader)
+ {
+ this.responseBodyReader = responseBodyReader;
+ }
+
+ /**
+ * Sets a callback handler to be invoked for the specified header name.
+ * @param name the header name
+ * @param handler the handler to receive the value for the header
+ */
+ public void setResponseHeaderHandler(String name,
+ ResponseHeaderHandler handler)
+ {
+ responseHeaderHandlers.put(name, handler);
+ }
+
+ /**
+ * Sets an authenticator that can be used to handle authentication
+ * automatically.
+ * @param authenticator the authenticator
+ */
+ public void setAuthenticator(Authenticator authenticator)
+ {
+ this.authenticator = authenticator;
+ }
+
+ /**
+ * Sets the request body negotiation threshold.
+ * If this is set, it determines the maximum size that the request body
+ * may be before body negotiation occurs(via the
+ * <code>100-continue</code> expectation). This ensures that a large
+ * request body is not sent when the server wouldn't have accepted it
+ * anyway.
+ * @param threshold the body negotiation threshold, or &lt;=0 to disable
+ * request body negotation entirely
+ */
+ public void setRequestBodyNegotiationThreshold(int threshold)
+ {
+ requestBodyNegotiationThreshold = threshold;
+ }
+
+ /**
+ * Dispatches this request.
+ * A request can only be dispatched once; calling this method a second
+ * time results in a protocol exception.
+ * @exception IOException if an I/O error occurred
+ * @return an HTTP response object representing the result of the operation
+ */
+ public Response dispatch()
+ throws IOException
+ {
+ if (dispatched)
+ {
+ throw new ProtocolException("request already dispatched");
+ }
+ final String CRLF = "\r\n";
+ final String HEADER_SEP = ": ";
+ final String US_ASCII = "US-ASCII";
+ final String version = connection.getVersion();
+ Response response;
+ int contentLength = -1;
+ boolean retry = false;
+ int attempts = 0;
+ boolean expectingContinue = false;
+ if (requestBodyWriter != null)
+ {
+ contentLength = requestBodyWriter.getContentLength();
+ if (contentLength > requestBodyNegotiationThreshold)
+ {
+ expectingContinue = true;
+ setHeader("Expect", "100-continue");
+ }
+ else
+ {
+ setHeader("Content-Length", Integer.toString(contentLength));
+ }
+ }
+
+ try
+ {
+ // Loop while authentication fails or continue
+ do
+ {
+ retry = false;
+ // Send request
+ connection.fireRequestEvent(RequestEvent.REQUEST_SENDING, this);
+
+ // Get socket output and input streams
+ OutputStream out = connection.getOutputStream();
+ LineInputStream in =
+ new LineInputStream(connection.getInputStream());
+ // Request line
+ String requestUri = path;
+ if (connection.isUsingProxy() &&
+ !"*".equals(requestUri) &&
+ !"CONNECT".equals(method))
+ {
+ requestUri = getRequestURI();
+ }
+ String line = method + ' ' + requestUri + ' ' + version + CRLF;
+ out.write(line.getBytes(US_ASCII));
+ // Request headers
+ for (Iterator i = requestHeaders.keySet().iterator();
+ i.hasNext(); )
+ {
+ String name =(String) i.next();
+ String value =(String) requestHeaders.get(name);
+ line = name + HEADER_SEP + value + CRLF;
+ out.write(line.getBytes(US_ASCII));
+ }
+ out.write(CRLF.getBytes(US_ASCII));
+ // Request body
+ if (requestBodyWriter != null && !expectingContinue)
+ {
+ byte[] buffer = new byte[4096];
+ int len;
+ int count = 0;
+
+ requestBodyWriter.reset();
+ do
+ {
+ len = requestBodyWriter.write(buffer);
+ if (len > 0)
+ {
+ out.write(buffer, 0, len);
+ }
+ count += len;
+ }
+ while (len > -1 && count < contentLength);
+ out.write(CRLF.getBytes(US_ASCII));
+ }
+ out.flush();
+ // Sent event
+ connection.fireRequestEvent(RequestEvent.REQUEST_SENT, this);
+ // Get response
+ response = readResponse(in);
+ int sc = response.getCode();
+ if (sc == 401 && authenticator != null)
+ {
+ if (authenticate(response, attempts++))
+ {
+ retry = true;
+ }
+ }
+ else if (sc == 100 && expectingContinue)
+ {
+ requestHeaders.remove("Expect");
+ setHeader("Content-Length", Integer.toString(contentLength));
+ expectingContinue = false;
+ retry = true;
+ }
+ }
+ while (retry);
+ }
+ catch (IOException e)
+ {
+ connection.close();
+ throw e;
+ }
+ return response;
+ }
+
+ Response readResponse(LineInputStream in)
+ throws IOException
+ {
+ String line;
+ int len;
+
+ // Read response status line
+ line = in.readLine();
+ if (line == null)
+ {
+ throw new ProtocolException("Peer closed connection");
+ }
+ if (!line.startsWith("HTTP/"))
+ {
+ throw new ProtocolException(line);
+ }
+ len = line.length();
+ int start = 5, end = 6;
+ while (line.charAt(end) != '.')
+ {
+ end++;
+ }
+ int majorVersion = Integer.parseInt(line.substring(start, end));
+ start = end + 1;
+ end = start + 1;
+ while (line.charAt(end) != ' ')
+ {
+ end++;
+ }
+ int minorVersion = Integer.parseInt(line.substring(start, end));
+ start = end + 1;
+ end = start + 3;
+ int code = Integer.parseInt(line.substring(start, end));
+ String message = line.substring(end + 1, len - 1);
+ // Read response headers
+ Headers responseHeaders = new Headers();
+ responseHeaders.parse(in);
+ notifyHeaderHandlers(responseHeaders);
+ // Construct response
+ int codeClass = code / 100;
+ Response ret = new Response(majorVersion, minorVersion, code,
+ codeClass, message, responseHeaders);
+ switch (code)
+ {
+ case 204:
+ case 205:
+ case 304:
+ break;
+ default:
+ // Does response body reader want body?
+ boolean notify = (responseBodyReader != null);
+ if (notify)
+ {
+ if (!responseBodyReader.accept(this, ret))
+ {
+ notify = false;
+ }
+ }
+ readResponseBody(ret, in, notify);
+ }
+ return ret;
+ }
+
+ void notifyHeaderHandlers(Headers headers)
+ {
+ for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ String name =(String) entry.getKey();
+ // Handle Set-Cookie
+ if ("Set-Cookie".equalsIgnoreCase(name))
+ {
+ String value = (String) entry.getValue();
+ handleSetCookie(value);
+ }
+ ResponseHeaderHandler handler =
+ (ResponseHeaderHandler) responseHeaderHandlers.get(name);
+ if (handler != null)
+ {
+ String value = (String) entry.getValue();
+ handler.setValue(value);
+ }
+ }
+ }
+
+ void readResponseBody(Response response, InputStream in,
+ boolean notify)
+ throws IOException
+ {
+ byte[] buffer = new byte[4096];
+ int contentLength = -1;
+ Headers trailer = null;
+
+ String transferCoding = response.getHeader("Transfer-Encoding");
+ if ("chunked".equalsIgnoreCase(transferCoding))
+ {
+ trailer = new Headers();
+ in = new ChunkedInputStream(in, trailer);
+ }
+ else
+ {
+ contentLength = response.getIntHeader("Content-Length");
+ }
+ String contentCoding = response.getHeader("Content-Encoding");
+ if (contentCoding != null && !"identity".equals(contentCoding))
+ {
+ if ("gzip".equals(contentCoding))
+ {
+ in = new GZIPInputStream(in);
+ }
+ else if ("deflate".equals(contentCoding))
+ {
+ in = new InflaterInputStream(in);
+ }
+ else
+ {
+ throw new ProtocolException("Unsupported Content-Encoding: " +
+ contentCoding);
+ }
+ }
+
+ // Persistent connections are the default in HTTP/1.1
+ boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) ||
+ "close".equalsIgnoreCase(response.getHeader("Connection")) ||
+ (connection.majorVersion == 1 && connection.minorVersion == 0) ||
+ (response.majorVersion == 1 && response.minorVersion == 0);
+
+ int count = contentLength;
+ int len = (count > -1) ? count : buffer.length;
+ len = (len > buffer.length) ? buffer.length : len;
+ while (len > -1)
+ {
+ len = in.read(buffer, 0, len);
+ if (len < 0)
+ {
+ // EOF
+ connection.closeConnection();
+ break;
+ }
+ if (notify)
+ {
+ responseBodyReader.read(buffer, 0, len);
+ }
+ if (count > -1)
+ {
+ count -= len;
+ if (count < 1)
+ {
+ if (doClose)
+ {
+ connection.closeConnection();
+ }
+ break;
+ }
+ }
+ }
+ if (notify)
+ {
+ responseBodyReader.close();
+ }
+ if (trailer != null)
+ {
+ response.getHeaders().putAll(trailer);
+ notifyHeaderHandlers(trailer);
+ }
+ }
+
+ boolean authenticate(Response response, int attempts)
+ throws IOException
+ {
+ String challenge = response.getHeader("WWW-Authenticate");
+ if (challenge == null)
+ {
+ challenge = response.getHeader("Proxy-Authenticate");
+ }
+ int si = challenge.indexOf(' ');
+ String scheme = (si == -1) ? challenge : challenge.substring(0, si);
+ if ("Basic".equalsIgnoreCase(scheme))
+ {
+ Properties params = parseAuthParams(challenge.substring(si + 1));
+ String realm = params.getProperty("realm");
+ Credentials creds = authenticator.getCredentials(realm, attempts);
+ String userPass = creds.getUsername() + ':' + creds.getPassword();
+ byte[] b_userPass = userPass.getBytes("US-ASCII");
+ byte[] b_encoded = BASE64.encode(b_userPass);
+ String authorization =
+ scheme + " " + new String(b_encoded, "US-ASCII");
+ setHeader("Authorization", authorization);
+ return true;
+ }
+ else if ("Digest".equalsIgnoreCase(scheme))
+ {
+ Properties params = parseAuthParams(challenge.substring(si + 1));
+ String realm = params.getProperty("realm");
+ String nonce = params.getProperty("nonce");
+ String qop = params.getProperty("qop");
+ String algorithm = params.getProperty("algorithm");
+ String digestUri = getRequestURI();
+ Credentials creds = authenticator.getCredentials(realm, attempts);
+ String username = creds.getUsername();
+ String password = creds.getPassword();
+ connection.incrementNonce(nonce);
+ try
+ {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ final byte[] COLON = { 0x3a };
+
+ // Calculate H(A1)
+ md5.reset();
+ md5.update(username.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(realm.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(password.getBytes("US-ASCII"));
+ byte[] ha1 = md5.digest();
+ if ("md5-sess".equals(algorithm))
+ {
+ byte[] cnonce = generateNonce();
+ md5.reset();
+ md5.update(ha1);
+ md5.update(COLON);
+ md5.update(nonce.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(cnonce);
+ ha1 = md5.digest();
+ }
+ String ha1Hex = toHexString(ha1);
+
+ // Calculate H(A2)
+ md5.reset();
+ md5.update(method.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(digestUri.getBytes("US-ASCII"));
+ if ("auth-int".equals(qop))
+ {
+ byte[] hEntity = null; // TODO hash of entity body
+ md5.update(COLON);
+ md5.update(hEntity);
+ }
+ byte[] ha2 = md5.digest();
+ String ha2Hex = toHexString(ha2);
+
+ // Calculate response
+ md5.reset();
+ md5.update(ha1Hex.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(nonce.getBytes("US-ASCII"));
+ if ("auth".equals(qop) || "auth-int".equals(qop))
+ {
+ String nc = getNonceCount(nonce);
+ byte[] cnonce = generateNonce();
+ md5.update(COLON);
+ md5.update(nc.getBytes("US-ASCII"));
+ md5.update(COLON);
+ md5.update(cnonce);
+ md5.update(COLON);
+ md5.update(qop.getBytes("US-ASCII"));
+ }
+ md5.update(COLON);
+ md5.update(ha2Hex.getBytes("US-ASCII"));
+ String digestResponse = toHexString(md5.digest());
+
+ String authorization = scheme +
+ " username=\"" + username + "\"" +
+ " realm=\"" + realm + "\"" +
+ " nonce=\"" + nonce + "\"" +
+ " uri=\"" + digestUri + "\"" +
+ " response=\"" + digestResponse + "\"";
+ setHeader("Authorization", authorization);
+ return true;
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ return false;
+ }
+ }
+ // Scheme not recognised
+ return false;
+ }
+
+ Properties parseAuthParams(String text)
+ {
+ int len = text.length();
+ String key = null;
+ StringBuffer buf = new StringBuffer();
+ Properties ret = new Properties();
+ boolean inQuote = false;
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ if (c == '"')
+ {
+ inQuote = !inQuote;
+ }
+ else if (c == '=' && key == null)
+ {
+ key = buf.toString().trim();
+ buf.setLength(0);
+ }
+ else if (c == ' ' && !inQuote)
+ {
+ String value = unquote(buf.toString().trim());
+ ret.put(key, value);
+ key = null;
+ buf.setLength(0);
+ }
+ else if (c != ',' || (i <(len - 1) && text.charAt(i + 1) != ' '))
+ {
+ buf.append(c);
+ }
+ }
+ if (key != null)
+ {
+ String value = unquote(buf.toString().trim());
+ ret.put(key, value);
+ }
+ return ret;
+ }
+
+ String unquote(String text)
+ {
+ int len = text.length();
+ if (len > 0 && text.charAt(0) == '"' && text.charAt(len - 1) == '"')
+ {
+ return text.substring(1, len - 1);
+ }
+ return text;
+ }
+
+ /**
+ * Returns the number of times the specified nonce value has been seen.
+ * This always returns an 8-byte 0-padded hexadecimal string.
+ */
+ String getNonceCount(String nonce)
+ {
+ int nc = connection.getNonceCount(nonce);
+ String hex = Integer.toHexString(nc);
+ StringBuffer buf = new StringBuffer();
+ for (int i = 8 - hex.length(); i > 0; i--)
+ {
+ buf.append('0');
+ }
+ buf.append(hex);
+ return buf.toString();
+ }
+
+ /**
+ * Client nonce value.
+ */
+ byte[] nonce;
+
+ /**
+ * Generates a new client nonce value.
+ */
+ byte[] generateNonce()
+ throws IOException, NoSuchAlgorithmException
+ {
+ if (nonce == null)
+ {
+ long time = System.currentTimeMillis();
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ md5.update(Long.toString(time).getBytes("US-ASCII"));
+ nonce = md5.digest();
+ }
+ return nonce;
+ }
+
+ String toHexString(byte[] bytes)
+ {
+ char[] ret = new char[bytes.length * 2];
+ for (int i = 0, j = 0; i < bytes.length; i++)
+ {
+ int c =(int) bytes[i];
+ if (c < 0)
+ {
+ c += 0x100;
+ }
+ ret[j++] = Character.forDigit(c / 0x10, 0x10);
+ ret[j++] = Character.forDigit(c % 0x10, 0x10);
+ }
+ return new String(ret);
+ }
+
+ /**
+ * Parse the specified cookie list and notify the cookie manager.
+ */
+ void handleSetCookie(String text)
+ {
+ CookieManager cookieManager = connection.getCookieManager();
+ if (cookieManager == null)
+ {
+ return;
+ }
+ String name = null;
+ String value = null;
+ String comment = null;
+ String domain = connection.getHostName();
+ String path = this.path;
+ int lsi = path.lastIndexOf('/');
+ if (lsi != -1)
+ {
+ path = path.substring(0, lsi);
+ }
+ boolean secure = false;
+ Date expires = null;
+
+ int len = text.length();
+ String attr = null;
+ StringBuffer buf = new StringBuffer();
+ boolean inQuote = false;
+ for (int i = 0; i <= len; i++)
+ {
+ char c =(i == len) ? '\u0000' : text.charAt(i);
+ if (c == '"')
+ {
+ inQuote = !inQuote;
+ }
+ else if (!inQuote)
+ {
+ if (c == '=' && attr == null)
+ {
+ attr = buf.toString().trim();
+ buf.setLength(0);
+ }
+ else if (c == ';' || i == len || c == ',')
+ {
+ String val = unquote(buf.toString().trim());
+ if (name == null)
+ {
+ name = attr;
+ value = val;
+ }
+ else if ("Comment".equalsIgnoreCase(attr))
+ {
+ comment = val;
+ }
+ else if ("Domain".equalsIgnoreCase(attr))
+ {
+ domain = val;
+ }
+ else if ("Path".equalsIgnoreCase(attr))
+ {
+ path = val;
+ }
+ else if ("Secure".equalsIgnoreCase(val))
+ {
+ secure = true;
+ }
+ else if ("Max-Age".equalsIgnoreCase(attr))
+ {
+ int delta = Integer.parseInt(val);
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(System.currentTimeMillis());
+ cal.add(Calendar.SECOND, delta);
+ expires = cal.getTime();
+ }
+ else if ("Expires".equalsIgnoreCase(attr))
+ {
+ DateFormat dateFormat = new HTTPDateFormat();
+ try
+ {
+ expires = dateFormat.parse(val);
+ }
+ catch (ParseException e)
+ {
+ // if this isn't a valid date, it may be that
+ // the value was returned unquoted; in that case, we
+ // want to continue buffering the value
+ buf.append(c);
+ continue;
+ }
+ }
+ attr = null;
+ buf.setLength(0);
+ // case EOL
+ if (i == len || c == ',')
+ {
+ Cookie cookie = new Cookie(name, value, comment, domain,
+ path, secure, expires);
+ cookieManager.setCookie(cookie);
+ }
+ if (c == ',')
+ {
+ // Reset cookie fields
+ name = null;
+ value = null;
+ comment = null;
+ domain = connection.getHostName();
+ path = this.path;
+ if (lsi != -1)
+ {
+ path = path.substring(0, lsi);
+ }
+ secure = false;
+ expires = null;
+ }
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ else
+ {
+ buf.append(c);
+ }
+ }
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java b/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java
new file mode 100644
index 00000000000..05d98ebb81a
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/RequestBodyWriter.java
@@ -0,0 +1,69 @@
+/* RequestBodyWriter.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Callback interface for writing request body content.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface RequestBodyWriter
+{
+
+ /**
+ * Returns the total number of bytes that will be written in a single pass
+ * by this writer.
+ */
+ int getContentLength();
+
+ /**
+ * Initialises the writer.
+ * This will be called before each pass.
+ */
+ void reset();
+
+ /**
+ * Writes body content to the supplied buffer.
+ * @param buffer the content buffer
+ * @return the number of bytes written
+ */
+ int write(byte[] buffer);
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/Response.java b/libjava/classpath/gnu/java/net/protocol/http/Response.java
new file mode 100644
index 00000000000..29dc28b17d3
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/Response.java
@@ -0,0 +1,185 @@
+/* Response.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.util.Date;
+
+/**
+ * An HTTP response.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class Response
+{
+
+ /**
+ * The HTTP major version of the server issuing the response.
+ */
+ protected final int majorVersion;
+
+ /**
+ * The HTTP minor version of the server issuing the response.
+ */
+ protected final int minorVersion;
+
+ /**
+ * The HTTP status code of the response.
+ */
+ protected final int code;
+
+ /**
+ * The class of the response. This is the most significant digit of the
+ * status code.
+ * <dl>
+ * <dt><code>1xx</code></dt> <dd>Informational response</dd>
+ * <dt><code>2xx</code></dt> <dd>Success</dd>
+ * <dt><code>3xx</code></dt> <dd>Redirection</dd>
+ * <dt><code>4xx</code></dt> <dd>Client error</dd>
+ * <dt><code>5xx</code></dt> <dd>Server error</dd>
+ * </dl>
+ */
+ protected final int codeClass;
+
+ /**
+ * Human-readable text of the response.
+ */
+ protected final String message;
+
+ /**
+ * The response headers.
+ */
+ protected final Headers headers;
+
+ /**
+ * Constructs a new response with the specified parameters.
+ */
+ protected Response(int majorVersion, int minorVersion, int code,
+ int codeClass, String message,
+ Headers headers)
+ {
+ this.majorVersion = majorVersion;
+ this.minorVersion = minorVersion;
+ this.code = code;
+ this.codeClass = codeClass;
+ this.message = message;
+ this.headers = headers;
+ }
+
+ /**
+ * Returns the HTTP major version of the server issuing the response.
+ * @see #majorVersion
+ */
+ public int getMajorVersion()
+ {
+ return majorVersion;
+ }
+
+ /**
+ * Returns the HTTP minor version of the server issuing the response.
+ * @see #minorVersion
+ */
+ public int getMinorVersion()
+ {
+ return minorVersion;
+ }
+
+ /**
+ * Returns the HTTP status code of the response.
+ * @see #code
+ */
+ public int getCode()
+ {
+ return code;
+ }
+
+ /**
+ * Returns the class of the response.
+ * @see #codeClass
+ */
+ public int getCodeClass()
+ {
+ return codeClass;
+ }
+
+ /**
+ * Returns the human-readable text of the response.
+ * @see #message
+ */
+ public String getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * Returns the headers in the response.
+ */
+ public Headers getHeaders()
+ {
+ return headers;
+ }
+
+ /**
+ * Returns the header value for the specified name.
+ * @param name the header name
+ */
+ public String getHeader(String name)
+ {
+ return headers.getValue(name);
+ }
+
+ /**
+ * Returns the header value for the specified name as an integer.
+ * @param name the header name
+ */
+ public int getIntHeader(String name)
+ {
+ return headers.getIntValue(name);
+ }
+
+ /**
+ * Returns the header value for the specified name as a date.
+ * @param name the header name
+ */
+ public Date getDateHeader(String name)
+ {
+ return headers.getDateValue(name);
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/ResponseBodyReader.java b/libjava/classpath/gnu/java/net/protocol/http/ResponseBodyReader.java
new file mode 100644
index 00000000000..49e1b376f0f
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/ResponseBodyReader.java
@@ -0,0 +1,70 @@
+/* ResponseBodyReader.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Callback interface for receiving notification of response body content.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface ResponseBodyReader
+{
+
+ /**
+ * Indicate whether this reader is interested in the specified response.
+ * If it returns false, it will not receive body content notifications for
+ * that response.
+ */
+ boolean accept(Request request, Response response);
+
+ /**
+ * Receive notification of body content.
+ * @param buffer the content buffer
+ * @param offset the offset within the buffer that content starts
+ * @param length the length of the content
+ */
+ void read(byte[] buffer, int offset, int length);
+
+ /**
+ * Notifies the reader that the end of the content was reached.
+ */
+ void close();
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java
new file mode 100644
index 00000000000..8e4e6492acf
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/ResponseHeaderHandler.java
@@ -0,0 +1,57 @@
+/* ResponseHeaderHandler.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+/**
+ * Callback interface for objects that wish to be notified of response
+ * header values.
+ * @see Request#setHeaderHandler(String)
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface ResponseHeaderHandler
+{
+
+ /**
+ * Sets the value for the header associated with this handler.
+ */
+ void setValue(String value);
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java b/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java
new file mode 100644
index 00000000000..8947471885c
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/SimpleCookieManager.java
@@ -0,0 +1,140 @@
+/* CookieManager.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A simple non-persistent cookie manager. This class can be extended to
+ * provide cookie persistence.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class SimpleCookieManager
+ implements CookieManager
+{
+
+ /**
+ * The cookie cache.
+ * This is a dictionary mapping domains to maps of cookies by name.
+ */
+ protected Map cookies;
+
+ /**
+ * Constructor.
+ */
+ public SimpleCookieManager()
+ {
+ cookies = new HashMap();
+ }
+
+ public void setCookie(Cookie cookie)
+ {
+ String domain = cookie.getDomain();
+ Map map =(Map) cookies.get(domain);
+ if (map == null)
+ {
+ map = new HashMap();
+ cookies.put(domain, map);
+ }
+ String name = cookie.getName();
+ map.put(name, cookie); // will replace a cookie of the same name
+ }
+
+ public Cookie[] getCookies(String host, boolean secure, String path)
+ {
+ List matches = new ArrayList();
+ Date now = new Date();
+ if (Character.isLetter(host.charAt(0)))
+ {
+ int di = host.indexOf('.');
+ while (di != -1)
+ {
+ addCookies(matches, host, secure, path, now);
+ host = host.substring(di);
+ di = host.indexOf('.', 1);
+ }
+ }
+ addCookies(matches, host, secure, path, now);
+ Cookie[] ret = new Cookie[matches.size()];
+ matches.toArray(ret);
+ return ret;
+ }
+
+ private void addCookies(List matches, String domain, boolean secure,
+ String path, Date now)
+ {
+ Map map = (Map) cookies.get(domain);
+ if (map != null)
+ {
+ List expired = new ArrayList();
+ for (Iterator i = map.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ Cookie cookie = (Cookie) entry.getValue();
+ Date expires = cookie.getExpiryDate();
+ if (expires != null && expires.before(now))
+ {
+ expired.add(entry.getKey());
+ continue;
+ }
+ if (secure && !cookie.isSecure())
+ {
+ continue;
+ }
+ if (path.startsWith(cookie.getPath()))
+ {
+ matches.add(cookie);
+ }
+ }
+ // Good housekeeping
+ for (Iterator i = expired.iterator(); i.hasNext(); )
+ {
+ map.remove(i.next());
+ }
+ }
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionEvent.java b/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionEvent.java
new file mode 100644
index 00000000000..3f6f5454e73
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionEvent.java
@@ -0,0 +1,81 @@
+/* ConnectionEvent.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http.event;
+
+import java.util.EventObject;
+
+/**
+ * A connection event.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class ConnectionEvent
+ extends EventObject
+{
+
+ /**
+ * The connection closed event type.
+ */
+ public static final int CONNECTION_CLOSED = 0;
+
+ /**
+ * The type of this event.
+ */
+ protected int type;
+
+ /**
+ * Constructs a connection event with the specified source and type.
+ */
+ public ConnectionEvent(Object source, int type)
+ {
+ super(source);
+ this.type = type;
+ }
+
+ /**
+ * Returns the type of this event.
+ * @see #type
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionListener.java b/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionListener.java
new file mode 100644
index 00000000000..073e89d4407
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/event/ConnectionListener.java
@@ -0,0 +1,58 @@
+/* ConnectionListener.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http.event;
+
+import java.util.EventListener;
+
+/**
+ * A connection listener.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface ConnectionListener
+ extends EventListener
+{
+
+ /**
+ * Callback invoked when the associated connection is closed.
+ */
+ void connectionClosed(ConnectionEvent event);
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/event/RequestEvent.java b/libjava/classpath/gnu/java/net/protocol/http/event/RequestEvent.java
new file mode 100644
index 00000000000..281c621f33f
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/event/RequestEvent.java
@@ -0,0 +1,107 @@
+/* RequestEvent.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http.event;
+
+import gnu.java.net.protocol.http.Request;
+
+import java.util.EventObject;
+
+/**
+ * A request event.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public class RequestEvent
+ extends EventObject
+{
+
+ /**
+ * The request created event type.
+ */
+ public static final int REQUEST_CREATED = 0;
+
+ /**
+ * The request sending event type.
+ */
+ public static final int REQUEST_SENDING = 1;
+
+ /**
+ * The request sent event type.
+ */
+ public static final int REQUEST_SENT = 2;
+
+ /**
+ * The type of this event.
+ */
+ protected int type;
+
+ /**
+ * The request associated with this event.
+ */
+ protected Request request;
+
+ /**
+ * Constructs a request event with the specified source, type, and request.
+ */
+ public RequestEvent(Object source, int type, Request request)
+ {
+ super(source);
+ this.type = type;
+ this.request = request;
+ }
+
+ /**
+ * Returns the type of this event.
+ * @see #type
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns the request associated with this event.
+ */
+ public Request getRequest()
+ {
+ return request;
+ }
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/event/RequestListener.java b/libjava/classpath/gnu/java/net/protocol/http/event/RequestListener.java
new file mode 100644
index 00000000000..c880fbce6f0
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/event/RequestListener.java
@@ -0,0 +1,70 @@
+/* RequestListener.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http.event;
+
+import java.util.EventListener;
+
+/**
+ * A request listener.
+ *
+ * @author Chris Burdess (dog@gnu.org)
+ */
+public interface RequestListener
+ extends EventListener
+{
+
+ /**
+ * Callback invoked when a request is created from the associated
+ * connection.
+ */
+ void requestCreated(RequestEvent event);
+
+ /**
+ * Callback invoked when the request has been initialised with all data
+ * and before sending this data to the server.
+ */
+ void requestSending(RequestEvent event);
+
+ /**
+ * Callback invoked after all request data has been sent to the server.
+ */
+ void requestSent(RequestEvent event);
+
+}
+
diff --git a/libjava/classpath/gnu/java/net/protocol/http/event/package.html b/libjava/classpath/gnu/java/net/protocol/http/event/package.html
new file mode 100644
index 00000000000..6aed0fc0169
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/event/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in gnu.java.net.protocol.http.event package.
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - gnu.java.net.protocol.http.event</title></head>
+
+<body>
+<p></p>
+
+</body>
+</html>
diff --git a/libjava/classpath/gnu/java/net/protocol/http/package.html b/libjava/classpath/gnu/java/net/protocol/http/package.html
new file mode 100644
index 00000000000..8cf7c1e1663
--- /dev/null
+++ b/libjava/classpath/gnu/java/net/protocol/http/package.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in gnu.java.net.protocol.http package.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - gnu.java.net.protocol.http</title></head>
+
+<body>
+
+<p>
+This package contains an HTTP/1.1 client, as described in RFC 2616.
+It supports the following features:
+<ul>
+<li>Persistent connections</li>
+<li>Basic and Digest authentication (RFC 2617)</li>
+<li>HTTPS</li>
+<li>HTTP proxies</li>
+<li>HTTP/1.0 compatibility</li>
+<li>Support for WebDAV methods and other HTTP extensions</li>
+<li>Automatic decoding of the chunked transfer-coding</li>
+<li>Parsing of HTTP date headers</li>
+<li>Support for the 100-continue expectation</li>
+</ul>
+</p>
+
+<p>
+The API is similar to the <a href='http://www.webdav.org/neon/'>neon</a>
+WebDAV/HTTP library. A logical connection to the server is instantiated,
+and multiple requests can be issued for this connection. Each request
+has an atomic <code>dispatch</code> method which returns the response.
+All I/O, authentication, etc is handled by registering callback objects
+with the request prior to dispatch, which are notified during the dispatch
+procedure as necessary. Simple byte-array content callbacks are supplied
+which can manage any request/response content that fits in available memory.
+</p>
+
+<p>
+An URL stream handler is provided, supporting the full HttpURLConnection
+specification.
+</p>
+
+</body>