diff options
author | Michael Koch <konqueror@gmx.de> | 2004-11-30 22:15:02 +0000 |
---|---|---|
committer | Michael Koch <konqueror@gmx.de> | 2004-11-30 22:15:02 +0000 |
commit | 9748ead9ac1814a77a84be8180d447f453facf5d (patch) | |
tree | b14efca33582f8f7615dc9353aedca3db38d24e6 /gnu | |
parent | 32446f486f3619c654fe2bf90911a77a4135dfd3 (diff) | |
download | classpath-9748ead9ac1814a77a84be8180d447f453facf5d.tar.gz |
2004-11-30 Michael Koch <konqueror@gmx.de>
* gnu/java/net/BASE64.java,
gnu/java/net/CRLFInputStream.java,
gnu/java/net/CRLFOutputStream.java,
gnu/java/net/EmptyX509TrustManager.java,
gnu/java/net/GetLocalHostAction.java,
gnu/java/net/GetSystemPropertyAction.java,
gnu/java/net/LineInputStream.java,
gnu/java/net/protocol/http/Authenticator.java,
gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java,
gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java,
gnu/java/net/protocol/http/ChunkedInputStream.java,
gnu/java/net/protocol/http/Cookie.java,
gnu/java/net/protocol/http/CookieManager.java,
gnu/java/net/protocol/http/Credentials.java,
gnu/java/net/protocol/http/HTTPConnection.java,
gnu/java/net/protocol/http/HTTPDateFormat.java,
gnu/java/net/protocol/http/HTTPURLConnection.java,
gnu/java/net/protocol/http/Headers.java,
gnu/java/net/protocol/http/Request.java,
gnu/java/net/protocol/http/RequestBodyWriter.java,
gnu/java/net/protocol/http/Response.java,
gnu/java/net/protocol/http/ResponseBodyReader.java,
gnu/java/net/protocol/http/ResponseHeaderHandler.java,
gnu/java/net/protocol/http/SimpleCookieManager.java,
gnu/java/net/protocol/http/event/ConnectionEvent.java,
gnu/java/net/protocol/http/event/ConnectionListener.java,
gnu/java/net/protocol/http/event/RequestEvent.java,
gnu/java/net/protocol/http/event/RequestListener.java:
New files
* gnu/java/net/protocol/http/Connection.java:
gnu/java/net/protocol/http/TODO: Removed
* gnu/java/net/protocol/http/Handler.java: Updated.
Merged HTTP protocol implementation from GNU inetlib.
Diffstat (limited to 'gnu')
31 files changed, 5462 insertions, 601 deletions
diff --git a/gnu/java/net/BASE64.java b/gnu/java/net/BASE64.java new file mode 100644 index 000000000..e711bb6e8 --- /dev/null +++ b/gnu/java/net/BASE64.java @@ -0,0 +1,184 @@ +/* BASE.java -- + Copyright (C) 2003, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +/** + * Encodes and decodes text according to the BASE64 encoding. + * + * @author Chris Burdess (dog@gnu.org) + */ +public final class BASE64 +{ + private static final byte[] src = { + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x2b, 0x2f + }; + + private static final byte[] dst; + static + { + dst = new byte[0x100]; + for (int i = 0x0; i < 0xff; i++) + { + dst[i] = -1; + } + for (int i = 0; i < src.length; i++) + { + dst[src[i]] = (byte) i; + } + } + + private BASE64() + { + } + + /** + * Encode the specified byte array using the BASE64 algorithm. + * + * @param bs the source byte array + */ + public static byte[] encode(byte[] bs) + { + int si = 0, ti = 0; // source/target array indices + byte[] bt = new byte[((bs.length + 2) * 4) / 3]; // target byte array + for (; si < bs.length; si += 3) + { + int buflen = bs.length - si; + if (buflen == 1) + { + byte b = bs[si]; + int i = 0; + bt[ti++] = src[b >>> 2 & 0x3f]; + bt[ti++] = src[(b << 4 & 0x30) + (i >>> 4 & 0xf)]; + } + else if (buflen == 2) + { + byte b1 = bs[si], b2 = bs[si + 1]; + int i = 0; + bt[ti++] = src[b1 >>> 2 & 0x3f]; + bt[ti++] = src[(b1 << 4 & 0x30) + (b2 >>> 4 & 0xf)]; + bt[ti++] = src[(b2 << 2 & 0x3c) + (i >>> 6 & 0x3)]; + } + else + { + byte b1 = bs[si], b2 = bs[si + 1], b3 = bs[si + 2]; + bt[ti++] = src[b1 >>> 2 & 0x3f]; + bt[ti++] = src[(b1 << 4 & 0x30) + (b2 >>> 4 & 0xf)]; + bt[ti++] = src[(b2 << 2 & 0x3c) + (b3 >>> 6 & 0x3)]; + bt[ti++] = src[b3 & 0x3f]; + } + } + /*while (ti < bt.length) + { + bt[ti++] = 0x3d; + }*/ + return bt; + } + + /** + * Decode the specified byte array using the BASE64 algorithm. + * + * @param bs the source byte array + */ + public static byte[] decode(byte[] bs) + { + int srclen = bs.length; + while (srclen > 0 && bs[srclen - 1] == 0x3d) + { + srclen--; /* strip padding character */ + } + byte[] buffer = new byte[srclen]; + int buflen = 0; + int si = 0; + int len = srclen - si; + while (len > 0) + { + byte b0 = dst[bs[si++] & 0xff]; + byte b2 = dst[bs[si++] & 0xff]; + buffer[buflen++] = (byte) (b0 << 2 & 0xfc | b2 >>> 4 & 0x3); + if (len > 2) + { + b0 = b2; + b2 = dst[bs[si++] & 0xff]; + buffer[buflen++] = (byte) (b0 << 4 & 0xf0 | b2 >>> 2 & 0xf); + if (len > 3) + { + b0 = b2; + b2 = dst[bs[si++] & 0xff]; + buffer[buflen++] = (byte) (b0 << 6 & 0xc0 | b2 & 0x3f); + } + } + len = srclen - si; + } + byte[] bt = new byte[buflen]; + System.arraycopy(buffer, 0, bt, 0, buflen); + return bt; + } + + public static void main(String[] args) + { + boolean decode = false; + for (int i = 0; i < args.length; i++) + { + if (args[i].equals("-d")) + { + decode = true; + } + else + { + try + { + byte[] in = args[i].getBytes("US-ASCII"); + byte[] out = decode ? decode(in) : encode(in); + System.out.println(args[i] + " = " + + new String(out, "US-ASCII")); + } + catch (java.io.UnsupportedEncodingException e) + { + e.printStackTrace(System.err); + } + } + } + } +} diff --git a/gnu/java/net/CRLFInputStream.java b/gnu/java/net/CRLFInputStream.java new file mode 100644 index 000000000..706c23447 --- /dev/null +++ b/gnu/java/net/CRLFInputStream.java @@ -0,0 +1,205 @@ +/* CRLFInputStream.java -- + Copyright (C) 2002, 2003, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * An input stream that filters out CR/LF pairs into LFs. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class CRLFInputStream + extends FilterInputStream +{ + /** + * The CR octet. + */ + public static final int CR = 13; + + /** + * The LF octet. + */ + public static final int LF = 10; + + /** + * Buffer. + */ + protected int buf = -1; + + /** + * Buffer at time of mark. + */ + protected int markBuf = -1; + + /** + * Constructs a CR/LF input stream connected to the specified input + * stream. + */ + public CRLFInputStream(InputStream in) + { + super(in); + } + + /** + * Reads the next byte of data from this input stream. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read() + throws IOException + { + int c; + if (buf != -1) + { + c = buf; + buf = -1; + return c; + } + else + { + c = super.read(); + if (c == CR) + { + buf = super.read(); + if (buf == LF) + { + c = buf; + buf = -1; + } + } + } + return c; + } + + /** + * Reads up to b.length bytes of data from this input stream into + * an array of bytes. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read(byte[] b) + throws IOException + { + return read(b, 0, b.length); + } + + /** + * Reads up to len bytes of data from this input stream into an + * array of bytes, starting at the specified offset. + * Returns -1 if the end of the stream has been reached. + * @exception IOException if an I/O error occurs + */ + public int read(byte[] b, int off, int len) + throws IOException + { + int shift = 0; + if (buf != -1) + { + // Push buf onto start of byte array + b[off] = (byte) buf; + off++; + len--; + buf = -1; + shift++; + } + int l = super.read(b, off, len); + l = removeCRLF(b, off - shift, l); + return l; + } + + /** + * Indicates whether this stream supports the mark and reset methods. + */ + public boolean markSupported() + { + return in.markSupported(); + } + + /** + * Marks the current position in this stream. + */ + public void mark(int readlimit) + { + in.mark(readlimit); + markBuf = buf; + } + + /** + * Repositions this stream to the position at the time the mark method was + * called. + */ + public void reset() + throws IOException + { + in.reset(); + buf = markBuf; + } + + private int removeCRLF(byte[] b, int off, int len) + { + int end = off + len; + for (int i = off; i < end; i++) + { + if (b[i] == CR) + { + if (i + 1 == end) + { + // This is the last byte, impossible to determine whether CRLF + buf = CR; + len--; + } + else if (b[i + 1] == LF) + { + // Shift left + end--; + for (int j = i; j < end; j++) + { + b[j] = b[j + 1]; + } + len--; + end = off + len; + } + } + } + return len; + } +} diff --git a/gnu/java/net/CRLFOutputStream.java b/gnu/java/net/CRLFOutputStream.java new file mode 100644 index 000000000..665a02b62 --- /dev/null +++ b/gnu/java/net/CRLFOutputStream.java @@ -0,0 +1,183 @@ +/* CRLFOutputStream.java -- + Copyright (C) 2002, 2003, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +/** + * An output stream that filters LFs into CR/LF pairs. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class CRLFOutputStream + extends FilterOutputStream +{ + static final String US_ASCII = "US-ASCII"; + + /** + * The CR octet. + */ + public static final int CR = 13; + + /** + * The LF octet. + */ + public static final int LF = 10; + + /** + * The CR/LF pair. + */ + public static final byte[] CRLF = { CR, LF }; + + /** + * The last byte read. + */ + protected int last; + + /** + * Constructs a CR/LF output stream connected to the specified output stream. + */ + public CRLFOutputStream(OutputStream out) + { + super(out); + last = -1; + } + + /** + * Writes a character to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(int ch) throws IOException + { + if (ch == CR) + { + out.write(CRLF); + } + else if (ch == LF) + { + if (last != CR) + { + out.write(CRLF); + } + } + else + { + out.write(ch); + } + last = ch; + } + + /** + * Writes a byte array to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + /** + * Writes a portion of a byte array to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(byte[] b, int off, int len) + throws IOException + { + int d = off; + len += off; + for (int i = off; i < len; i++) + { + switch (b[i]) + { + case CR: + out.write (b, d, i - d); + out.write (CRLF, 0, 2); + d = i + 1; + break; + case LF: + if (last != CR) + { + out.write (b, d, i - d); + out.write (CRLF, 0, 2); + } + d = i + 1; + break; + } + last = b[i]; + } + if (len - d > 0) + { + out.write (b, d, len - d); + } + } + + /** + * Writes the specified ASCII string to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void write(String text) + throws IOException + { + try + { + byte[] bytes = text.getBytes(US_ASCII); + write(bytes, 0, bytes.length); + } + catch (UnsupportedEncodingException e) + { + throw new IOException("The US-ASCII encoding is not supported " + + "on this system"); + } + } + + /** + * Writes a newline to the underlying stream. + * @exception IOException if an I/O error occurred + */ + public void writeln() + throws IOException + { + out.write(CRLF, 0, 2); + } +} + diff --git a/gnu/java/net/EmptyX509TrustManager.java b/gnu/java/net/EmptyX509TrustManager.java new file mode 100644 index 000000000..c7e9055a3 --- /dev/null +++ b/gnu/java/net/EmptyX509TrustManager.java @@ -0,0 +1,69 @@ +/* EmptyX509TrustManager.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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.X509TrustManager; + +/** + * Empty implementation of an X509 trust manager. + * This implementation does not check any certificates in the chain. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class EmptyX509TrustManager + implements X509TrustManager +{ + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + } + + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[0]; + } +} + diff --git a/gnu/java/net/GetLocalHostAction.java b/gnu/java/net/GetLocalHostAction.java new file mode 100644 index 000000000..36d17d9df --- /dev/null +++ b/gnu/java/net/GetLocalHostAction.java @@ -0,0 +1,65 @@ +/* GetLocalHostAction.java -- + Copyright (C) 2003, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.PrivilegedAction; + +/** + * Privileged action to retrieve the local host InetAddress. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class GetLocalHostAction + implements PrivilegedAction +{ + public Object run() + { + try + { + return InetAddress.getLocalHost(); + } + catch (UnknownHostException e) + { + return null; + } + } +} + diff --git a/gnu/java/net/GetSystemPropertyAction.java b/gnu/java/net/GetSystemPropertyAction.java new file mode 100644 index 000000000..d31fc7e2d --- /dev/null +++ b/gnu/java/net/GetSystemPropertyAction.java @@ -0,0 +1,67 @@ +/* GetSystemPropertyAction.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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.security.PrivilegedAction; + +/** + * Privileged action for retrieving system properties. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class GetSystemPropertyAction + implements PrivilegedAction +{ + final String name; + + /** + * Constructor. + * @param name the the name of the system property to retrieve + */ + public GetSystemPropertyAction(String name) + { + this.name = name; + } + + public Object run() + { + return System.getProperty(name); + } +} + diff --git a/gnu/java/net/LineInputStream.java b/gnu/java/net/LineInputStream.java new file mode 100644 index 000000000..71ee35e17 --- /dev/null +++ b/gnu/java/net/LineInputStream.java @@ -0,0 +1,198 @@ +/* LineInputStream.java -- + Copyright (C) 2002, 2003, 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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; + +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * An input stream that can read lines of input. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class LineInputStream + extends FilterInputStream +{ + /* + * Line buffer. + */ + private ByteArrayOutputStream buf; + + /* + * Encoding to use when translating bytes to characters. + */ + private String encoding; + + /* + * End-of-stream flag. + */ + private boolean eof; + + /** + * Whether we can use block reads. + */ + private final boolean blockReads; + + /** + * Constructor using the US-ASCII character encoding. + * @param in the underlying input stream + */ + public LineInputStream(InputStream in) + { + this(in, "US-ASCII"); + } + + /** + * Constructor. + * @param in the underlying input stream + * @param encoding the character encoding to use + */ + public LineInputStream(InputStream in, String encoding) + { + super(in); + buf = new ByteArrayOutputStream(); + this.encoding = encoding; + eof = false; + blockReads = in.markSupported(); + } + + /** + * Read a line of input. + */ + public String readLine() + throws IOException + { + if (eof) + { + return null; + } + do + { + if (blockReads) + { + // Use mark and reset to read chunks of bytes + final int MIN_LENGTH = 1024; + int len, pos; + + len = in.available(); + len = (len < MIN_LENGTH) ? MIN_LENGTH : len; + byte[] b = new byte[len]; + in.mark(len); + // Read into buffer b + len = in.read(b, 0, len); + // Handle EOF + if (len == -1) + { + eof = true; + if (buf.size() == 0) + { + return null; + } + else + { + // We don't care about resetting buf + return buf.toString(encoding); + } + } + // Get index of LF in b + pos = indexOf(b, len, (byte) 0x0a); + if (pos != -1) + { + // Write pos bytes to buf + buf.write(b, 0, pos); + // Reset stream, and read pos + 1 bytes + in.reset(); + pos += 1; + while (pos > 0) + { + len = in.read(b, 0, pos); + pos -= len; + } + // Return line + String ret = buf.toString(encoding); + buf.reset(); + return ret; + } + else + { + // Append everything to buf and fall through to re-read. + buf.write(b, 0, len); + } + } + else + { + // We must use character reads in order not to read too much + // from the underlying stream. + int c = in.read(); + switch (c) + { + case -1: + eof = true; + if (buf.size() == 0) + { + return null; + } + // Fall through and return contents of buffer. + case 0x0a: // LF + String ret = buf.toString(encoding); + buf.reset(); + return ret; + default: + buf.write(c); + } + } + } + while (true); + } + + private int indexOf(byte[] b, int len, byte c) + { + for (int pos = 0; pos < len; pos++) + { + if (b[pos] == c) + { + return pos; + } + } + return -1; + } +} + diff --git a/gnu/java/net/protocol/http/Authenticator.java b/gnu/java/net/protocol/http/Authenticator.java new file mode 100644 index 000000000..1943810c9 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java b/gnu/java/net/protocol/http/ByteArrayRequestBodyWriter.java new file mode 100644 index 000000000..d817114dc --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java b/gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java new file mode 100644 index 000000000..1e615916c --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/ChunkedInputStream.java b/gnu/java/net/protocol/http/ChunkedInputStream.java new file mode 100644 index 000000000..cb051bf71 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.InputStream; +import java.io.IOException; +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/gnu/java/net/protocol/http/Connection.java b/gnu/java/net/protocol/http/Connection.java deleted file mode 100644 index aac565c71..000000000 --- a/gnu/java/net/protocol/http/Connection.java +++ /dev/null @@ -1,553 +0,0 @@ -/* HttpURLConnection.java -- URLConnection class for HTTP protocol - Copyright (C) 1998, 1999, 2000, 2002, 2003, 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., 59 Temple Place, Suite 330, Boston, MA -02111-1307 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.HeaderFieldHelper; -import gnu.java.security.action.GetPropertyAction; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.InputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.ProtocolException; -import java.net.Socket; -import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -/** - * This subclass of java.net.URLConnection models a URLConnection via - * the HTTP protocol. - * - * Status: Minimal subset of functionality. Proxies only partially - * handled; Redirects not yet handled. FileNameMap handling needs to - * be considered. useCaches, ifModifiedSince, and - * allowUserInteraction need consideration as well as doInput and - * doOutput. - * - * @author Aaron M. Renn <arenn@urbanophile.com> - * @author Warren Levy <warrenl@cygnus.com> - */ -public final class Connection extends HttpURLConnection -{ - /** - * The socket we are connected to - */ - private Socket socket; - - // Properties depeending on system properties settings - static int proxyPort = 80; - static boolean proxyInUse = false; - static String proxyHost = null; - static String userAgent; - - static - { - // Recognize some networking properties listed at - // http://java.sun.com/j2se/1.4/docs/guide/net/properties.html. - String port = null; - GetPropertyAction getProperty = new GetPropertyAction("http.proxyHost"); - proxyHost = (String) AccessController.doPrivileged(getProperty); - if (proxyHost != null) - { - proxyInUse = true; - getProperty.setParameters("http.proxyPort"); - port = (String) AccessController.doPrivileged(getProperty); - if (port != null) - { - try - { - proxyPort = Integer.parseInt(port); - } - catch (NumberFormatException ex) - { - // Nothing. - } - } - } - - getProperty.setParameters("http.agent"); - userAgent = (String) AccessController.doPrivileged(getProperty); - } - - /** - * The InputStream for this connection. - */ - private DataInputStream inputStream; - - /** - * The OutputStream for this connection - */ - private OutputStream outputStream; - - /** - * bufferedOutputStream is a buffer to contain content of the HTTP request, - * and will be written to outputStream all at once - */ - private ByteArrayOutputStream bufferedOutputStream; - - /** - * This object holds the request properties. - */ - private HashMap requestProperties = new HashMap(); - - /** - * This is the object that holds the header field information - */ - private HeaderFieldHelper headers = new HeaderFieldHelper(); - - /** - * Calls superclass constructor to initialize - */ - protected Connection(URL url) - { - super(url); - - /* Set up some variables */ - doOutput = false; - } - - /** - * Connects to the remote host, sends the request, and parses the reply - * code and header information returned - */ - public void connect() throws IOException - { - // Call is ignored if already connected. - if (connected) - return; - - // Get address and port number. - int port; - if (proxyInUse) - { - port = proxyPort; - socket = new Socket(proxyHost, port); - } - else - { - if ((port = url.getPort()) == -1) - port = 80; - // Open socket and output stream. - socket = new Socket(url.getHost(), port); - } - - inputStream = - new DataInputStream(new BufferedInputStream(socket.getInputStream())); - outputStream = new BufferedOutputStream (socket.getOutputStream()); - - sendRequest(); - receiveReply(); - - connected = true; - } - - /** - * Disconnects from the remote server. - */ - public void disconnect() - { - if (socket != null) - { - try - { - socket.close(); - } - catch (IOException e) - { - // Ignore errors in closing socket. - } - socket = null; - } - } - - /** - * Write HTTP request header and content to outputWriter. - */ - void sendRequest() throws IOException - { - // Create PrintWriter for easier sending of headers. - PrintWriter outputWriter = - new PrintWriter(new OutputStreamWriter(outputStream, "8859_1")); - - // Send request including any request properties that were set. - String requestFile = url.getFile(); - outputWriter.print(getRequestMethod() + " " - + (requestFile.length() != 0 ? requestFile : "/") - + " HTTP/1.1\r\n"); - - // Set additional HTTP headers. - if (getRequestProperty ("Host") == null) - setRequestProperty ("Host", url.getHost()); - - if (getRequestProperty ("Connection") == null) - setRequestProperty ("Connection", "Close"); - - if (getRequestProperty ("user-agent") == null) - setRequestProperty ("user-agent", userAgent); - - if (getRequestProperty ("accept") == null) - setRequestProperty ("accept", "*/*"); - - if (getRequestProperty ("Content-type") == null) - setRequestProperty ("Content-type", "application/x-www-form-urlencoded"); - - // Set correct content length. - if (bufferedOutputStream != null) - setRequestProperty ("Content-length", String.valueOf (bufferedOutputStream.size())); - - // Write all req_props name-value pairs to the output writer. - Iterator itr = getRequestProperties().entrySet().iterator(); - - while (itr.hasNext()) - { - Map.Entry e = (Map.Entry) itr.next(); - outputWriter.print (e.getKey() + ": " + e.getValue() + "\r\n"); - } - - // One more CR-LF indicates end of header. - outputWriter.print ("\r\n"); - outputWriter.flush(); - - // Write content - if (bufferedOutputStream != null) - { - bufferedOutputStream.writeTo (outputStream); - outputStream.flush(); - } - } - - /** - * Read HTTP reply from inputStream. - */ - private void receiveReply() throws IOException - { - // Parse the reply - String line = inputStream.readLine(); - String saveline = line; - int idx = line.indexOf (" "); - - if ((idx == -1) - || (line.length() < (idx + 6))) - throw new IOException ("Server reply was unparseable: " + saveline); - - headers.addHeaderField (null, line); - - line = line.substring (idx + 1); - String code = line.substring (0, 3); - - try - { - responseCode = Integer.parseInt (code); - } - catch (NumberFormatException e) - { - throw new IOException ("Server reply was unparseable: " + saveline); - } - - responseMessage = line.substring (4); - - // Now read the header lines - String key = null, value = null; - - while (true) - { - line = inputStream.readLine(); - - if (line.equals("")) - break; - - // Check for folded lines - if (line.startsWith (" ") - || line.startsWith("\t")) - { - // Trim off leading space - do - { - if (line.length() == 1) - throw new IOException("Server header lines were unparseable: " - + line); - - line = line.substring (1); - } - while (line.startsWith(" ") - || line.startsWith("\t")); - - value = value + " " + line; - } - else - { - if (key != null) - { - headers.addHeaderField (key.toLowerCase(), value); - key = null; - value = null; - } - - // Parse out key and value - idx = line.indexOf (":"); - if ((idx == -1) - || (line.length() < (idx + 2))) - throw new IOException ("Server header lines were unparseable: " - + line); - - key = line.substring (0, idx); - value = line.substring (idx + 1); - - // Trim off leading space - while (value.startsWith (" ") - || value.startsWith ("\t")) - { - if (value.length() == 1) - throw new IOException ("Server header lines were unparseable: " - + line); - - value = value.substring (1); - } - } - } - - if (key != null) - { - headers.addHeaderField (key.toLowerCase(), value.toLowerCase()); - } - } - - /** - * Return a boolean indicating whether or not this connection is - * going through a proxy - * - * @return true if using a proxy, false otherwise - */ - public boolean usingProxy() - { - return proxyInUse; - } - - /** - * Returns an InputStream for reading from this connection. This stream - * will be "queued up" for reading just the contents of the requested file. - * Overrides URLConnection.getInputStream() - * - * @return An InputStream for this connection. - * - * @exception IOException If an error occurs - */ - public InputStream getInputStream() throws IOException - { - if (!connected) - connect(); - - if (!doInput) - throw new ProtocolException("Can't open InputStream if doInput is false"); - - return inputStream; - } - - /** - * Returns on OutputStream for writing to this connection. This method - * implicitely changes request method to <code>POST</code>. - * - * @return An OutputStream for this connection. - * - * @exception IOException If an error occurs - */ - public OutputStream getOutputStream() throws IOException - { - if (connected) - throw new ProtocolException - ("You cannot get an output stream for an existing http connection"); - - if (!doOutput) - throw new ProtocolException - ("Want output stream while haven't setDoOutput(true)"); - - if (bufferedOutputStream == null) - bufferedOutputStream = new ByteArrayOutputStream (256); //default is too small - - // Force POST request method. - setRequestMethod("POST"); - - return bufferedOutputStream; - } - - /** - * Overrides java.net.HttpURLConnection.setRequestMethod() in order to - * restrict the available methods to only those we support. - * - * @param method The RequestMethod to use - * - * @exception ProtocolException If the specified method is not valid - */ - public void setRequestMethod (String method) throws ProtocolException - { - method = method.toUpperCase(); - - if (method.equals("GET") - || method.equals("HEAD") - || method.equals("POST")) - super.setRequestMethod (method); - else - throw new ProtocolException ("Unsupported or unknown request method " + - method); - } - - public void addRequestProperty(String key, String value) - { - if (connected) - throw new IllegalStateException("Already connected"); - - String old = (String) requestProperties.put(key, value); - - if (old != null) - requestProperties.put(key, old + "," + value); - } - - public String getRequestProperty(String key) - { - if (connected) - throw new IllegalStateException("Already connected"); - - return (String) requestProperties.get(key); - } - - public void setRequestProperty(String key, String value) - { - if (connected) - throw new IllegalStateException("Already connected"); - - requestProperties.put(key, value); - } - - public Map getRequestProperties() - { - if (connected) - throw new IllegalStateException("Already connected"); - - return requestProperties; - } - - public String getHeaderField(String name) - { - if (!connected) - try - { - connect(); - } - catch (IOException x) - { - return null; - } - - return (String) headers.getHeaderFieldValueByKey(name.toLowerCase()); - } - - public Map getHeaderFields() - { - if (!connected) - try - { - connect(); - } - catch (IOException x) - { - return null; - } - - return headers.getHeaderFields(); - } - - /** - * This method returns the header field value at the specified numeric - * index. - * - * @param n The index into the header field array - * - * @return The value of the specified header field, or <code>null</code> - * if the specified index is not valid. - */ - public String getHeaderField(int n) - { - if (!connected) - try - { - connect(); - } - catch (IOException x) - { - return null; - } - - return headers.getHeaderFieldValueByIndex (n); - } - - /** - * This method returns the header field key at the specified numeric - * index. - * - * @param n The index into the header field array - * - * @return The name of the header field key, or <code>null</code> if the - * specified index is not valid. - */ - public String getHeaderFieldKey(int n) - { - if (!connected) - try - { - connect(); - } - catch (IOException x) - { - return null; - } - - return headers.getHeaderFieldKeyByIndex (n); - } -} diff --git a/gnu/java/net/protocol/http/Cookie.java b/gnu/java/net/protocol/http/Cookie.java new file mode 100644 index 000000000..fc4fde4f0 --- /dev/null +++ b/gnu/java/net/protocol/http/Cookie.java @@ -0,0 +1,161 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.ParseException; +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/gnu/java/net/protocol/http/CookieManager.java b/gnu/java/net/protocol/http/CookieManager.java new file mode 100644 index 000000000..59a36e317 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/Credentials.java b/gnu/java/net/protocol/http/Credentials.java new file mode 100644 index 000000000..ff4a9ad12 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/HTTPConnection.java b/gnu/java/net/protocol/http/HTTPConnection.java new file mode 100644 index 000000000..62518cd2e --- /dev/null +++ b/gnu/java/net/protocol/http/HTTPConnection.java @@ -0,0 +1,638 @@ +/* HTTPConnection.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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.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 gnu.java.net.EmptyX509TrustManager; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.net.SocketFactory; +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 = initUserAgent(); + + private static String initUserAgent() + { + try + { + StringBuffer buf = new StringBuffer("inetlib/1.1 ("); + buf.append(System.getProperty("os.name")); + buf.append("; "); + buf.append(System.getProperty("os.arch")); + buf.append("; "); + buf.append(System.getProperty("user.language")); + buf.append(")"); + return buf.toString(); + } + catch (SecurityException e) + { + return "inetlib/1.1"; + } + } + + /** + * 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; + + /** + * The socket this connection communicates on. + */ + protected Socket socket; + + /** + * 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 = Collections.synchronizedList(new ArrayList(4)); + requestListeners = Collections.synchronizedList(new ArrayList(4)); + } + + /** + * 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) + { + throw new IllegalArgumentException("path must have non-zero length"); + } + Request ret = new Request(this, method, path); + 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 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 + { + TrustManager tm = new EmptyX509TrustManager(); + SSLContext context = SSLContext.getInstance("SSL"); + TrustManager[] trust = new TrustManager[] { tm }; + context.init(null, trust, null); + SSLSocketFactory factory = context.getSocketFactory(); + SSLSocket ss = + (SSLSocket) factory.createSocket(socket, connectHostname, + connectPort, true); + String[] protocols = { "TLSv1", "SSLv3" }; + ss.setEnabledProtocols(protocols); + ss.setUseClientMode(true); + 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; + } + + protected InputStream getInputStream() + throws IOException + { + if (socket == null) + { + getSocket(); + } + return in; + } + + protected OutputStream getOutputStream() + throws IOException + { + if (socket == null) + { + getSocket(); + } + return out; + } + + /** + * Closes the underlying socket, if any. + */ + protected 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; + } + } + } + +} + diff --git a/gnu/java/net/protocol/http/HTTPDateFormat.java b/gnu/java/net/protocol/http/HTTPDateFormat.java new file mode 100644 index 000000000..22ef05631 --- /dev/null +++ b/gnu/java/net/protocol/http/HTTPDateFormat.java @@ -0,0 +1,435 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.PrintStream; +import java.text.*; +import java.util.*; + +/** + * 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/gnu/java/net/protocol/http/HTTPURLConnection.java b/gnu/java/net/protocol/http/HTTPURLConnection.java new file mode 100644 index 000000000..bb079a090 --- /dev/null +++ b/gnu/java/net/protocol/http/HTTPURLConnection.java @@ -0,0 +1,511 @@ +/* HTTPURLConnection.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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.ProtocolException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A URLConnection that uses the HTTPConnection class. + * + * @author Chris Burdess (dog@gnu.org) + */ +public class HTTPURLConnection + extends HttpURLConnection +{ + + /* + * The underlying connection. + */ + private HTTPConnection connection; + + private String proxyHostname; + private int proxyPort; + + private Request request; + private Headers requestHeaders; + private ByteArrayOutputStream requestSink; + private boolean requestMethodSetExplicitly; + + private Response response; + private ByteArrayInputStream responseSink; + + /** + * Constructor. + * @param url the URL + */ + public HTTPURLConnection(URL url) + { + super(url); + requestHeaders = new Headers(); + AccessController.doPrivileged(this.new GetProxyAction()); + } + + class GetProxyAction + implements PrivilegedAction + { + + public Object run() + { + proxyHostname = System.getProperty("http.proxyHost"); + if (proxyHostname != null) + { + String port = System.getProperty("http.proxyPort"); + proxyPort = (port != null) ? Integer.parseInt (port) : -1; + } + 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 = new HTTPConnection(host, port, secure); + } + if (proxyHostname != null) + { + if (proxyPort < 0) + { + proxyPort = secure ? HTTPConnection.HTTPS_PORT : + HTTPConnection.HTTP_PORT; + } + connection.setProxy(proxyHostname, proxyPort); + } + request = connection.newRequest(method, file); + 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"); + 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; + } + // Otherwise this is not an HTTP redirect, can't follow + } + else + { + responseSink = new ByteArrayInputStream(reader.toByteArray ()); + } + } + while (retry); + connected = true; + } + + 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 Map getHeaderFields() + { + if (!connected) + { + try + { + connect(); + } + catch (IOException e) + { + return null; + } + } + Map ret = new LinkedHashMap(); + ret.put(null, getStatusLine(response)); + ret.putAll(response.getHeaders()); + 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 + { + 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 + { + 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(); + } + +} + diff --git a/gnu/java/net/protocol/http/Handler.java b/gnu/java/net/protocol/http/Handler.java index 6f2ebb125..43ff63df9 100644 --- a/gnu/java/net/protocol/http/Handler.java +++ b/gnu/java/net/protocol/http/Handler.java @@ -1,5 +1,5 @@ -/* Handler.java -- HTTP protocol handler for java.net - Copyright (c) 1998, 1999, 2003 Free Software Foundation, Inc. +/* Handler.java -- + Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -7,7 +7,7 @@ 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 @@ -35,6 +35,7 @@ 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; @@ -43,44 +44,30 @@ import java.net.URLConnection; import java.net.URLStreamHandler; /** - * This is the protocol handler for the HTTP protocol. It implements - * the abstract openConnection() method from URLStreamHandler by returning - * a new HttpURLConnection object (from this package). All other - * methods are inherited + * An HTTP URL stream handler. * - * @author Aaron M. Renn (arenn@urbanophile.com) - * @author Warren Levy - * @author Anthony Green <green@redhat.com> + * @author Chris Burdess (dog@gnu.org) */ -public class Handler extends URLStreamHandler +public class Handler + extends URLStreamHandler { - /** - * A do nothing constructor - */ - public Handler() - { - } /** - * This method returs a new HttpURLConnection for the specified URL - * - * @param url The URL to return a connection for - * - * @return The URLConnection - * - * @exception IOException If an error occurs + * Returns the default HTTP port (80). */ - protected URLConnection openConnection (URL url) throws IOException + protected int getDefaultPort() { - return new Connection (url); + return HTTPConnection.HTTP_PORT; } /** - * Returns the default port for a URL parsed by this handler. + * Returns an HTTPURLConnection for the given URL. */ - protected int getDefaultPort() + public URLConnection openConnection(URL url) + throws IOException { - return 80; + return new HTTPURLConnection(url); } -} // class Handler +} + diff --git a/gnu/java/net/protocol/http/Headers.java b/gnu/java/net/protocol/http/Headers.java new file mode 100644 index 000000000..e56139f02 --- /dev/null +++ b/gnu/java/net/protocol/http/Headers.java @@ -0,0 +1,363 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.InputStream; +import java.io.IOException; +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 + value.append(line.substring(0, len - 1)); + } + 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) == ' '); + value.append(line.substring(di, len - 1)); + } + } + } + + 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/gnu/java/net/protocol/http/Request.java b/gnu/java/net/protocol/http/Request.java new file mode 100644 index 000000000..123e889c0 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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.protocol.http.event.RequestEvent; +import gnu.java.net.BASE64; +import gnu.java.net.LineInputStream; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.net.Socket; +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 <=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: + 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/gnu/java/net/protocol/http/RequestBodyWriter.java b/gnu/java/net/protocol/http/RequestBodyWriter.java new file mode 100644 index 000000000..c977451c3 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/Response.java b/gnu/java/net/protocol/http/Response.java new file mode 100644 index 000000000..5a0a618f8 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/ResponseBodyReader.java b/gnu/java/net/protocol/http/ResponseBodyReader.java new file mode 100644 index 000000000..dea3ce9bb --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/ResponseHeaderHandler.java b/gnu/java/net/protocol/http/ResponseHeaderHandler.java new file mode 100644 index 000000000..ea0112076 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/SimpleCookieManager.java b/gnu/java/net/protocol/http/SimpleCookieManager.java new file mode 100644 index 000000000..19649074a --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/TODO b/gnu/java/net/protocol/http/TODO deleted file mode 100644 index f4fc5c660..000000000 --- a/gnu/java/net/protocol/http/TODO +++ /dev/null @@ -1,18 +0,0 @@ --- Base protocol - --- Proxy support - --- Caching - --- If modified since support - --- Connect, getInputStream(), getOutputStream, disconnect() - --- Header parsing - --- GET method implementation only - --- Request parameter handling - --- Basically everything beyond simple fetching - diff --git a/gnu/java/net/protocol/http/event/ConnectionEvent.java b/gnu/java/net/protocol/http/event/ConnectionEvent.java new file mode 100644 index 000000000..749f711fa --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/event/ConnectionListener.java b/gnu/java/net/protocol/http/event/ConnectionListener.java new file mode 100644 index 000000000..a29955d80 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/event/RequestEvent.java b/gnu/java/net/protocol/http/event/RequestEvent.java new file mode 100644 index 000000000..ebc91d2ef --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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/gnu/java/net/protocol/http/event/RequestListener.java b/gnu/java/net/protocol/http/event/RequestListener.java new file mode 100644 index 000000000..6bf0c96e6 --- /dev/null +++ b/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., 59 Temple Place, Suite 330, Boston, MA +02111-1307 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); + +} + |