diff options
author | Aaron M. Renn <arenn@urbanophile.com> | 1998-05-18 00:34:12 +0000 |
---|---|---|
committer | Aaron M. Renn <arenn@urbanophile.com> | 1998-05-18 00:34:12 +0000 |
commit | cef12ba87faabdb23fc796df9046ddf61247e325 (patch) | |
tree | f19569ad75e9695f2448093caeed508768f9a5cb | |
parent | e5cd4c332de77192e9beb4b0533a2e6ffe0e0cd1 (diff) | |
download | classpath-cef12ba87faabdb23fc796df9046ddf61247e325.tar.gz |
Initial Checkin
72 files changed, 11014 insertions, 0 deletions
diff --git a/gnu/Makefile.am b/gnu/Makefile.am new file mode 100644 index 000000000..4163db7d3 --- /dev/null +++ b/gnu/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = java + diff --git a/gnu/java/Makefile.am b/gnu/java/Makefile.am new file mode 100644 index 000000000..656bb40c9 --- /dev/null +++ b/gnu/java/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = net + diff --git a/gnu/java/net/Makefile.am b/gnu/java/net/Makefile.am new file mode 100644 index 000000000..35d55a582 --- /dev/null +++ b/gnu/java/net/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = content http + diff --git a/gnu/java/net/content/Makefile.am b/gnu/java/net/content/Makefile.am new file mode 100644 index 000000000..174dae80f --- /dev/null +++ b/gnu/java/net/content/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = text + diff --git a/gnu/java/net/content/text/ChangeLog b/gnu/java/net/content/text/ChangeLog new file mode 100644 index 000000000..fe92bbc0b --- /dev/null +++ b/gnu/java/net/content/text/ChangeLog @@ -0,0 +1,8 @@ +Sat May 16 15:15:10 1998 arenn's Development Account <devel@larissa.foo.com> + + * plain.java: Changed package to gnu.* + +Sun May 3 21:04:11 1998 arenn's Development Account <devel@larissa.foo.com> + + * plain.java: Wrote this class + diff --git a/gnu/java/net/content/text/Makefile.am b/gnu/java/net/content/text/Makefile.am new file mode 100644 index 000000000..bb8a15e23 --- /dev/null +++ b/gnu/java/net/content/text/Makefile.am @@ -0,0 +1,6 @@ +## Input file for automake to generate the Makefile.in used by configure + +textdir = $(datadir)/gnu/java/net/content/text + +text_JAVA = plain.java + diff --git a/gnu/java/net/content/text/plain.java b/gnu/java/net/content/text/plain.java new file mode 100644 index 000000000..405ad1a4a --- /dev/null +++ b/gnu/java/net/content/text/plain.java @@ -0,0 +1,70 @@ +/************************************************************************* +/* plain.java -- Content Handler for text/plain type +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package gnu.java.net.content.text; + +import java.net.ContentHandler; +import java.net.URLConnection; +import java.io.IOException; + +/** + * This class is the ContentHandler for the text/plain MIME type. It + * simply returns an InputStream object of the text being read. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class plain extends ContentHandler +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Default do nothing constructor + */ +public +plain() +{ + ; +} + +/*************************************************************************/ + +/** + * Returns an InputStream as the content for this object + * + * @param url_con The URLConnection to get the content of + * + * @return An InputStream for that connection + * + * @exception IOException If an error occurs + */ +public Object +getContent(URLConnection url_con) throws IOException +{ + return(url_con.getInputStream()); +} + +} // class plain + diff --git a/gnu/java/net/http/ChangeLog b/gnu/java/net/http/ChangeLog new file mode 100644 index 000000000..44c1503f1 --- /dev/null +++ b/gnu/java/net/http/ChangeLog @@ -0,0 +1,18 @@ +Sat May 16 15:13:44 1998 arenn's Development Account <devel@larissa.foo.com> + + * HttpURLConnection.java: Switched package to gnu.* + + * Handler.java: Switched package to gnu.* + +Sat Apr 25 16:44:30 1998 arenn's Development Account <devel@larissa.foo.com> + + * HttpURLConnection.java: Finished off this class + + +Fri Apr 24 18:23:58 1998 arenn's Development Account <devel@larissa.foo.com> + + * Handler.java: Wrote this class. + + * HttpURLConnection.java: Wrote an initial cut at this class with + all methods stubbed out + diff --git a/gnu/java/net/http/Handler.java b/gnu/java/net/http/Handler.java new file mode 100644 index 000000000..e3d9b2ed5 --- /dev/null +++ b/gnu/java/net/http/Handler.java @@ -0,0 +1,77 @@ +/************************************************************************* +/* Handler.java -- HTTP protocol handler for java.net +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package gnu.java.net.http; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLConnection; +import java.io.IOException; + +/** + * 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 + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Handler extends URLStreamHandler +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * A do nothing constructor + */ +public +Handler() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * 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 + */ +protected URLConnection +openConnection(URL url) throws IOException +{ + return(new gnu.java.net.http.HttpURLConnection(url)); +} + +} // class Handler + diff --git a/gnu/java/net/http/HttpURLConnection.java b/gnu/java/net/http/HttpURLConnection.java new file mode 100644 index 000000000..33212c46e --- /dev/null +++ b/gnu/java/net/http/HttpURLConnection.java @@ -0,0 +1,321 @@ +/************************************************************************* +/* HttpURLConnection.java -- URLConnection class for HTTP protocol +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package gnu.java.net.http; + +import java.net.URL; +import java.net.URLConnection; +import java.net.Socket; +import java.net.ProtocolException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.IOException; + +/** + * This subclass of java.net.URLConnection models a URLConnection via + * the HTTP protocol. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class HttpURLConnection extends java.net.HttpURLConnection +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The socket we are connected to + */ +protected Socket socket; + +/** + * The InputStream for this connection + */ +protected InputStream in_stream; + +/** + * The OutputStream for this connection + */ +protected OutputStream out_stream; + +/** + * The PrintWriter for this connection (used internally) + */ +protected PrintWriter out_writer; + +/** + * The InputStreamReader for this connection (used internally) + */ +protected InputStreamReader in_reader; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Calls superclass constructor to initialize + */ +protected +HttpURLConnection(URL url) +{ + super(url); + + /* Set up some variables */ + doOutput = false; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Reads a line of text from the InputStream + */ +private String +readLine() throws IOException +{ + StringBuffer sb = new StringBuffer(""); + + byte[] buf = new byte[1]; + for (;;) + { + int read = in_stream.read(buf, 0, 1); + if (read == -1) + throw new IOException("Premature end of input"); + + if (buf[0] == '\r') + continue; + if (buf[0] == '\n') + break; + sb.append((char)buf[0]); + } + return(sb.toString()); +} + +/*************************************************************************/ + +/** + * Connects to the remote host, sends the request, and parses the reply + * code and header information returned + */ +public void +connect() throws IOException +{ + // Connect up + if (url.getPort() == -1) + socket = new Socket(url.getHost(), 80); + else + socket = new Socket(url.getHost(), url.getPort()); + + out_stream = socket.getOutputStream(); + in_stream = socket.getInputStream(); + + in_reader = new InputStreamReader(in_stream); + out_writer = new PrintWriter(new OutputStreamWriter(out_stream)); + + // Send the request + out_writer.print(getRequestMethod() + " " + getURL().getFile() + + " HTTP/1.1\r\n"); + + String propval = getRequestProperty("host"); + if (propval == null) + out_writer.print("Host: " + getURL().getHost() + "\r\n"); + else + out_writer.print("Host: " + propval + "\r\n"); + out_writer.print("Connection: close" + "\r\n"); + + propval = getRequestProperty("user-agent"); + if (propval == null) + out_writer.print("User-Agent: jcl/0.0\r\n"); + else + out_writer.print("User-Agent: " + propval + "\r\n"); + + propval = getRequestProperty("accept"); + if (propval == null) + out_writer.print("Accept: */*\r\n"); + else + out_writer.print("Accept: " + propval + "\r\n"); + + out_writer.print("\r\n"); + out_writer.flush(); + + // Parse the reply + String line = readLine(); + String saveline = line; + + int idx = line.indexOf(" " ); + if ((idx == -1) || (line.length() < (idx + 6))) + throw new IOException("Server reply was unparseable: " + saveline); + + 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; + for (;;) + { + line = 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) + { + headerKeys.addElement(key.toLowerCase()); + headerValues.addElement(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) + { + headerKeys.addElement(key.toLowerCase()); + headerValues.addElement(value); + } +} + +/*************************************************************************/ + +/** + * Disconnects from the remote server + */ +public void +disconnect() +{ + try + { + socket.close(); + } + catch(IOException e) { ; } +} + +/*************************************************************************/ + +/** + * 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")) + super.setRequestMethod(method); + else + throw new ProtocolException("Unsupported or unknown request method " + + method); +} + +/*************************************************************************/ + +/** + * 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(false); +} + +/*************************************************************************/ + +/** + * 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(); + + return(in_stream); +} + +} // class HttpURLConnection + diff --git a/gnu/java/net/http/Makefile.am b/gnu/java/net/http/Makefile.am new file mode 100644 index 000000000..243e6dd38 --- /dev/null +++ b/gnu/java/net/http/Makefile.am @@ -0,0 +1,6 @@ +## Input file for automake to generate the Makefile.in used by configure + +httpdir = $(datadir)/gnu/java/net/http + +http_JAVA = HttpURLConnection.java Handler.java + diff --git a/gnu/java/net/http/TODO b/gnu/java/net/http/TODO new file mode 100644 index 000000000..f4fc5c660 --- /dev/null +++ b/gnu/java/net/http/TODO @@ -0,0 +1,18 @@ +-- 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/java/Makefile.am b/java/Makefile.am new file mode 100644 index 000000000..656bb40c9 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = net + diff --git a/java/net/ContentHandler.java b/java/net/ContentHandler.java new file mode 100644 index 000000000..93aca22ba --- /dev/null +++ b/java/net/ContentHandler.java @@ -0,0 +1,74 @@ +/************************************************************************* +/* ContentHandler.java -- Abstract class for handling content from URL's +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This is an abstract class that is the superclass for classes that read + * objects from URL's. Calling the getContent() method in the URL class + * or the URLConnection class will cause an instance of a subclass of + * ContentHandler to be created for the MIME type of the object being + * downloaded from the URL. Thus, this class is seldom needed by + * applications/applets directly, but only indirectly through methods in + * other classes. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class ContentHandler +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Do nothing constructor + */ +public +ContentHandler() +{ + ; +} + +/*************************************************************************/ + +/** + * This method reads from the InputStream of the passed in URL connection + * and uses the data downloaded to create an Object represening the + * content. For example, if the URL is pointing to a GIF file, this + * method might return an Image object. This method should be overridden + * by subclasses. + * + * @param urlcon A URLConnection object to read data from + * + * @return An object representing the data read + * + * @exception IOException If an error occurs + */ +public abstract synchronized Object +getContent(URLConnection urlcon) throws IOException; + +} // class ContentHandler + diff --git a/java/net/ContentHandlerFactory.java b/java/net/ContentHandlerFactory.java new file mode 100644 index 000000000..65b8e47ed --- /dev/null +++ b/java/net/ContentHandlerFactory.java @@ -0,0 +1,45 @@ +/************************************************************************* +/* ContentHandlerFactory.java -- Interface for creating content handlers +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This interface maps MIME types to ContentHandler objects. It consists + * of one method that, when passed a MIME type, returns a handler for that + * type. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface ContentHandlerFactory +{ +/** + * This method is passed a MIME type as a string and is responsible for + * returning the appropriate ContentType object. + * + * @param mime_type The MIME type to map to a ContentHandler + * + * @return The ContentHandler for the passed in MIME type + */ +public abstract ContentHandler +createContentHandler(String mime_type); + +} // interface ContentHandlerFactory + diff --git a/java/net/DatagramPacket.java b/java/net/DatagramPacket.java new file mode 100644 index 000000000..66c8dfe1d --- /dev/null +++ b/java/net/DatagramPacket.java @@ -0,0 +1,240 @@ +/************************************************************************* +/* DatagramPacket.java -- Class to model a packet to be sent via UDP +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This class models a packet of data that is to be sent across the network + * using a connectionless protocol such as UDP. It contains the data + * to be send, as well as the destination address and port. Note that + * datagram packets can arrive in any order and are not guaranteed to be + * delivered at all. + * <p> + * This class can also be used for receiving data from the network. + * <p> + * Note that for all method below where the buffer length passed by the + * caller cannot exceed the actually length of the byte array passed as + * the buffer, if this condition is not true, then the method silently + * reduces the length value to maximum allowable value. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class DatagramPacket +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The data buffer to send + */ +protected byte[] buf; + +/** + * The length of the data buffer to send + */ +protected int len; + +/** + * The address to which the packet should be sent or from which it + * was received + */ +protected InetAddress addr; + +/** + * The port to which the packet should be sent or from which it was + * was received. + */ +protected int port; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Creates a DatagramPacket for receiving packets from the network + * + * @param buf A buffer for storing the returned packet data + * @param len The length of the buffer (must be <= buf.length) + */ +public +DatagramPacket(byte[] buf, int len) +{ + // Hmm, what should we do if len > buf.length? I say silently reduce len + if (buf == null) + len = 0; + else if (len > buf.length) + len = buf.length; + + this.buf = buf; + this.len = len; +} + +/*************************************************************************/ + +/** + * Creates a DatagramPacket for transmitting packets across the network. + * + * @param buf A buffer containing the data to send + * @param len The length of the buffer (must be <= buf.length) + * @param addr The address to send to + * @param port The port to send to + */ +public +DatagramPacket(byte[] buf, int len, InetAddress addr, int port) +{ + this(buf, len); + + this.addr = addr; + this.port = port; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the address that this packet is being sent to or, if it was used + * to receive a packet, the address that is was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * returns null + * + * @return The address for this packet + */ +public InetAddress +getAddress() +{ + return(addr); +} + +/*************************************************************************/ + +/** + * This sets the address to which the data packet will be transmitted. + * + * @param addr The destination address + */ +public synchronized void +setAddress(InetAddress addr) +{ + this.addr = addr; +} + +/*************************************************************************/ + +/** + * Returns the port number this packet is being sent to or, if it was used + * to receive a packet, the port that it was received from. If the + * constructor that doesn not take an address was used to create this object + * and no packet was actually read into this object, then this method + * will return 0. + * + * @return The port number for this packet + */ +public int +getPort() +{ + return(port); +} + +/*************************************************************************/ + +/** + * This sets the port to which the data packet will be transmitted. + * + * @param port The destination port + */ +public synchronized void +setPort(int port) +{ + this.port = port; +} + +/*************************************************************************/ + +/** + * Returns the data buffer for this packet + * + * @return This packet's data buffer + */ +public byte[] +getData() +{ + return(buf); +} + +/*************************************************************************/ + +/** + * Sets the data buffer for this packet. + * + * @return The new buffer for this packet + */ +public synchronized void +setData(byte[] buf) +{ + this.buf = buf; + + if (buf == null) + { + this.buf = null; + len = 0; + } + else if (len > buf.length) + len = buf.length; +} + +/*************************************************************************/ + +/** + * Returns the length of the data in the buffer + * + * @return The length of the data + */ +public int +getLength() +{ + return(len); +} + +/*************************************************************************/ + +/** + * Sets the length of the data in the buffer. + * + * @param len The new length. (Where len <= buf.length) + */ +public synchronized void +setLength(int len) +{ + this.len = len; +} + +} // class DatagramPacket + diff --git a/java/net/DatagramSocket.java b/java/net/DatagramSocket.java new file mode 100644 index 000000000..3fef08592 --- /dev/null +++ b/java/net/DatagramSocket.java @@ -0,0 +1,230 @@ +/************************************************************************* +/* DatagramSocket.java -- A class to model UDP sockets +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This class models a connectionless datagram socket that sends + * individual packets of data across the network. In the TCP/IP world, + * this means UDP. Datagram packets do not have guaranteed delivery, + * or any guarantee about the order the data will be received on the + * remote host. + * <p> + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class DatagramSocket +{ + +/*************************************************************************/ + +/** + * Instance Variables + */ + +/** + * This is the implementation object used by this socket. + */ +protected DatagramSocketImpl impl; + +/** + * This is the address we are bound to + */ +protected InetAddress local_addr; + +/*************************************************************************/ + +/** + * Constructors + */ + +/** + * Create a DatagramSocket that binds to a random port and every + * address on the local machine. + * + * @exception SocketException If an error occurs + */ +public +DatagramSocket() throws SocketException +{ + this(0, null); +} + +/*************************************************************************/ + +/** + * Create a DatagramSocket that binds to the specified port and every + * address on the local machine + * + * @param port The local port number to bind to + * + * @exception SocketException If an error occurs + */ +public +DatagramSocket(int port) throws SocketException +{ + this(port, null); +} + +/*************************************************************************/ + +/** + * Create a DatagramSocket that binds to the specified local port and + * address + * + * @param port The local port number to bind to + * @param addr The local address to bind to + * + * @exception SocketException If an error occurs + */ +public +DatagramSocket(int port, InetAddress addr) throws SocketException +{ + // Why is there no factory for this? + impl = new PlainDatagramSocketImpl(); + + impl.create(); + + try + { + if (addr == null) + addr = InetAddress.getInaddrAny(); + + impl.localPort = port; + this.local_addr = addr; + + impl.bind(port, addr); + + local_addr = addr; + } + catch (UnknownHostException e) + { + throw new SocketException(e.toString()); + } +} + +/*************************************************************************/ + +/** + * Closes this socket. + */ +public synchronized void +close() +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * Returns the local address this socket is bound to + */ +public InetAddress +getLocalAddress() +{ + return(local_addr); +} + +/*************************************************************************/ + +/** + * Returns the local port this socket is bound to + */ +public int +getLocalPort() +{ + return(impl.getLocalPort()); +} + +/*************************************************************************/ + +/** + * Returns the value of the socket's SO_TIMEOUT setting. If this method + * returns 0 then SO_TIMEOUT is disabled. + * + * @exception SocketException If an error occurs + */ +public synchronized int +getSoTimeout() throws SocketException +{ + Object obj = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (obj instanceof Integer) + return(((Integer)obj).intValue()); + else + throw new SocketException("Internal Error"); +} + +/*************************************************************************/ + +/** + * Sets the value of the socket's SO_TIMEOUT value. A value of 0 will + * disable SO_TIMEOUT. Any other value is the number of milliseconds + * a socket read/write will block before timing out. + * + * @param timeout The new SO_TIMEOUT value + * + * @exception SocketException If an error occurs + */ +public synchronized void +setSoTimeout(int timeout) throws SocketException +{ + impl.setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); +} + +/*************************************************************************/ + +/** + * Reads a datagram packet from the socket. Note that this method + * will block until a packet is received from the network. On return, + * the passed in DatagramPacket is populated with the data received + * and all the other information about the packet. + * + * @param packet A DatagramPacket for storing the data + * + * @exception IOException If an error occurs + */ +public synchronized void +receive(DatagramPacket packet) throws IOException +{ + impl.receive(packet); +} + +/*************************************************************************/ + +/** + * Sends the specified packet. The host and port to which the packet + * are to be sent should be set inside the packet. + * + * @param packet The packet of data to send + * + * @exception IOException If an error occurs + */ +public synchronized void +send(DatagramPacket packet) throws IOException +{ + impl.send(packet); +} + +} // class DatagramSocket + diff --git a/java/net/DatagramSocketImpl.java b/java/net/DatagramSocketImpl.java new file mode 100644 index 000000000..5120d64a7 --- /dev/null +++ b/java/net/DatagramSocketImpl.java @@ -0,0 +1,217 @@ +/************************************************************************* +/* DatagramSocketImpl.java -- Abstract class for UDP socket implementations +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * This abstract class models a datagram socket implementation. An + * actual implementation class would implement these methods, probably + * via redirecting them to native code. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class DatagramSocketImpl implements SocketOptions +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The FileDescriptor object for this object. *** I would really like to know + * how to create one of these. ****** + */ +protected FileDescriptor fd; + +/** + * The local port to which this socket is bound + */ +protected int localPort; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Do nothing constructor + */ +public +DatagramSocketImpl() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method binds the socket to the specified local port and address. + * + * @param port The port number to bind to + * @param addr The address to bind to + * + * @exception SocketException If an error occurs + */ +protected abstract synchronized void +bind(int port, InetAddress addr) throws SocketException; + +/*************************************************************************/ + +/** + * This methods closes the socket + */ +protected abstract synchronized void +close(); + +/*************************************************************************/ + +/** + * Creates a new datagram socket. + * + * @exception SocketException If an error occurs + */ +protected abstract synchronized void +create() throws SocketException; + +/*************************************************************************/ + +/** + * Returns the FileDescriptor for this socket + */ +protected FileDescriptor +getFileDescriptor() +{ + return(fd); +} + +/*************************************************************************/ + +/** + * Returns the local port this socket is bound to + */ +protected int +getLocalPort() +{ + return(localPort); +} + +/*************************************************************************/ + +/** + * This method returns the current Time to Live (TTL) setting on this + * socket. + * + * @exception IOException If an error occurs + */ +protected abstract synchronized byte +getTTL() throws IOException; + +/*************************************************************************/ + +/** + * Sets the Time to Live (TTL) setting on this socket to the specified + * value. + * + * @param ttl The new Time to Live value + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +setTTL(byte ttl) throws IOException; + +/*************************************************************************/ + +/** + * Causes this socket to join the specified multicast group + * + * @param addr The multicast address to join with + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +join(InetAddress addr) throws IOException; + +/*************************************************************************/ + +/** + * Causes the socket to leave the specified multicast group. + * + * @param addr The multicast address to leave + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +leave(InetAddress addr) throws IOException; + +/*************************************************************************/ + +/** + * Takes a peek at the next packet received in order to retrieve the + * address of the sender + * + * @param ********** Wish I knew what this was for ************ + * + * @return ******* Wish I knew ************ + * + * @exception If an error occurs + */ +protected abstract synchronized int +peek(InetAddress addr) throws IOException; + +/*************************************************************************/ + +/** + * Receives a packet of data from the network Will block until a packet + * arrives. The packet info in populated into the passed in + * DatagramPacket object. + * + * @param packet A place to store the incoming packet. + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +receive(DatagramPacket packet) throws IOException; + +/*************************************************************************/ + +/** + * Transmits the specified packet of data to the network. The destination + * host and port should be encoded in the packet. + * + * @param packet The packet to send + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +send(DatagramPacket packet) throws IOException; + +} // class DatagramSocketImpl + diff --git a/java/net/FileNameMap.java b/java/net/FileNameMap.java new file mode 100644 index 000000000..278c4b625 --- /dev/null +++ b/java/net/FileNameMap.java @@ -0,0 +1,43 @@ +/************************************************************************* +/* FileNameMap.java -- Maps filenames to MIME types +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This interface has one method which, when passed a filename, returns + * the MIME type associated with that filename. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface FileNameMap +{ +/** + * This method is passed a filename and is responsible for determining + * the appropriate MIME type for that file. + * + * @param filename The name of the file to generate a MIME type for. + * + * @return The MIME type for the filename passed in. + */ +public abstract String +getContentTypeFor(String filename); + +} // interface FileNameMap diff --git a/java/net/HttpURLConnection.java b/java/net/HttpURLConnection.java new file mode 100644 index 000000000..a2ea25fa1 --- /dev/null +++ b/java/net/HttpURLConnection.java @@ -0,0 +1,433 @@ +/************************************************************************* +/* HttpURLConnection.java -- HTTP connection to web server +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This class provides a common abstract implementation for those + * URL connection classes that will connect using the HTTP protocol. + * In addition to the functionality provided by the URLConnection + * class, it defines constants for HTTP return code values and + * methods for setting the HTTP request method and determining whether + * or not to follow redirects. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class HttpURLConnection extends URLConnection +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +// HTTP return codes + +/** + * Indicates that the client may continue with its request. This value + * is specified as part of RFC 2068 but was not included in Sun's JDK, so + * beware of using this value + */ +public static final int HTTP_CONTINUE = 100; + +/** + * Indicates that the server received and is willing to comply with a + * request to switch protocols initiated by the client. This value is + * specified as part of RFC 2068 but was not included in Sun's JDK as of + * release 1.2, so beware of using this value. + */ +public static final int HTTP_SWITCHING_PROTOCOLS = 101; + +/** + * Indicates the request succeeded + */ +public static final int HTTP_OK = 200; + +/** + * The requested resource has been created. + */ +public static final int HTTP_CREATED = 201; + +/** + * The request has been accepted for processing but has not completed. + * There is no guarantee that the requested action will actually ever + * be completed succesfully, but everything is ok so far. + */ +public static final int HTTP_ACCEPTED = 202; + +/** + * The meta-information returned in the header is not the actual data + * from the original server, but may be from a local or other copy. + * Normally this still indicates a successful completion. + */ +public static final int HTTP_NOT_AUTHORITATIVE = 203; + +/** + * The server performed the request, but there is no data to send + * back. This indicates that the user's display should not be changed. + */ +public static final int HTTP_NO_CONTENT = 204; + +/** + * The server performed the request, but there is no data to sent back, + * however, the user's display should be "reset" to clear out any form + * fields entered. + */ +public static final int HTTP_RESET = 205; + +/** + * The server completed the partial GET request for the resource. + */ +public static final int HTTP_PARTIAL = 206; + +/** + * There is a list of choices available for the requested resource + */ +public static final int HTTP_MULT_CHOICE = 300; + +/** + * The resource has been permanently moved to a new location. + */ +public static final int HTTP_MOVED_PERM = 301; + +/** + * The resource requested has been temporarily moved to a new location. + */ +public static final int HTTP_MOVED_TEMP = 302; + +/** + * The response to the request issued is available at another location + */ +public static final int HTTP_SEE_OTHER = 303; + +/** + * The document has not been modified since the criteria specified in + * a conditional GET + */ +public static final int HTTP_NOT_MODIFIED = 304; + +/** + * The requested resource needs to be accessed through a proxy. + */ +public static final int HTTP_USE_PROXY = 305; + +/** + * The request was misformed or could not be understood. + */ +public static final int HTTP_BAD_REQUEST = 400; + +/** + * The request made requires user authorization. Try again with + * a correct authentication header. + */ +public static final int HTTP_UNAUTHORIZED = 401; + +/** + * Code reserved for future use - I hope way in the future + */ +public static final int HTTP_PAYMENT_REQUIRED = 402; + +/** + * There is no permission to access the requested resource + */ +public static final int HTTP_FORBIDDEN = 403; + +/** + * The requested resource was not found + */ +public static final int HTTP_NOT_FOUND = 404; + +/** + * The specified request method is not allowed for this resource + */ +public static final int HTTP_BAD_METHOD = 405; + +/** + * Based on the input headers sent, the resource returned in response + * to the request would not be acceptable to the client. + */ +public static final int HTTP_NOT_ACCEPTABLE = 406; + +/** + * The client must authenticate with a proxy prior to attempting this + * request. + */ +public static final int HTTP_PROXY_AUTH = 407; + +/** + * The request timed out. + */ +public static final int HTTP_CLIENT_TIMEOUT = 408; + +/** + * There is a conflict between the current state of the resource and the + * requested action. + */ +public static final int HTTP_CONFLICT = 409; + +/** + * The requested resource is no longer available. This ususally indicates + * a permanent condition. + */ +public static final int HTTP_GONE = 410; + +/** + * A Content-Length header is required for this request, but was not + * supplied + */ +public static final int HTTP_LENGTH_REQUIRED = 411; + +/** + * A client specified pre-condition was not met on the server. + */ +public static final int HTTP_PRECON_FAILED = 412; + +/** + * The request sent was too large for the server to handle + */ +public static final int HTTP_ENTITY_TOO_LARGE = 413; + +/** + * The name of the resource specified was too long + */ +public static final int HTTP_REQ_TOO_LONG = 414; + +/** + * The request is in a format not supported by the requested resource + */ +public static final int HTTP_UNSUPPORTED_TYPE = 415; + +/** + * The server encountered an unexpected error (such as a CGI script crash) + * that prevents the request from being fulfilled + */ +public static final int HTTP_INTERNAL_ERROR = 500; + +/** + * The server does not support the requested functionality. This code + * is specified in RFC 2068, but is not in Sun's JDK 1.2 Beware of using + * this variable. + */ +public static final int HTTP_NOT_IMPLEMENTED = 501; + +/** + * The proxy encountered a bad response from the server it was proxy-ing for + */ +public static final int HTTP_BAD_GATEWAY = 502; + +/** + * The HTTP service is not availalble, such as because it is overloaded + * and does not want additional requests. + */ +public static final int HTTP_UNAVAILABLE = 503; + +/** + * The proxy timed out getting a reply from the remote server it was + * proxy-ing for. + */ +public static final int HTTP_GATEWAY_TIMEOUT = 504; + +/** + * This server does not support the protocol version requested. + */ +public static final int HTTP_VERSION = 505; + +// Non-HTTP response static variables + +/** + * Flag to indicate whether or not redirects should be automatically + * followed. + */ +private static boolean follow_redirects = true; + +/** + * This is a list of valid request methods, separated by "|" characters. + */ +private static String valid_methods = "|GET|POST|HEAD|OPTIONS|PUT|DELETE|TRACE|"; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The requested method in use for this connection. + */ +protected String method = "GET"; // GET is the default method + +/** + * The response code received from the server + */ +protected int responseCode = -1; + +/** + * The response message string received from the server. + */ +protected String responseMessage = null; + +/*************************************************************************/ + +/* + * Class Methods + */ + +/** + * Sets a flag indicating whether or not to automatically follow HTTP + * redirects. If not otherwise set, this value defaults to true. Note + * that this value cannot be set by applets. + * + * @param follow true if redirects should be followed, false otherwise + */ +public static void +setFollowRedirectes(boolean follow) +{ + follow_redirects = follow; +} + +/*************************************************************************/ + +/** + * Returns a boolean indicating whether or not HTTP redirects will + * automatically be followed or not. + * + * @return true if redirects will be followed, false otherwise + */ +public static boolean +getFollowRedirects() +{ + return(follow_redirects); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Create an HttpURLConnection for the specified URL + * + * @param url The URL to create this connection for. + */ +protected +HttpURLConnection(URL url) +{ + super(url); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Sets the HTTP request method for this object. Allowable methods are: + * GET, POST, HEAD, OPTIONS, PUT, DELETE, and TRACE. Note that not all + * protocol implementations will necessarily support all of these + * request methods. The default value is GET. + * + * @param method The request method to use + * + * @exception ProtocolException If the requested method isn't valid or cannot be used + */ +public synchronized void +setRequestMethod(String method) throws ProtocolException +{ + if (valid_methods.indexOf("|" + method.toUpperCase() + "|") == -1) + throw new ProtocolException(method.toUpperCase()); + + this.method = method; +} + +/*************************************************************************/ + +/** + * The request method currently in use for this connection. + * + * @exception The request method + */ +public String +getRequestMethod() +{ + return(method); +} + +/*************************************************************************/ + +/** + * Returns the numeric response code received from the server, or -1 if + * no response code has yet been received, or the response code could not + * be determined. Note that all valid response codes have class variables + * defined for them in this class. + * + * @return The response code + * + * @IOException If an error occurs + */ +public int +getResponseCode() throws IOException +{ + return(responseCode); +} + +/*************************************************************************/ + +/** + * Returns the response message (everything after the response code in the + * reply string received from the server) or null if no connection has been + * made or no response message could be determined from the server output + * + * @return The response message + * + * @exception IOException If an error occurs + */ +public String +getResponseMessage() throws IOException +{ + return(responseMessage); +} + +/*************************************************************************/ + +/** + * Closes the connection to the server. + */ +public abstract void +disconnect(); + +/*************************************************************************/ + +/** + * Returns a boolean indicating whether or not this connection is going + * through a proxy + * + * @return true if through a proxy, false otherwise + */ +public abstract boolean +usingProxy(); + +} // class HttpURLConnection + diff --git a/java/net/InetAddress.java b/java/net/InetAddress.java new file mode 100644 index 000000000..5bcd3d9d5 --- /dev/null +++ b/java/net/InetAddress.java @@ -0,0 +1,661 @@ +/************************************************************************* +/* InetAddress.java -- Class to model an Internet address +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.Serializable; +import java.util.Hashtable; +import java.util.StringTokenizer; + +/** + * This class models an Internet address. It does not have a public + * constructor. Instead, new instances of this objects are created + * using the static methods getLocalHost(), getByName(), and + * getAllByName(). + * <p> + * This class fulfills the function of the C style functions gethostname(), + * gethostbyname(), and gethostbyaddr(). It resolves Internet DNS names + * into their corresponding numeric addresses and vice versa. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class InetAddress implements Serializable +{ + +/*************************************************************************/ + +/* + * Static Variables + */ + +// Static Initializer to load the shared library needed for name resolution +static +{ + System.loadLibrary("javanet"); +} + +/** + * The default DNS hash table size + */ +private static final int DEFAULT_CACHE_SIZE = 400; + +/** + * The default caching period in minutes + */ +private static final int DEFAULT_CACHE_PERIOD = (4 * 60); + +/** + * Percentage of cache entries to purge when the table gets full + */ +private static final int DEFAULT_CACHE_PURGE_PCT = 30; + +/** + * The special IP address INADDR_ANY + */ +private static InetAddress inaddr_any; + +/** + * The size of the cache + */ +private static int cache_size = 0; + +/** + * The length of time we will continue to read the address from cache + * before forcing another lookup + */ +private static int cache_period = 0; + +/** + * What percentage of the cache we will purge if it gets full + */ +private static int cache_purge_pct = 0; + +/** + * Hashtable to use as DNS lookup cache + */ +private static Hashtable cache; + +// Static initializer for the cache +static +{ + cache_size = DEFAULT_CACHE_SIZE; + cache_period = DEFAULT_CACHE_PERIOD * 60 * 1000; + cache_purge_pct = DEFAULT_CACHE_PURGE_PCT; + + // Look for properties that override default caching behavior + try + { + String propval; + + propval = System.getProperty("gnu.java.net.dns_cache_size"); + if (propval != null) + cache_size = Integer.parseInt(propval); + + propval = System.getProperty("gnu.java.net.dns_cache_period"); + cache_period = Integer.parseInt(propval) * 60 * 1000; + + propval = System.getProperty("gnu.java.net.dns_cache_purge_pct"); + cache_purge_pct = Integer.parseInt(propval); + } + catch (SecurityException e) { ; } + catch (NumberFormatException e) { ; } + + // Fallback to defaults if necessary + if ((cache_purge_pct < 1) || (cache_purge_pct > 100)) + cache_purge_pct = DEFAULT_CACHE_PURGE_PCT; + + // Create the cache + if (cache_size != 0) + cache = new Hashtable(cache_size); +} + +/*************************************************************************/ + +/* + * Instance variables + */ + +/** + * An array of octets representing an IP address + */ +int[] my_ip; + +/** + * The name of the host for this address + */ +String hostname; + +/** + * Backup hostname alias for this address. + */ +String hostname_alias; + +/** + * The time this address was looked up + */ +long lookup_time; + +/*************************************************************************/ + +/* + * Class Methods + */ + +/** + * This method checks the DNS cache to see if we have looked this hostname + * up before. If so, we return the cached addresses unless it has been in the + * cache too long. + * + * @param hostname The hostname to check for + * + * @return The InetAddress for this hostname or null if not available + */ +private static synchronized InetAddress[] +checkCacheFor(String hostname) +{ + InetAddress[] addrs = null; + + if (cache_size == 0) + return(null); + + Object obj = cache.get(hostname); + if (obj == null) + return(null); + + if (obj instanceof InetAddress[]) + addrs = (InetAddress[])obj; + + if (addrs == null) + return(null); + + if (cache_period != -1) + if ((System.currentTimeMillis() - addrs[0].lookup_time) > cache_period) + { + cache.remove(hostname); + return(null); + } + + return(addrs); +} + +/*************************************************************************/ + +/** + * This method adds an InetAddress object to our DNS cache. Note that + * if the cache is full, then we run a purge to get rid of old entries. + * This will cause a performance hit, thus applications using lots of + * lookups should set the cache size to be very large. + * + * @param hostname The hostname to cache this address under + * @param addr The InetAddress or InetAddress array to store + */ +private static synchronized void +addToCache(String hostname, Object addr) +{ + if (cache_size == 0) + return; + + // Check to see if hash table is full + if (cache_size != -1) + if (cache.size() == cache_size) + { + ; //*** Add code to purge later. + } + + cache.put(hostname, addr); +} + +/*************************************************************************/ + +/** + * Returns the special address INADDR_ANY used for binding to a local + * port on all IP addresses hosted by a the local host. + * + * @return An InetAddress object representing INDADDR_ANY + * + * @exception UnknownHostException If an error occurs + */ +static InetAddress +getInaddrAny() throws UnknownHostException +{ + if (inaddr_any == null) + { + int[] addr = lookupInaddrAny(); + inaddr_any = new InetAddress(addr); + } + + return(inaddr_any); +} + +/*************************************************************************/ + +/** + * Returns an array of InetAddress objects representing all the host/ip + * addresses of a given host, given the host's name. This name can be + * either a hostname such as "www.urbanophile.com" or an IP address in + * dotted decimal format such as "127.0.0.1". If the value is null, the + * hostname of the local machine is supplied by default. + * + * @param hostname The name of the desired host, or null for the local machine + * + * @return All addresses of the host as an array of InetAddress's + * + * @exception UnknownHostException If no IP address can be found for the given hostname + */ +public static InetAddress[] +getAllByName(String hostname) throws UnknownHostException +{ + // Default to current host if necessary + if (hostname == null) + { + InetAddress addr = getLocalHost(); + return(getAllByName(addr.getHostName())); + } + + // Check the cache for this host before doing a lookup + InetAddress[] addrs = checkCacheFor(hostname); + if (addrs != null) + return(addrs); + + // Not in cache, try the lookup + int[][] iplist = getHostByName(hostname); + if (iplist.length == 0) + throw new UnknownHostException(hostname); + + addrs = new InetAddress[iplist.length]; + + for (int i = 0; i < iplist.length; i++) + { + if (iplist[i].length != 4) + throw new UnknownHostException(hostname); + + // Don't store the hostname in order to force resolution of the + // canonical names of these ip's when the user asks for the hostname + // But do specify the host alias so if the IP returned won't + // reverse lookup we don't throw an exception. + addrs[i] = new InetAddress(iplist[i], null, hostname); + } + + addToCache(hostname, addrs); + + return(addrs); +} + +/*************************************************************************/ + +/** + * Returns an InetAddress object representing the IP address of the given + * hostname. This name can be either a hostname such as "www.urbanophile.com" + * or an IP address in dotted decimal format such as "127.0.0.1". If the + * hostname is null, the hostname of the local machine is supplied by + * default. This method is equivalent to returning the first element in + * the InetAddress array returned from GetAllByName. + * + * @param hostname The name of the desired host, or null for the local machine + * + * @return The address of the host as an InetAddress + * + * @exception UnknownHostException If no IP address can be found for the given hostname + */ +public static InetAddress +getByName(String hostname) throws UnknownHostException +{ + // Default to current host if necessary + if (hostname == null) + return(getLocalHost()); + + // First, check to see if it is an IP address. If so, then don't + // do a DNS lookup. + StringTokenizer st = new StringTokenizer(hostname, "."); + if (st.countTokens() == 4) + { + int i; + int[] ip = new int[4]; + for (i = 0; i < 4; i++) + { + try + { + ip[i] = Integer.parseInt(st.nextToken()); + if ((ip[i] < 0) || (ip[1] > 255)) + break; + } + catch (NumberFormatException e) + { + break; + } + } + if (i == 4) + { + return(new InetAddress(ip)); + } + } + + // Wasn't and IP, so try the lookup + InetAddress[] addrs = getAllByName(hostname); + + return(addrs[0]); +} + +/*************************************************************************/ + +/** + * Returns an InetAddress object representing the address of the current + * host. + * + * @return The local host's address + * + * @exception UnknownHostException If an error occurs + */ +public static InetAddress +getLocalHost() throws UnknownHostException +{ + String hostname = getLocalHostName(); + + return(getByName(hostname)); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Initializes this object's my_ip instance variable from the passed in + * int array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + */ +private +InetAddress(int[] addr) +{ + this(addr, null, null); +} + +/*************************************************************************/ + +/** + * Initializes this object's my_ip instance variable from the passed in + * int array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + */ +private +InetAddress(int[] addr, String hostname) +{ + this(addr, hostname, null); +} + +/*************************************************************************/ + +/** + * Initializes this object's my_ip instance variable from the passed in + * int array. Note that this constructor is protected and is called + * only by static methods in this class. + * + * @param addr The IP number of this address as an array of bytes + * @param hostname The hostname of this IP address. + * @param hostname_alias A backup hostname to use if hostname is null to prevent reverse lookup failures + */ +private +InetAddress(int[] addr, String hostname, String hostname_alias) +{ + my_ip = new int[addr.length]; + + for (int i = 0; i < addr.length; i++) + my_ip[i] = addr[i]; + + this.hostname = hostname; + this.hostname_alias = hostname_alias; + lookup_time = System.currentTimeMillis(); +} + +/*************************************************************************/ + +/** + * Instance Methods + */ + +/** + * Tests this address for equality against another InetAddress. The two + * addresses are considered equal if they contain the exact same octets. + * This implementation overrides Object.equals() + * + * @param addr The address to test for equality + * + * @return true if the passed in object's address is equal to this one's, false otherwise + */ +public boolean +equals(Object addr) +{ + if (!(addr instanceof InetAddress)) + return(false); + + byte[] test_ip = ((InetAddress)addr).getAddress(); + + if (test_ip.length != my_ip.length) + return(false); + + for (int i = 0; i < my_ip.length; i++) + if (test_ip[i] != (byte)my_ip[i]) + return(false); + + return(true); +} + +/*************************************************************************/ + +/** + * Returns the IP address of this object as a int array. + * + * @return IP address + */ +public byte[] +getAddress() +{ + byte[] addr = new byte[my_ip.length]; + + for (int i = 0; i < my_ip.length; i++) + { + addr[i] = (byte)my_ip[i]; + } + + return(addr); +} + +/*************************************************************************/ + +/** + * Returns the IP address of this object as a String. The address is in + * the dotted octet notation, for example, "127.0.0.1". + * + * @return The IP address of this object in String form + */ +public String +getHostAddress() +{ + StringBuffer addr = new StringBuffer(); + + for (int i = 0; i < my_ip.length; i++) + { + addr.append((int)my_ip[i]); + if (i < (my_ip.length - 1)) + addr.append("."); + } + + return(addr.toString()); +} + +/*************************************************************************/ + +/** + * Returns the hostname for this address. This will return the IP address + * as a String if there is no hostname available for this address + * + * @return The hostname for this address + */ +public String +getHostName() +{ + if (hostname != null) + return(hostname); + + try + { + hostname = getHostByAddr(my_ip); + return(hostname); + } + catch (UnknownHostException e) + { + if (hostname_alias != null) + return(hostname_alias); + else + return(getHostAddress()); + } +} + +/*************************************************************************/ + +/** + * Returns a hash value for this address. Useful for creating hash + * tables. Overrides Object.hashCode() + * + * @return A hash value for this address. + */ +public int +hashCode() +{ + long val1 = 0, val2 = 0; + + // Its obvious here that I have no idea how to generate a good + // hash key + for (int i = 0; i < my_ip.length; i++) + val1 = val1 + (my_ip[i] << ((my_ip.length - i) / 8)); + + for (int i = 0; i < my_ip.length; i++) + val2 = val2 + (my_ip[i] * 10 * i); + + val1 = (val1 >> 1) ^ val2; + + return((int)val1); +} + +/*************************************************************************/ + +/** + * Returns true if this address is a multicast address, false otherwise. + * An address is multicast if the high four bits are "1110". These are + * also known as "Class D" addresses. + * + * @return true if mulitcast, false if not + */ +public boolean +isMulticastAddress() +{ + if (my_ip.length == 0) + return(false); + + // Mask against high order bits of 1110 + if ((my_ip[0] & 224) == 224) + return(true); + + return(false); +} + +/*************************************************************************/ + +/** + * Converts this address to a String. This string contains the IP in + * dotted decimal form. For example: "127.0.0.1" This method is equivalent + * to getHostAddress() and overrides Object.toString() + * + * @return This address in String form + */ +public String +toString() +{ + return(getHostAddress()); +} + +/*************************************************************************/ + +/* + * Native Methods + */ + +/** + * This native method looks up the hostname of the local machine + * we are on. If the actual hostname cannot be determined, then the + * value "localhost" we be used. This native method wrappers the + * "gethostname" function. + * + * @return The local hostname. + */ +private static native String +getLocalHostName(); + +/*************************************************************************/ + +/** + * Returns the value of the special address INADDR_ANY + */ +private static native int[] +lookupInaddrAny() throws UnknownHostException; + +/*************************************************************************/ + +/** + * This method returns the hostname for a given IP address. It will + * throw an UnknownHostException if the hostname cannot be determined. + * + * @param ip The IP address as a int arrary + * + * @return The hostname + * + * @exception UnknownHostException If the reverse lookup fails + */ +private static native String +getHostByAddr(int[] ip) throws UnknownHostException; + +/*************************************************************************/ + +/** + * Returns a list of all IP addresses for a given hostname. Will throw + * an UnknownHostException if the hostname cannot be resolved. + */ +private static native int[][] +getHostByName(String hostname) throws UnknownHostException; + +/*************************************************************************/ + +public static void +main(String[] argv) +{ + System.out.println("The name of the local host is: " + + getLocalHostName()); +} + +} // class InetAddress + + diff --git a/java/net/JarURLConnection.java b/java/net/JarURLConnection.java new file mode 100644 index 000000000..64f445f96 --- /dev/null +++ b/java/net/JarURLConnection.java @@ -0,0 +1,237 @@ +/************************************************************************* +/* JarURLConnection.java -- Class for manipulating remote jar files +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; +import java.security.Identity; +import java.util.jar.*; + +/** + * This abstract class represents a common superclass for implementations + * of jar URL's. A jar URL is a special type of URL that allows JAR + * files on remote systems to be accessed. It has the form: + * <p> + * jar:<standard URL pointing to jar file>!/file/within/jarfile + * <p> for example: + * <p> + * jar:http://www.urbanophile.com/java/foo.jar!/com/urbanophile/bar.class + * <p> + * That example URL points to the file /com/urbanophile/bar.class in the + * remote JAR file http://www.urbanophile.com/java/foo.jar. The HTTP + * protocol is used only as an example. Any supported remote protocol + * can be used. + * <p> + * This class currently works by retrieving the entire jar file into a + * local cache file, then performing standard jar operations on it. + * (At least this is true for the default protocol implementation). + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class JarURLConnection extends URLConnection +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the actual URL that points the remote jar file. This is parsed + * out of the jar URL by the constructor. + */ +private URL real_url; + +/** + * This is the jar file "entry name" or portion after the "!/" in the + * URL which represents the pathname inside the actual jar file + */ +private String entry_name; + +/** + * The JarFile object for the jar file pointed to by the real URL + */ +private JarFile jar_file; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Creates a JarURLConnection from a URL objects + * + * @param URL url The URL object for this connection. + */ +protected +JarURLConnection(URL url) throws MalformedURLException +{ + super(url); + + // Now, strip off the "jar:" and everything from the "!/" to the end + // to get the "real" URL inside + String url_string = url.toExternalForm(); + + if (!url_string.startsWith("jar:")) + throw new MalformedURLException(url_string); + + if (url_string.indexOf("!/") == -1) + throw new MalformedURLException(url_string); + + String real_url_string = url_string.substring(4, url_string.indexOf("!/") - 4); + + real_url = new URL(real_url_string); + if (url_string.length() == (url_string.indexOf("!/") + 1)) + entry_name = ""; + else + entry_name = url_string.substring(url_string.indexOf("!/") + 2); +} + +/*************************************************************************/ + +/** + * This method returns the "real" URL where the JarFile is located. + * //****Is this right?***** + * + * @return The remote URL + */ +public URL +getJarFileURL() +{ + return(real_url); +} + +/*************************************************************************/ + +/** + * Returns the "entry name" portion of the jar URL. This is the portion + * after the "!/" in the jar URL that represents the pathname inside the + * actual jar file. + * + * @return The entry name. + */ +public String +getEntryName() +{ + return(entry_name); +} + +/*************************************************************************/ + +/** + * Returns a read-only JarFile object for the remote jar file + * + * @return The JarFile object + * + * @exception IOException If an error occurs + */ +public abstract JarFile +getJarFile() throws IOException; + +/*************************************************************************/ + +/** + * Returns a Manifest object for this jar file, or null if there is no + * manifest. + * + * @return The Manifest + * + * @exception IOException If an error occurs + */ +public Manifest +getManifest() throws IOException +{ + if (jar_file == null) + jar_file = getJarFile(); + + return(jar_file.getManifest()); +} + +/*************************************************************************/ + +/** + * Returns the entry in this jar file specified by the URL. + * + * @return The jar entry + * + * @exception IOException If an error occurs + */ +public JarEntry +getJarEntry() throws IOException +{ + if (jar_file == null) + jar_file = getJarFile(); + + return(jar_file.getJarEntry(entry_name)); +} + +/*************************************************************************/ + +/** + * Returns the Attributes for the Jar entry specified by the URL or null + * if none + * + * @return The Attributes + * + * @exception IOException If an error occurs + */ +public Attributes +getAttributes() throws IOException +{ + return(getJarEntry().getAttributes()); +} + +/*************************************************************************/ + +/** + * Returns the main Attributes for the jar file specified in the URL or + * null if there are none + * + * @return The main Attributes + * + * @exception IOException If an error occurs + */ +public Attributes +getMainAttributes() throws IOException +{ + return(getManifest().getMainAttributes()); +} + +/*************************************************************************/ + +/** + * Returns an array of Identity objects for the jar file entry specified + * by this URL or null if there are none + * + * @return An Identity array + * + * @exception IOException If an error occurs + */ +public Identity[] +getIdentities() throws IOException +{ + return(getJarEntry().getIdentities()); +} + +} // class JarURLConnection + diff --git a/java/net/Makefile.am b/java/net/Makefile.am new file mode 100644 index 000000000..ac7e42ae2 --- /dev/null +++ b/java/net/Makefile.am @@ -0,0 +1,23 @@ +## Input file for automake to generate the Makefile.in used by configure + +javanetdir = $(datadir)/java/net + +javanet_JAVA = BindException.java ConnectException.java ContentHandler.java \ + ContentHandlerFactory.java DatagramPacket.java \ + DatagramSocket.java DatagramSocketImpl.java FileNameMap.java \ + HttpURLConnection.java InetAddress.java \ + MalformedURLException.java \ + MimeTypeMapper.java MulticastSocket.java \ + NoRouteToHostException.java PasswordAuthentication.java \ + PlainDatagramSocketImpl.java PlainSocketImpl.java \ + ProtocolException.java ServerSocket.java Socket.java \ + SocketException.java SocketImpl.java SocketImplFactory.java \ + SocketInputStream.java SocketOptions.java \ + SocketOutputStream.java URL.java URLConnection.java \ + URLEncoder.java URLStreamHandler.java \ + URLStreamHandlerFactory.java UnknownHostException.java \ + UnknownServiceException.java + +EXTRA_DIST = Authenticator.java JarURLConnection.java NetPermission.java \ + SocketPermission.java STATUS TODO + diff --git a/java/net/MalformedURLException.java b/java/net/MalformedURLException.java new file mode 100644 index 000000000..a7419c0d8 --- /dev/null +++ b/java/net/MalformedURLException.java @@ -0,0 +1,61 @@ +/************************************************************************* +/* MalformedURLException.java -- A URL was not in a valid format +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This exception indicates that a URL passed to an object was not in a + * valid format. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MalformedURLException extends java.io.IOException +{ + +/* + * Constructors + */ + +/** + * Constructs a new MalformedURLException with no descriptive message. + */ +public +MalformedURLException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new MalformedURLException with a descriptive message + * passed in as an argument. + * + * @param message A message describing the error that occurs + */ +public +MalformedURLException(String message) +{ + super(message); +} + +} // class MalformedURLException + diff --git a/java/net/MimeTypeMapper.java b/java/net/MimeTypeMapper.java new file mode 100644 index 000000000..dc9a639a2 --- /dev/null +++ b/java/net/MimeTypeMapper.java @@ -0,0 +1,216 @@ +/************************************************************************* +/* MimeTypeMapper.java -- A class for mapping file names to MIME types +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.util.Hashtable; + +/** + * This non-public class is used to implement the FileNameMap interface + * which defines a mechanism for mapping filenames to MIME types. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class MimeTypeMapper implements FileNameMap +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * This array of strings is used to identify a MIME type based on a file + * extension. This is list is based on the Apache mime.types file. + */ +protected static final String[][] mime_strings = { + { "application/mac-binhex40", "hqx" }, + { "application/mac-compactpro", "cpt" }, + { "application/msword", "doc" }, + { "application/octet-stream", "bin" }, + { "application/octet-stream", "dms" }, + { "application/octet-stream", "lha" }, + { "application/octet-stream", "lzh" }, + { "application/octet-stream", "exe" }, + { "application/octet-stream", "class" }, + { "application/oda", "oda" }, + { "application/pdf", "pdf" }, + { "application/postscript", "ai" }, + { "application/postscript", "eps" }, + { "application/postscript", "ps" }, + { "application/powerpoint", "ppt" }, + { "application/rtf", "rtf" }, + { "application/x-bcpio", "bcpio" }, + { "application/x-cdlink", "vcd" }, + { "application/x-compress", "Z" }, + { "application/x-cpio", "cpio" }, + { "application/x-csh", "csh" }, + { "application/x-director", "dcr" }, + { "application/x-director", "dir" }, + { "application/x-director", "dxr" }, + { "application/x-dvi", "dvi" }, + { "application/x-gtar", "gtar" }, + { "application/x-gzip", "gz" }, + { "application/x-hdf", "hdf" }, + { "application/x-httpd-cgi", "cgi" }, + { "application/x-koan", "skp" }, + { "application/x-koan", "skd" }, + { "application/x-koan", "skt" }, + { "application/x-koan", "skm" }, + { "application/x-latex", "latex" }, + { "application/x-mif", "mif" }, + { "application/x-netcdf", "nc" }, + { "application/x-netcdf", "cdf" }, + { "application/x-sh", "sh" }, + { "application/x-shar", "shar" }, + { "application/x-stuffit", "sit" }, + { "application/x-sv4cpio", "sv4cpio" }, + { "application/x-sv4crc", "sv4crc" }, + { "application/x-tar", "tar" }, + { "application/x-tcl", "tcl" }, + { "application/x-tex", "tex" }, + { "application/x-texinfo", "texinfo" }, + { "application/x-texinfo", "texi" }, + { "application/x-troff", "t" }, + { "application/x-troff", "tr" }, + { "application/x-troff", "roff" }, + { "application/x-troff-man", "man" }, + { "application/x-troff-me", "me" }, + { "application/x-troff-ms", "ms" }, + { "application/x-ustar", "ustar" }, + { "application/x-wais-source", "src" }, + { "application/zip", "zip" }, + { "audio/basic", "au" }, + { "audio/basic", "snd" }, + { "audio/mpeg", "mpga" }, + { "audio/mpeg", "mp2" }, + { "audio/mpeg", "mp3" }, + { "audio/x-aiff", "aif" }, + { "audio/x-aiff", "aiff" }, + { "audio/x-aiff", "aifc" }, + { "audio/x-pn-realaudio", "ram" }, + { "audio/x-pn-realaudio-plugin", "rpm" }, + { "audio/x-realaudio", "ra" }, + { "audio/x-wav", "wav" }, + { "chemical/x-pdb", "pdb" }, + { "chemical/x-pdb", "xyz" }, + { "image/gif", "gif" }, + { "image/ief", "ief" }, + { "image/jpeg", "jpeg" }, + { "image/jpeg", "jpg" }, + { "image/jpeg", "jpe" }, + { "image/png", "png" }, + { "image/tiff", "tiff" }, + { "image/tiff", "tif" }, + { "image/x-cmu-raster", "ras" }, + { "image/x-portable-anymap", "pnm" }, + { "image/x-portable-bitmap", "pbm" }, + { "image/x-portable-graymap", "pgm" }, + { "image/x-portable-pixmap", "ppm" }, + { "image/x-rgb", "rgb" }, + { "image/x-xbitmap", "xbm" }, + { "image/x-xpixmap", "xpm" }, + { "image/x-xwindowdump", "xwd" }, + { "text/html", "html" }, + { "text/html", "htm" }, + { "text/plain", "txt" }, + { "text/richtext", "rtx" }, + { "text/tab-separated-values", "tsv" }, + { "text/x-setext", "etx" }, + { "text/x-sgml", "sgml" }, + { "text/x-sgml", "sgm" }, + { "video/mpeg", "mpeg" }, + { "video/mpeg", "mpg" }, + { "video/mpeg", "mpe" }, + { "video/quicktime", "qt" }, + { "video/quicktime", "mov" }, + { "video/x-msvideo", "avi" }, + { "video/x-sgi-movie", "movie" }, + { "x-conference/x-cooltalk", "ice" }, + { "x-world/x-vrml", "wrl" }, + { "x-world/x-vrml", "vrml" } + }; + +/** + * The MIME types above are put into this Hashtable for faster lookup. + */ +private static Hashtable mime_types = new Hashtable(150); + +// Static initializer to load MIME types into Hashtable +static +{ + for (int i = 0; i < mime_strings.length; i++) + mime_types.put(mime_strings[i][1], mime_strings[i][0]); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * A do nothing constructor + */ +public +MimeTypeMapper() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The method returns the MIME type of the filename passed as an argument. + * The value returned is based on the extension of the filename. The + * default content type returned if this method cannot determine the + * actual content type is "application/octet-stream" + * + * @param filename The name of the file to return the MIME type for + * + * @return The MIME type + */ +public String +getContentTypeFor(String filename) +{ + int index = filename.lastIndexOf("."); + if (index != -1) + { + if (index == filename.length()) + return("application/octet-stream"); + else + filename = filename.substring(index + 1); + } + + String type = (String)mime_types.get(filename); + if (type == null) + return("application/octet-stream"); + else + return(type); +} + +} // class MimeTypeMapper + diff --git a/java/net/MulticastSocket.java b/java/net/MulticastSocket.java new file mode 100644 index 000000000..029357f4e --- /dev/null +++ b/java/net/MulticastSocket.java @@ -0,0 +1,197 @@ +/************************************************************************* +/* MulticastSocket.java -- Class for using multicast sockets +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This class models a multicast UDP socket. A multicast address is a + * class D internet address (one whose most significant bits are 1110). + * A multicast group consists of a multicast address and a well known + * port number. All members of the group listening on that address and + * port will receive all the broadcasts to the group. + * <p> + * Please note that applets are not allowed to use multicast sockets + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class MulticastSocket extends DatagramSocket +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Create a MulticastSocket that this not bound to any address + * + * @exception IOException If an error occurs + */ +public +MulticastSocket() throws IOException +{ + super(); +} + +/*************************************************************************/ + +/** + * Create a multicast socket bound to the specified port + * + * @param The port to bind to + * + * @exception IOException If an error occurs + */ +public +MulticastSocket(int port) throws IOException +{ + super(port); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the current value of the "Time to Live" option. This is the + * number of hops a packet can make before it "expires". + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ +public synchronized byte +getTTL() throws IOException +{ + return(impl.getTTL()); +} + +/*************************************************************************/ + +/** + * Sets the "Time to Live" value for a socket. The value must be between + * 1 and 255. + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ +public synchronized void +setTTL(byte ttl) throws IOException +{ + impl.setTTL(ttl); +} + +/*************************************************************************/ + +/** + * Returns the interface being used for multicast packets + * + * @return The multicast interface + * + * @exception SocketException If an error occurs + */ +public synchronized InetAddress +getInterface() throws SocketException +{ + Object obj; + + obj = impl.getOption(SocketOptions.IP_MULTICAST_IF); + + if (!(obj instanceof InetAddress)) + throw new SocketException("Internal Error"); + + return(((InetAddress)obj)); +} + +/*************************************************************************/ + +/** + * Sets the interface to use for multicast packets. + * + * @param addr The new interface to use + * + * @exception SocketException If an error occurs + */ +public synchronized void +setInterface(InetAddress addr) throws SocketException +{ + impl.setOption(SocketOptions.IP_MULTICAST_IF, addr); +} + +/*************************************************************************/ + +/** + * Joins the specified mulitcast group. + * + * @param addr The address of the group to join + * + * @exception IOException If an error occurs + */ +public synchronized void +joinGroup(InetAddress addr) throws IOException +{ + impl.join(addr); +} + +/*************************************************************************/ + +/** + * Leaves the specified multicast group + * + * @param addr The address of the group to leave + * + * @exception IOException If an error occurs + */ +public synchronized void +leaveGroup(InetAddress addr) throws IOException +{ + impl.leave(addr); +} + +/*************************************************************************/ + +/** + * Sends a packet of data to a multicast address with a TTL that is + * different from the default TTL on this socket. The default TTL for + * the socket is not changed. + * + * @param packet The packet of data to send + * @param ttl The TTL for this packet + * + * @exception IOException If an error occurs + */ +public synchronized void +send(DatagramPacket packet, byte ttl) throws IOException +{ + byte old_ttl = getTTL(); + setTTL(ttl); + send(packet); + setTTL(old_ttl); +} + +} // class MulticastSocket + diff --git a/java/net/NetPermission.java b/java/net/NetPermission.java new file mode 100644 index 000000000..4cf96c982 --- /dev/null +++ b/java/net/NetPermission.java @@ -0,0 +1,72 @@ +/************************************************************************* +/* NetPermission.java -- A class for basic miscellaneous network permission +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.security.BasicPermission; + +/** + * This class is used to model miscellaneous network permissions. It is + * a subclass of BasicPermission. This means that it models a "boolean" + * permission. One that you either have or do not have. Thus there is + * no permitted action list associated with this object. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class NetPermission extends BasicPermission +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Creates a new NetPermission with the specified name which is any valid + * String. + * + * @param name The name of this permission + */ +public +NetPermission(String name) +{ + super(name); +} + +/*************************************************************************/ + +/** + * Creates a new NetPermission with the specified name and value. Note that + * the value field is irrelevant and is ignored. This constructor should + * never need to be used. + * + * @param name The name of this permission + * @param perms The permitted actions of this permission (ignored) + */ +public +NetPermission(String name, String perms) +{ + super(name); +} + +} // class NetPermission + diff --git a/java/net/NoRouteToHostException.java b/java/net/NoRouteToHostException.java new file mode 100644 index 000000000..64322529d --- /dev/null +++ b/java/net/NoRouteToHostException.java @@ -0,0 +1,61 @@ +/************************************************************************* +/* NoRouteToHostException.java -- Cannot connect to a host +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This exception indicates that there is no TCP/IP route to the requested + * host. This is often due to a misconfigured routing table. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class NoRouteToHostException extends SocketException +{ + +/* + * Constructors + */ + +/** + * Constructs a new NoRouteToHostException with no descriptive message. + */ +public +NoRouteToHostException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new NoRouteToHostException with a descriptive message (such as the + * text from strerror(3)) passed in as an argument + * + * @param message A message describing the error that occurs + */ +public +NoRouteToHostException(String message) +{ + super(message); +} + +} // class NoRouteToHostException + diff --git a/java/net/PasswordAuthentication.java b/java/net/PasswordAuthentication.java new file mode 100644 index 000000000..88f469d23 --- /dev/null +++ b/java/net/PasswordAuthentication.java @@ -0,0 +1,99 @@ +/************************************************************************* +/* PasswordAuthentication.java -- Container class for username/password pairs +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This class serves a container for username/password pairs. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class PasswordAuthentication +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The username + */ +protected String username; + +/** + * The password + */ +protected String password; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Creates a new PasswordAuthentication object from the specified username + * and password. + * + * @param username The username for this object + * @param password The password for this object + */ +public +PasswordAuthentication(String username, String password) +{ + this.username = username; + this.password = password; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the username associated with this object + * + * @return The username + */ +public String +getUsername() +{ + return(username); +} + +/*************************************************************************/ + +/** + * Returns the password associated with this object + * + * @return The password + */ +public String +getPassword() +{ + return(password); +} + +} // class PasswordAuthentication + diff --git a/java/net/PlainDatagramSocketImpl.java b/java/net/PlainDatagramSocketImpl.java new file mode 100644 index 000000000..356f3f0c9 --- /dev/null +++ b/java/net/PlainDatagramSocketImpl.java @@ -0,0 +1,250 @@ +/************************************************************************* +/* PlainDatagramSocketImpl.java -- Default DatagramSocket implementation +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This is the default socket implementation for datagram sockets. + * It makes native calls to C routines that implement BSD style + * SOCK_DGRAM sockets in the AF_INET family. + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PlainDatagramSocketImpl extends DatagramSocketImpl +{ + +/*************************************************************************/ + +/* + * Static Variables + */ + +// Static initializer to load native library +static +{ + System.loadLibrary("javanet"); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the actual underlying file descriptor + */ +protected int native_fd = -1; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Default do nothing constructor + */ +public +PlainDatagramSocketImpl() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Creates a new datagram socket + * + * @exception SocketException If an error occurs + */ +protected native synchronized void +create() throws SocketException; + +/*************************************************************************/ + +/** + * Closes the socket + */ +protected native synchronized void +close(); + +/*************************************************************************/ + +/** + * Binds this socket to a particular port and interface + * + * @param port The port to bind to + * @param addr The address to bind to + * + * @exception SocketException If an error occurs + */ +protected native synchronized void +bind(int port, InetAddress addr) throws SocketException; + +/*************************************************************************/ + +/** + * Sends a packet of data to a remote host + * + * @param packet The packet to send + * + * @exception IOException If an error occurs + */ +protected synchronized void +send(DatagramPacket packet) throws IOException +{ + sendto(packet.getAddress(), packet.getPort(), packet.getData(), + packet.getLength()); +} + +/*************************************************************************/ + +/** + * Sends a packet of data to a remote host + * + * @param addr The address to send to + * @param port The port to send to + * @param buf The buffer to send + * @param len The length of the data to send + * + * @exception IOException If an error occurs + */ +private native synchronized void +sendto(InetAddress addr, int port, byte[] buf, int len) throws IOException; + +/*************************************************************************/ + +/** + * What does this method really do? + */ +protected synchronized int +peek(InetAddress addr) throws IOException +{ + throw new IOException("Not Implemented Yet"); +} + +/*************************************************************************/ + +/** + * Receives a UDP packet from the network + * + * @param packet The packet to fill in with the data received + * + * @exception IOException IOException If an error occurs + */ +protected native synchronized void +receive(DatagramPacket packet) throws IOException; + +/*************************************************************************/ + +/** + * Joins a multicast group + * + * @param addr The group to join + * + * @exception IOException If an error occurs + */ +protected native synchronized void +join(InetAddress addr) throws IOException; + +/*************************************************************************/ + +/** + * Leaves a multicast group + * + * @param addr The group to leave + * + * @exception IOException If an error occurs + */ +protected native synchronized void +leave(InetAddress addr) throws IOException; + +/*************************************************************************/ + +/** + * Gets the Time to Live value for the socket + * + * @return The TTL value + * + * @exception IOException If an error occurs + */ +protected synchronized byte +getTTL() throws IOException +{ + Object obj = getOption(SocketOptions.IP_TTL); + + if (!(obj instanceof Integer)) + throw new IOException("Internal Error"); + + return(((Integer)obj).byteValue()); +} + +/*************************************************************************/ + +/** + * Sets the Time to Live value for the socket + * + * @param ttl The new TTL value + * + * @exception IOException If an error occurs + */ +protected synchronized void +setTTL(byte ttl) throws IOException +{ + setOption(SocketOptions.IP_TTL, new Integer(ttl)); +} + +/*************************************************************************/ + +/** + * Retrieves the value of an option on the socket + * + * @param option_id The identifier of the option to retrieve + * + * @return The value of the option + * + * @exception SocketException If an error occurs + */ +public native synchronized Object +getOption(int option_id) throws SocketException; + +/*************************************************************************/ + +/** + * Sets the value of an option on the socket + * + * @param option_id The identifier of the option to set + * @param val The value of the option to set + * + * @exception SocketException If an error occurs + */ +public native synchronized void +setOption(int option_id, Object val) throws SocketException; + +} // class PlainDatagramSocketImpl + diff --git a/java/net/PlainSocketImpl.java b/java/net/PlainSocketImpl.java new file mode 100644 index 000000000..448f7f10d --- /dev/null +++ b/java/net/PlainSocketImpl.java @@ -0,0 +1,285 @@ +/************************************************************************* +/* PlainSocketImpl.java -- Default socket implementation +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +/** + * Unless the application installs its own SocketImplFactory, this is the + * default socket implemetation that will be used. It simply uses a + * combination of Java and native routines to implement standard BSD + * style sockets of family AF_INET and types SOCK_STREAM and SOCK_DGRAM + * + * @version 0.1 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class PlainSocketImpl extends SocketImpl +{ + +/*************************************************************************/ + +/* + * Static Variables + */ + +// Static initializer to load native library +static +{ + System.loadLibrary("javanet"); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the native file descriptor for this socket + */ +protected int native_fd = -1; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Default do nothing constructor + */ +public +PlainSocketImpl() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * Accepts a new connection on this socket and returns in in the + * passed in SocketImpl. + * + * @param impl The SocketImpl object to accept this connection. + */ +protected native synchronized void +accept(SocketImpl impl) throws IOException; + +/*************************************************************************/ + +/** + * Returns the number of bytes that the caller can read from this socket + * without blocking. //*****Figure out if we can do something here + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ +protected int +available() throws IOException +{ + return(0); +} + +/*************************************************************************/ + +/** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. **** How bind to INADDR_ANY? **** + * + * @param addr The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ +protected native synchronized void +bind(InetAddress addr, int port) throws IOException; + +/*************************************************************************/ + +/** + * Closes the socket. This will cause any InputStream or OutputStream + * objects for this Socket to be closed as well. + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ +protected native synchronized void +close() throws IOException; + +/*************************************************************************/ + +/** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ +protected native synchronized void +connect(InetAddress addr, int port) throws IOException; + +/*************************************************************************/ + +/** + * Connects to the remote hostname and port specified as arguments. + * + * @param hostname The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ +protected synchronized void +connect(String hostname, int port) throws IOException +{ + InetAddress addr = InetAddress.getByName(hostname); + connect(addr, port); +} + +/*************************************************************************/ + +/** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + */ +protected native synchronized void +create(boolean stream) throws IOException; + +/*************************************************************************/ + +/** + * Starts listening for connections on a socket. The queuelen parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param queuelen The length of the pending connection queue + * + * @exception IOException If an error occurs + */ +protected native synchronized void +listen(int queuelen) throws IOException; + +/*************************************************************************/ + +/** + * Internal method used by SocketInputStream for reading data from + * the connection. Reads up to len bytes of data into the buffer + * buf starting at offset bytes into the buffer. + * + * @return The actual number of bytes read or -1 if end of stream. + * + * @exception IOException If an error occurs + */ +protected native synchronized int +read(byte[] buf, int offset, int len) throws IOException; + +/*************************************************************************/ + +/** + * Internal method used by SocketOuputStream for writing data to + * the connection. Writes up to len bytes of data from the buffer + * buf starting at offset bytes into the buffer. + * + * @exception IOException If an error occurs + */ +protected native synchronized void +write(byte[] buf, int offset, int len) throws IOException; + +/*************************************************************************/ + +/** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. The option_id parameter is one of the defined constants in + * this interface. + * + * @param option_id The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ +public native synchronized void +setOption(int option_id, Object val) throws SocketException; + +/*************************************************************************/ + +/** + * Returns the current setting of the specified option. The Object returned + * will be an Integer for options that have integer values. The option_id + * is one of the defined constants in this interface. + * + * @param option_id The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ +public native synchronized Object +getOption(int option_id) throws SocketException; + +/*************************************************************************/ + +/** + * Returns an InputStream object for reading from this socket. This will + * be an instance of SocketInputStream. + * + * @return An InputStream + * + * @exception IOException If an error occurs + */ +protected synchronized InputStream +getInputStream() throws IOException +{ + return(new SocketInputStream(this)); +} + +/*************************************************************************/ + +/** + * Returns an OutputStream object for writing to this socket. This will + * be an instance of SocketOutputStream. + * + * @return An OutputStream + * + * @exception IOException If an error occurs + */ +protected synchronized OutputStream +getOutputStream() throws IOException +{ + return(new SocketOutputStream(this)); +} + +} // class PlainSocketImpl + diff --git a/java/net/ProtocolException.java b/java/net/ProtocolException.java new file mode 100644 index 000000000..a1d3da1f9 --- /dev/null +++ b/java/net/ProtocolException.java @@ -0,0 +1,62 @@ +/************************************************************************* +/* ProtocolException.java -- A low level protocol error occuredA low level protocol error occured +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This exception indicates that some sort of low level protocol + * exception occured. Look in the descriptive message (if any) for + * details on what went wrong + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ProtocolException extends java.io.IOException +{ + +/* + * Constructors + */ + +/** + * Constructs a new ProtocolException with no descriptive message. + */ +public +ProtocolException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new ProtocolException with a descriptive message (such as the + * text from strerror(3)) passed in as an argument + * + * @param message A message describing the error that occurs + */ +public +ProtocolException(String message) +{ + super(message); +} + +} // class ProtocolException + diff --git a/java/net/STATUS b/java/net/STATUS new file mode 100644 index 000000000..25dff963e --- /dev/null +++ b/java/net/STATUS @@ -0,0 +1,48 @@ +X ContentHandlerFactory +X FileNameMap +X SocketImplFactory +X URLStreamHandlerFactory +* Authenticator +* ContentHandler ++ DatagramPacket ++ DatagramSocket ++ DatagramSocketImpl ++ HttpURLConnection ++ InetAddress +* JarURLConnection ++ MulticastSocket +* NetPermission +* PasswordAuthentication ++ PlainDatagramSocketImpl ++ PlainSocketImpl (internal) ++ ServerSocket ++ Socket ++ SocketImpl ++ SocketInputStream (internal) ++ SocketOptions (internal) ++ SocketOutputStream (internal) +* SocketPermission ++ URL + URLClassLoader ++ URLConnection +* URLEncoder ++ URLStreamHandler +X BindException +X ConnectException +X MalformedURLException +X NoRouteToHostException +X ProtocolException +X SocketException +X UnknownHostException +X UnknownServiceException + +--------------- ++ Native InetAddress ++ Native SocketImpl ++ Native DatagramSocketImpl ++ Protocol Handler for HTTP + Protocol Handler for FTP ++ ContentHandler for text + ContentHandler for gif + ContentHandler for jpeg + diff --git a/java/net/ServerSocket.java b/java/net/ServerSocket.java new file mode 100644 index 000000000..1c4a0d4d8 --- /dev/null +++ b/java/net/ServerSocket.java @@ -0,0 +1,324 @@ +/************************************************************************* +/* ServerSocket.java -- Class for implementing server side sockets +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; +//import java.security.AccessControlException; + +/** + * This class models server side sockets. The basic model is that the + * server socket is created and bound to some well known port. It then + * listens for and accepts connections. At that point the client and + * server sockets are ready to communicate with one another utilizing + * whatever application layer protocol they desire. + * <p> + * As with the Socket class, most instance methods of this class simply + * redirect their calls to an implementation class. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class ServerSocket +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * This is the user defined SocketImplFactory, if one is supplied + */ +protected static SocketImplFactory factory; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This is the SocketImp object to which most instance methods in this + * class are redirected + */ +protected SocketImpl impl; + +/*************************************************************************/ + +/** + * Since the implementation doesn't directly store this value, we keep + * it here. This is the local address we are bound to. + */ +InetAddress addr; + +/*************************************************************************/ + +/* + * Class methods + */ + +/** + * Sets the SocketImplFactory for all ServerSockets. This may only be done + * once per virtual machine. Subsequent attempts will generate a + * SocketException. Note that a SecurityManager check is made prior to + * setting the factory. If insufficient privileges exist to set the factory, + * an IOException will be thrown + * + * @exception SocketException If the factory object is already defined + * @exception IOException If any other error occurs + */ +public static synchronized void +setSocketFactory(SocketImplFactory factory) throws IOException +{ + // See if already set + if (ServerSocket.factory != null) + throw new SocketException("SocketImplFactory already defined"); + + // Check permission to perform this operation + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + try + { + sm.checkSetFactory(); + } +// catch (AccessControlException e) + catch (SecurityException e) + { + throw new IOException(e.toString()); + } + } + + ServerSocket.factory = factory; +} + +/*************************************************************************/ + +/** + * This protected constructor is used by subclasses of ServerSocket. It + * simply load the implementation and returns + */ +protected +ServerSocket() +{ + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); +} + +/*************************************************************************/ + +/** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to 50. + * + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ +public +ServerSocket(int port) throws IOException +{ + this(port, 50, InetAddress.getInaddrAny()); +} + +/*************************************************************************/ + +/** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * arg2. + * + * @param port The port number to bind to + * @param queuelen The length of the pending connection queue + * + * @exception IOException If an error occurs + */ +public +ServerSocket(int port, int queuelen) throws IOException +{ + this(port, queuelen, InetAddress.getInaddrAny()); +} + +/*************************************************************************/ + +/** + * Creates a server socket and binds it to the specified port. If the + * port number is 0, a random free port will be chosen. The pending + * connection queue on this socket will be set to the value passed as + * arg2. The third argument specifies a particular local address to + * bind to. + * + * @param port The port number to bind to + * @param queuelen The length of the pending connection queue + * @param addr The address to bind to + * + * @exception IOException If an error occurs + */ +public +ServerSocket(int port, int queuelen, InetAddress addr) throws IOException +{ + this(); + if (impl == null) + throw new IOException("Cannot initialize Socket implementation"); + + impl.create(true); + impl.bind(addr, port); + this.addr = addr; + impl.listen(queuelen); + +} + +/*************************************************************************/ + +/** + * This protected method is used to help subclasses override + * ServerSocket.accept(). The passed in Socket will be connected when + * this method returns. + * + * @param socket The Socket that is used for the accepted connection + * + * @exception IOException If an error occurs + */ +protected final synchronized void +implAccept(Socket socket) throws IOException +{ + impl.accept(socket.impl); +} + +/*************************************************************************/ + +/** + * Accepts a new connection and returns a connected Socket instance + * representing that connection. This method will block until a + * connection is available. + * + * @exception IOException If an error occurs + */ +public synchronized Socket +accept() throws IOException +{ + Socket socket = new Socket(); + impl.accept(socket.impl); + + return(socket); +} + +/*************************************************************************/ + +/** + * Closes this socket and stops listening for connections + * + * @exception IOException If an error occurs + */ +public synchronized void +close() throws IOException +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * This method returns the local address to which this socket is bound + * + * @return The socket's local address + */ +public InetAddress +getInetAddress() +{ + return(addr); +} + +/*************************************************************************/ + +/** + * This method returns the local port number to which this socket is bound + * + * @return The socket's port number + */ +public int +getLocalPort() +{ + return(impl.getLocalPort()); +} + +/*************************************************************************/ + +/** + * Retrieves the current value of the SO_TIMEOUT setting. A value of 0 + * implies that SO_TIMEOUT is disabled (ie, operations never time out). + * This is the number of milliseconds a socket operation can block before + * an InterruptedIOException is thrown. + * + * @return The value of SO_TIMEOUT + * + * @exception IOException If an error occurs + */ +public synchronized int +getSoTimeout() throws IOException +{ + Object obj = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (!(obj instanceof Integer)) + throw new IOException("Internal Error"); + + return(((Integer)obj).intValue()); +} + +/*************************************************************************/ + +/** + * Sets the value of SO_TIMEOUT. A value of 0 implies that SO_TIMEOUT is + * disabled (ie, operations never time out). This is the number of + * milliseconds a socket operation can block before an + * InterruptedIOException is thrown. + * + * @param timeout The new SO_TIMEOUT value + * + * @exception IOException If an error occurs + */ +public synchronized void +setSoTimeout(int timeout) throws IOException +{ + impl.setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); +} + +/*************************************************************************/ + +/** + * Returns the value of this ServerSocket as a String. Overrides + * Object.toString() + * + * @return This socket represented as a String + */ +public String +toString() +{ + return(addr + ":" + getLocalPort()); +} + +} // class ServerSocket + diff --git a/java/net/Socket.java b/java/net/Socket.java new file mode 100644 index 000000000..6708232e9 --- /dev/null +++ b/java/net/Socket.java @@ -0,0 +1,591 @@ +/************************************************************************* +/* Socket.java -- Client socket implementation +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.*; +import java.security.*; + +/** + * This class models a client site socket. A socket is a TCP/IP endpoint + * for network communications conceptually similar to a file handle. + * <p> + * This class does not actually do any work. Instead, it redirects all of + * its calls to a socket implementation object which implements the + * SocketImpl interface. The implementation class is instantiated by + * factory class that implements the SocketImplFactory interface. A default + * factory is provided, however the factory may be set by a call to + * the setSocketImplFactory method. Note that this may only be done once + * per virtual machine. If a subsequent attempt is made to set the + * factory, a SocketException will be thrown. + * + * @version + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class Socket +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * This is the user SocketImplFactory for this class. If this variable is + * null, a default factory is used. + */ +protected static SocketImplFactory factory; + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The implementation object to which calls are redirected + */ +protected SocketImpl impl; + +/** + * The local address to which we are connected + */ +InetAddress local_addr; + +/*************************************************************************/ + +/* + * Class Methods + */ + +/** + * Sets the SocketImplFactory. This may be done only once per virtual + * machine. Subsequent attempts will generate a SocketException. Note that + * a SecurityManager check is made prior to setting the factory. If + * insufficient privileges exist to set the factory, then an IOException + * will be thrown. + * + * @exception SocketException If the SocketImplFactory is already defined + * @exception IOException If any other error occurs + */ +public static synchronized void +setSocketImplFactory(SocketImplFactory factory) throws IOException +{ + // See if already set + if (Socket.factory != null) + throw new SocketException("SocketImplFactory already defined"); + + // Check permissions + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + { + try + { + sm.checkSetFactory(); + } +// catch (AccessControlException e) + catch (SecurityException e) + { + throw new IOException(e.toString()); + } + } + + Socket.factory = factory; +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This creates a Socket object without connecting to a remote host. This + * useful for subclasses of socket that might want this behavior. + */ +protected +Socket() +{ + if (factory != null) + impl = factory.createSocketImpl(); + else + impl = new PlainSocketImpl(); +} + +/*************************************************************************/ + +/** + * This creates a Socket object without connecting to a remote host. This + * is useful for subclasses of socket that might want this behavior. + * Additionally, this socket will be created using the supplied implementation + * class instead the default class or one returned by a factory. This + * value can be null, but if it is, all instance methods in Socket should + * be overridden because most of them rely on this value being populated. + * + * @param impl The SocketImpl to use for this Socket + * + * @exception SocketException If an error occurs + */ +protected +Socket(SocketImpl impl) throws SocketException +{ + this.impl = impl; +} + +/*************************************************************************/ + +/** + * Creates a Socket and connects to the address and port number specified + * as arguments. + * + * @param add The address to connect to + * @param port The port number to connect to + * + * @exception IOException If an error occurs + */ +public +Socket(InetAddress addr, int port) throws IOException +{ + this(addr, port, InetAddress.getInaddrAny(), 0, true); +} + +/*************************************************************************/ + +/** + * Creates a Socket and connects to the address and port number specified + * as arguments. If the stream param is true, a stream socket will be + * created, otherwise a datagram socket is created. + * <p> + * <b>This method is deprecated. Use the DatagramSocket class for creating + * datagram sockets.</b> + * + * @param add The address to connect to + * @param port The port number to connect to + * @param stream True to create a stream socket, false to create a datagram socket + * + * @exception IOException If an error occurs + * + * @deprecated + */ +public +Socket(InetAddress addr, int port, boolean stream) throws IOException +{ + this(addr, port, InetAddress.getInaddrAny(), 0, stream); +} + +/*************************************************************************/ + +/** + * Creates a Socket and connects to the address and port number specified + * as arguments, plus binds to the specified local address and port. + * + * @param raddr The remote address to connect to + * @param rport The remote port to connect to + * @param laddr The local address to connect to + * @param lport The local port to connect to + * + * @exception IOException If an error occurs + */ +public +Socket(InetAddress raddr, int rport, InetAddress laddr, + int lport) throws IOException +{ + this(raddr, rport, laddr, lport, true); +} + +/*************************************************************************/ + +/** + * Creates a Socket and connects to the hostname and port specified as + * arguments. + * + * @param hostname The name of the host to connect to + * @param port The port number to connect to + * + * @exception UnknownHostException If the hostname cannot be resolved to an address + * @exception IOException If an error occurs + */ +public +Socket(String hostname, int port) throws IOException +{ + this(InetAddress.getByName(hostname), port, InetAddress.getInaddrAny(), + 0, true); +} + +/*************************************************************************/ + +/** + * Creates a Socket and connects to the hostname and port specified as + * arguments. If the stream argument is set to true, then a stream socket + * is created. If it is false, a datagram socket is created. + * <p> + * <b>This method is deprecated. Use the DatagramSocket class to create + * datagram oriented sockets.</b> + * + * @param hostname The name of the host to connect to + * @param port The port to connect to + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + */ +public +Socket(String hostname, int port, boolean stream) throws IOException +{ + this(InetAddress.getByName(hostname), port, InetAddress.getInaddrAny(), + 0, stream); +} + +/*************************************************************************/ + +/** + * This constructor is where the real work takes place. Connect to the + * specified address and port. Use default local values if not specified, + * otherwise use the local host and port passed in. Create as stream or + * datagram based on "stream" argument. + * <p> + * ******* Check security ************* + * + * @param raddr The remote address to connect to + * @param rport The remote port to connect to + * @param laddr The local address to connect to + * @param lport The local port to connect to + * @param stream true for a stream socket, false for a datagram socket + * + * @exception IOException If an error occurs + */ +private +Socket(InetAddress raddr, int rport, InetAddress laddr, int lport, + boolean stream) throws IOException +{ + this(); + if (impl == null) + throw new IOException("Cannot initialize Socket implementation"); + + impl.create(stream); + + if (laddr != null) + impl.bind(laddr, lport); + + local_addr = laddr; + + if (raddr != null) + impl.connect(raddr, rport); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Closes the socket. + * + * @exception IOException If an error occurs + */ +public synchronized void +close() throws IOException +{ + if (impl != null) + impl.close(); +} + +/*************************************************************************/ + +/** + * Returns the address of the remote end of the socket. If this socket + * is not connected, then ??? + * + * @return The remote address this socket is connected to + */ +public InetAddress +getInetAddress() +{ + if (impl != null) + return(impl.getInetAddress()); + + return(null); +} + +/*************************************************************************/ + +/** + * Returns the port number of the remote end of the socket connection. If + * this socket is not connected, then ??? + * + * @return The remote port this socket is connected to + */ +public int +getPort() +{ + if (impl != null) + return(impl.getPort()); + + return(-1); +} + +/*************************************************************************/ + +/** + * Returns the local address to which this socket is bound. If this socket + * is not connected, then ??? + * + * @return The local address + */ +public InetAddress +getLocalAddress() +{ + return(local_addr); +} + +/*************************************************************************/ + +/** + * Returns the local port number to which this socket is bound. If this + * socket is not connected, then ??? + * + * @return The local port + */ +public int +getLocalPort() +{ + if (impl != null) + return(impl.getLocalPort()); + + return(-1); +} + +/*************************************************************************/ + +/** + * Returns an InputStream for reading from this socket. + * + * @return The InputStream object + * + * @exception IOException If an error occurs + */ +public synchronized InputStream +getInputStream() throws IOException +{ + if (impl != null) + return(impl.getInputStream()); + + throw new IOException("Not connected"); +} + +/*************************************************************************/ + +/** + * Returns an OutputStream for writing to this socket. + * + * @return The OutputStream object + * + * @exception IOException If an error occurs + */ +public synchronized OutputStream +getOutputStream() throws IOException +{ + if (impl != null) + return(impl.getOutputStream()); + + throw new IOException("Not connected"); +} + +/*************************************************************************/ + +/** + * Returns the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. This method either returns the timeouts (in hundredths of + * of a second (****????****)) if SO_LINGER is set, or -1 if SO_LINGER is + * not set. + * + * @return The SO_LINGER timeout in hundreths of a second or -1 if SO_LINGER not set + * + * @exception SocketException If an error occurs + */ +public synchronized int +getSoLinger() throws SocketException +{ + if (impl == null) + throw new SocketException("Not connected"); + + Object obj = impl.getOption(SocketOptions.SO_LINGER); + + if (obj instanceof Boolean) + return(-1); // Boolean is only returned in unset case + + if (obj instanceof Integer) + return(((Integer)obj).intValue()); + else + throw new SocketException("Internal Error"); +} + +/*************************************************************************/ + +/** + * Sets the value of the SO_LINGER option on the socket. If the + * SO_LINGER option is set on a socket and there is still data waiting to + * be sent when the socket is closed, then the close operation will block + * until either that data is delivered or until the timeout period + * expires. The linger interval is specified in hundreths of a second + * *********???????******** + * + * @param state true to enable SO_LINGER, false to disable + * @param timeout The SO_LINGER timeout in hundreths of a second or -1 if SO_LINGER not set + * + * @exception SocketException If an error occurs + */ +public synchronized void +setSoLinger(boolean state, int timeout) throws SocketException +{ + if (impl == null) + throw new SocketException("No socket created"); + + if (state == true) + impl.setOption(SocketOptions.SO_LINGER, new Integer(timeout)); + else + impl.setOption(SocketOptions.SO_LINGER, new Boolean(false)); + + return; +} + +/*************************************************************************/ + +/** + * Returns the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in thousandths of a second. (*****Is it *******); + * + * @return The length of the timeout in thousandth's of a second or 0 if not set + * + * @exception SocketException If an error occurs + */ +public synchronized int +getSoTimeout() throws SocketException +{ + if (impl == null) + throw new SocketException("Not connected"); + + Object obj = impl.getOption(SocketOptions.SO_TIMEOUT); + + if (obj instanceof Integer) + return(((Integer)obj).intValue()); + else + throw new SocketException("Internal Error"); +} + +/*************************************************************************/ + +/** + * Sets the value of the SO_TIMEOUT option on the socket. If this value + * is set, and an read/write is performed that does not complete within + * the timeout period, a short count is returned (or an EWOULDBLOCK signal + * would be sent in Unix if no data had been read). A value of 0 for + * this option implies that there is no timeout (ie, operations will + * block forever). On systems that have separate read and write timeout + * values, this method returns the read timeout. This + * value is in thousandths of a second (****????*****) + * + * @param timeout The length of the timeout in thousandth's of a second or 0 if not set + * + * @exception SocketException If an error occurs + */ +public synchronized void +setSoTimeout(int timeout) throws SocketException +{ + if (impl == null) + throw new SocketException("Not connected"); + + impl.setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); + + return; +} + +/*************************************************************************/ + +/** + * Tests whether or not the TCP_NODELAY option is set on the socket. + * Returns true if enabled, false if disabled. *** Need good explanation + * of this parameter. + * + * @return Whether or not TCP_NODELAY is set + * + * @exception SocketException If an error occurs + */ +public synchronized boolean +getTcpNoDelay() throws SocketException +{ + if (impl == null) + throw new SocketException("Not connected"); + + Object obj = impl.getOption(SocketOptions.TCP_NODELAY); + + if (obj instanceof Boolean) + return(((Boolean)obj).booleanValue()); + else + throw new SocketException("Internal Error"); +} + +/*************************************************************************/ + +/** + * Sets the TCP_NODELAY option is set on the socket. + * Returns true if enabled, false if disabled. *** Need good explanation + * of this parameter. + * + * @param state true to enable, false to disable + * + * @exception SocketException If an error occurs + */ +public synchronized void +setTcpNoDelay(boolean state) throws SocketException +{ + if (impl == null) + throw new SocketException("Not connected"); + + impl.setOption(SocketOptions.TCP_NODELAY, new Boolean(state)); + + return; +} + +/*************************************************************************/ + +/** + * Converts this Socket to a String. Overrides Object.toString() + * + * @return The String representation of this Socket + */ +public String +toString() +{ + return(getInetAddress().getHostName() + ":" + getPort()); +} + +} // class Socket + diff --git a/java/net/SocketException.java b/java/net/SocketException.java new file mode 100644 index 000000000..360eef28d --- /dev/null +++ b/java/net/SocketException.java @@ -0,0 +1,62 @@ +/************************************************************************* +/* SocketException.java -- An exception occured while performing a socket op +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This exception indicates that a generic error occured related to an + * operation on a socket. Check the descriptive message (if any) for + * details on the nature of this error + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class SocketException extends java.io.IOException +{ + +/* + * Constructors + */ + +/** + * Constructs a new SocketException with no descriptive message. + */ +public +SocketException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new SocketException with a descriptive message (such as the + * text from strerror(3)) passed in as an argument + * + * @param message A message describing the error that occurs + */ +public +SocketException(String message) +{ + super(message); +} + +} // class SocketException + diff --git a/java/net/SocketImpl.java b/java/net/SocketImpl.java new file mode 100644 index 000000000..e0f87ce62 --- /dev/null +++ b/java/net/SocketImpl.java @@ -0,0 +1,295 @@ +/************************************************************************* +/* SocketImpl.java -- Abstract socket implementation class +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.*; + +/** + * This abstract class serves as the parent class for socket implementations. + * The implementation class serves an intermediary to native routines that + * perform system specific socket operations. + * <p> + * A default implementation is provided by the system, but this can be + * changed via installing a SocketImplFactory (through a call to the + * static method Socket.setSocketImplFactory). A subclass of Socket can + * also pass in a SocketImpl to the Socket(SocketImpl) constructor to + * use an implementation different from the system default without installing + * a factory. + * <p> + * Note that the SocketOptions interface is protected. It contains the + * declaration of the methods getOption and setOption (***???***) that are + * used by Socket() for setting various options on the socket. This is + * of interest only to implementors. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class SocketImpl implements SocketOptions +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * The address of the remote end of the socket connection + */ +protected InetAddress address; + +/** + * The port number of the remote end of the socket connection + */ +protected int port; + +/** + * The port number the socket is bound to locally + */ +protected int localport; + +/** + * A FileDescriptor object representing this socket connection. + * ***** How do I create one of these? ******** + */ +protected FileDescriptor fd; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * A do nothing default public construtor + */ +public +SocketImpl() +{ + ; +} + +/*************************************************************************/ + +/** + * Accepts a connection on this socket. + * + * @param impl The implementation object for the accepted connection. + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +accept(SocketImpl impl) throws IOException; + +/*************************************************************************/ + +/** + * Returns the number of bytes that the caller can read from this socket + * without blocking. + * + * @return The number of readable bytes before blocking + * + * @exception IOException If an error occurs + */ +protected abstract synchronized int +available() throws IOException; + +/*************************************************************************/ + +/** + * Binds to the specified port on the specified addr. Note that this addr + * must represent a local IP address. **** How bind to INADDR_ANY? **** + * + * @param addr The address to bind to + * @param port The port number to bind to + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +bind(InetAddress addr, int port) throws IOException; + +/*************************************************************************/ + +/** + * Closes the socket. This will cause any InputStream or OutputStream + * objects for this Socket to be closed as well ******* Will it? ********* + * <p> + * Note that if the SO_LINGER option is set on this socket, then the + * operation could block. + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +close() throws IOException; + +/*************************************************************************/ + +/** + * Connects to the remote address and port specified as arguments. + * + * @param addr The remote address to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +connect(InetAddress addr, int port) throws IOException; + +/*************************************************************************/ + +/** + * Connects to the remote hostname and port specified as arguments. + * + * @param hostname The remote hostname to connect to + * @param port The remote port to connect to + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +connect(String hostname, int port) throws IOException; + +/*************************************************************************/ + +/** + * Creates a new socket that is not bound to any local address/port and + * is not connected to any remote address/port. This will be created as + * a stream socket if the stream parameter is true, or a datagram socket + * if the stream parameter is false. + * + * @param stream true for a stream socket, false for a datagram socket + */ +protected abstract void +create(boolean stream) throws IOException; + +/*************************************************************************/ + +/** + * Returns the FileDescriptor objects for this socket. + * + * @return A FileDescriptor for this socket. + */ +protected FileDescriptor +getFileDescriptor() +{ + return(fd); +} + +/*************************************************************************/ + +/** + * Returns the local port this socket is bound to + * + * @return The local port + */ +protected int +getLocalPort() +{ + return(localport); +} + +/*************************************************************************/ + +/** + * Returns the remote address this socket is connected to + * + * @return The remote address + */ +protected InetAddress +getInetAddress() +{ + return(address); +} + +/*************************************************************************/ + +/** + * Returns the remote port this socket is connected to + * + * @return The remote port + */ +protected int +getPort() +{ + return(port); +} + +/*************************************************************************/ + +/** + * Returns an InputStream object for reading from this socket + * + * @return An InputStream + * + * @exception IOException If an error occurs + */ +protected abstract synchronized InputStream +getInputStream() throws IOException; + +/*************************************************************************/ + +/** + * Returns an OutputStream object for writing to this socket + * + * @return An OutputStream + * + * @exception IOException If an error occurs + */ +protected abstract synchronized OutputStream +getOutputStream() throws IOException; + +/*************************************************************************/ + +/** + * Starts listening for connections on a socket. The queuelen parameter + * is how many pending connections will queue up waiting to be serviced + * before being accept'ed. If the queue of pending requests exceeds this + * number, additional connections will be refused. + * + * @param queuelen The length of the pending connection queue + * + * @exception IOException If an error occurs + */ +protected abstract synchronized void +listen(int queuelen) throws IOException; + +/*************************************************************************/ + +/** + * Returns a String representing the remote host and port of this + * socket. + */ +public String +toString() +{ + StringBuffer sb = new StringBuffer(""); + + if (address == null) + sb.append("<null>:"); + else + sb.append(address.getHostAddress() + ":"); + + sb.append(port); + + return(sb.toString()); +} + +} // class SocketImpl + diff --git a/java/net/SocketImplFactory.java b/java/net/SocketImplFactory.java new file mode 100644 index 000000000..81d575fb2 --- /dev/null +++ b/java/net/SocketImplFactory.java @@ -0,0 +1,40 @@ +/************************************************************************* +/* SocketImplFactory.java -- Interface to create a SocketImpl object +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This interface defines one method which returns a SocketImpl object. + * This should not be needed by ordinary applications. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface SocketImplFactory +{ +/** + * This method returns an instance of the SocketImpl object + * + * @return A SocketImpl object + */ +public abstract SocketImpl +createSocketImpl(); + +} // interface SocketImplFactory diff --git a/java/net/SocketInputStream.java b/java/net/SocketInputStream.java new file mode 100644 index 000000000..2d16be06f --- /dev/null +++ b/java/net/SocketInputStream.java @@ -0,0 +1,189 @@ +/************************************************************************* +/* SocketInputStream.java -- An InputStream for Sockets +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.InputStream; +import java.io.IOException; + +/** + * This class contains an implementation of InputStream for sockets. It + * in an internal only class used by PlainSocketImpl. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class SocketInputStream extends InputStream +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The PlainSocketImpl object this stream is associated with + */ +protected PlainSocketImpl impl; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Builds an instance of this class from a PlainSocketImpl object + */ +protected +SocketInputStream(PlainSocketImpl impl) +{ + this.impl = impl; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the number of bytes available to be read before blocking + */ +public int +available() throws IOException +{ + return(impl.available()); +} + +/*************************************************************************/ + +/** + * Determines if "mark" functionality is supported on this stream. For + * sockets, this is always false. Note that the superclass default is + * false, but it is overridden out of safety concerns and/or paranoia. + */ +public boolean +markSupported() +{ + return(false); +} + +/*************************************************************************/ + +/** + * Do nothing mark method since we don't support this functionality. Again, + * overriding out of paranoia. + * + * @param readlimit In theory, the number of bytes we can read before the mark becomes invalid + */ +public void +mark(int readlimit) +{ + ; +} + +/*************************************************************************/ + +/** + * Since we don't support mark, this method always throws an exception + * + * @exception IOException Everytime since we don't support this functionality + */ +public void +reset() throws IOException +{ + throw new IOException("Socket InputStreams do not support mark/reset"); +} + +/*************************************************************************/ + +/** + * This method not only closes the stream, it closes the underlying socket + * (and thus any connection) and invalidates any other Input/Output streams + * for the underlying impl object + */ +public void +close() throws IOException +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * Reads the next byte of data and returns it as an int. + * + * @return The byte read (as an int) or -1 if end of stream); + * + * @exception IOException If an error occurs. + */ +public int +read() throws IOException +{ + byte buf[] = new byte[1]; + + int bytes_read = read(buf, 0, buf.length); + + if (bytes_read != -1) + return((int)buf[0]); + else + return(-1); +} + +/*************************************************************************/ + +/** + * Reads up to buf.length bytes of data into the caller supplied buffer. + * + * @return The actual number of bytes read or -1 if end of stream + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf) throws IOException +{ + int bytes = read(buf, 0, buf.length); + + if (bytes == 0) + bytes = -1; + + return(bytes); +} + +/*************************************************************************/ + +/** + * Reads up to len bytes of data into the caller supplied buffer starting + * at offset bytes from the start of the buffer + * + * @return The number of bytes actually read or -1 if end of stream + * + * @exception IOException If an error occurs. + */ +public int +read(byte[] buf, int offset, int len) throws IOException +{ + return(impl.read(buf, offset, len)); +} + +} // class SocketInputStream + diff --git a/java/net/SocketOptions.java b/java/net/SocketOptions.java new file mode 100644 index 000000000..a4d701217 --- /dev/null +++ b/java/net/SocketOptions.java @@ -0,0 +1,108 @@ +/************************************************************************* +/* SocketOptions.java -- Implements options for sockets (duh!) +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This internal interface is used by SocketImpl to implement options + * on sockets. At least I think. The javadocs for SocketImpl show it + * implementing this interface and the Networking Enhancements description + * for Java 1.1 show two methods that aren't in the public javadocs, so + * I'll assume this is where they live. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +interface SocketOptions +{ + +/*************************************************************************/ + +/* + * Static Variables + */ + +// Note that these contant values were determined by experimentation and +// there is no way to currently know if the symbolic names are the +// same as in the JDK. + +/** + * Option id for the SO_LINGER value + */ +static final int SO_LINGER = 128; + +/** + * Option id for the SO_TIMEOUT value + */ +static final int SO_TIMEOUT = 4102; + +/** + * Option id for the TCP_NODELAY value + */ +static final int TCP_NODELAY = 1; + +/** + * Option id for the IP_TTL (time to live) value. + */ +static final int IP_TTL = 7777; + +/** + * Options id for the IP_MULTICAST_IF value + */ +static final int IP_MULTICAST_IF = 7778; + +/*************************************************************************/ + +/* + * Interface Methods + */ + +/** + * Sets the specified option on a socket to the passed in object. For + * options that take an integer argument, the passed in object is an + * Integer. The option_id parameter is one of the defined constants in + * this interface. + * + * @param option_id The identifier of the option + * @param val The value to set the option to + * + * @exception SocketException If an error occurs + */ +abstract void +setOption(int option_id, Object val) throws SocketException; + +/*************************************************************************/ + +/** + * Returns the current setting of the specified option. The Object returned + * will be an Integer for options that have integer values. The option_id + * is one of the defined constants in this interface. + * + * @param option_id The option identifier + * + * @return The current value of the option + * + * @exception SocketException If an error occurs + */ +abstract Object +getOption(int option_id) throws SocketException; + +} // interface SocketOptions + diff --git a/java/net/SocketOutputStream.java b/java/net/SocketOutputStream.java new file mode 100644 index 000000000..4b07d6d71 --- /dev/null +++ b/java/net/SocketOutputStream.java @@ -0,0 +1,148 @@ +/************************************************************************* +/* SocketOutputStream.java -- OutputStream for PlainSocketImpl +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * This class is used internally by PlainSocketImpl to be the OutputStream + * subclass returned by its getOutputStream method. It expects only to + * be used in that context. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +class SocketOutputStream extends OutputStream +{ + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The PlainSocketImpl object this stream is associated with + */ +protected PlainSocketImpl impl; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Build an instance of this class from a PlainSocketImpl object + */ +protected +SocketOutputStream(PlainSocketImpl impl) +{ + this.impl = impl; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This method closes the stream and the underlying socket connection. This + * action also effectively closes any other InputStream or OutputStream + * object associated with the connection. + * + * @exception IOException If an error occurs + */ +public void +close() throws IOException +{ + impl.close(); +} + +/*************************************************************************/ + +/** + * Hmmm, we don't seem to have a flush() method in Socket impl, so just + * return for now, but this might need to be looked at later. + * + * @exception IOException Can't happen + */ +public void +flush() throws IOException +{ + return; +} + +/*************************************************************************/ + +/** + * Writes a byte (passed in as an int) to the given output stream + * + * @param b The byte to write + * + * @exception IOException If an error occurs + */ +public void +write(int b) throws IOException +{ + byte buf[] = new byte[1]; + + Integer i = new Integer(b); + buf[0] = i.byteValue(); + + write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * Write an array of bytes to the output stream + * + * @param buf The array of bytes to write + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf) throws IOException +{ + write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * Writes len number of bytes from the array buf to the stream starting + * at offset bytes into the buffer. + * + * @param buf The buffer + * @param offset Offset into the buffer to start writing from + * @param len The number of bytes to write + */ +public void +write(byte[] buf, int offset, int len) throws IOException +{ + impl.write(buf, offset, len); +} + +} // class SocketOutputStream + diff --git a/java/net/SocketPermission.java b/java/net/SocketPermission.java new file mode 100644 index 000000000..068c56c11 --- /dev/null +++ b/java/net/SocketPermission.java @@ -0,0 +1,380 @@ +/************************************************************************* +/* SocketPermission.java -- Class modeling permissions for socket operations +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.security.Permission; +import java.security.PermissionCollection; + +/** + * This class models a specific set of permssions for connecting to a + * host. There are two elements to this, the host/port combination and + * the permission list. + * <p> + * The host/port combination is specified as followed + * <p> + * <pre> + * hostname[:[-]port[-[port]]] + * </pre> + * <p> + * The hostname portion can be either a hostname or IP address. If it is + * a hostname, a wildcard is allowed in hostnames. This wildcard is a "*" + * and matches one or more characters. Only one "*" may appear in the + * host and it must be the leftmost character. For example, + * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain. + * <p> + * The port portion can be either a single value, or a range of values + * treated as inclusive. The first or the last port value in the range + * can be omitted in which case either the minimum or maximum legal + * value for a port (respectively) is used by default. Here are some + * examples: + * <p><ul> + * <li>8080 - Represents port 8080 only + * <li>2000-3000 - Represents ports 2000 through 3000 inclusive + * <li>-4000 - Represents ports 0 through 4000 inclusive + * <li>1024- - Represents ports 1024 through 65535 inclusive + * </ul><p> + * The permission list is a comma separated list of individual permissions. + * These individual permissions are: + * <p> + * accept<br> + * connect<br> + * listen<br> + * resolve<br> + * <p> + * The "listen" permission is only relevant if the host is localhost. If + * any permission at all is specified, then resolve permission is implied to + * exist. + * <p> + * Here are a variety of examples of how to create SocketPermission's + * <p><pre> + * SocketPermission("www.urbanophile.com", "connect"); + * Can connect to any port on www.urbanophile.com + * SocketPermission("www.urbanophile.com:80", "connect,accept"); + * Can connect to or accept connections from www.urbanophile.com on port 80 + * SocketPermission("localhost:1024-", "listen,accept,connect"); + * Can connect to, accept from, an listen on any local port number 1024 and up. + * SocketPermission("*.edu", "connect"); + * Can connect to any host in the edu domain + * SocketPermission("197.197.20.1", "accept"); + * Can accept connections from 197.197.20.1 + * </pre><p> + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SocketPermission extends Permission +{ + +/*************************************************************************/ + +/** + * Instance Variables + */ + +/** + * A hostname/port combination as described above + */ +protected String hostport; + +/** + * A comma separated list of actions for which we have permission + */ +protected String perms; + +/*************************************************************************/ + +/** + * Creates a SocketPermission with the specified host/port combination + * and permissions string. + * + * @param hostport The hostname/port number combination + * @param perms The permissions string + */ +public +SocketPermission(String hostport, String perms) +{ + super(hostport); + + this.hostport = hostport; + this.perms = perms; +} + +/*************************************************************************/ + +/** + * Tests this object for equality against another. This will be true if + * and only if the passed object is an instance of SocketPermission and + * both its hostname/port combination and permissions string are + * identical. Overrides Permission.equals() + * + * @param obj The object to test against for equality + * + * @return true if object is equal to this object, false otherwise + */ +public boolean +equals(Object obj) +{ + if (!(obj instanceof SocketPermission)) + return(false); + + if (((SocketPermission)obj).hostport.equals(hostport)) + if (((SocketPermission)obj).perms.equals(perms)) + return(true); + + return(false); +} + +/*************************************************************************/ + +/** + * Returns a hash code value for this object. Overrides the + * Permission.hashCode() + * + * @return A hash code + */ +public int +hashCode() +{ + int hash = 100; + + //*** Get a real hash function + for (int i = 0; i < hostport.length(); i++) + hash = hash + (int)hostport.charAt(i) * 7; + + return(hash); +} + +/*************************************************************************/ + +/** + * Returns the list of permission actions in this object in canonical + * order. The canonical order is "connect,listen,accept,resolve" + * + * @return The permitted action string + */ +public String +getActions() +{ + boolean found = false; + StringBuffer sb = new StringBuffer(""); + + if (perms.indexOf("connect") != -1) + { + sb.append("connect"); + found = true; + } + + if (perms.indexOf("listen") != -1) + if (found) + sb.append(",listen"); + else + { + sb.append("listen"); + found = true; + } + + if (perms.indexOf("accept") != -1) + if (found) + sb.append(",accept"); + else + { + sb.append("accept"); + found = true; + } + + if (found) + sb.append(",resolve"); + else if (perms.indexOf("resolve") != -1) + sb.append("resolve"); + + return(sb.toString()); +} + +/*************************************************************************/ + +/** + * Returns a new PermissionCollection object that can hold + * SocketPermission's. Overrides Permission.newPermissionCollection + * + * @return A new PermissionCollection + */ +public PermissionCollection +newPermissionCollection() +{ + //***Implement + return(null); +} + +/*************************************************************************/ + +/** + * Returns true if the permission object passed it is implied by the + * this permission. This will be true if + * <p><ul> + * <li>The argument is of type SocketPermission + * <li>The permission list of the argument are in this object's permissions + * <li>The port range of the argument is within this objects port range + * <li>The hostname is equal to or a subset of this objects hostname + * </ul> + * <p> + * The argument's hostname will be a subset of this object's hostname if: + * <p><ul> + * <li>The argument's hostname or IP address is equal to this object's. + * <li>The argument's canonical hostname is equal to this object's. + * <li>The argument's canonical name matches this domains hostname with wildcards + * </ul> + * + * @param perm The Permission to check against + * + * @return True if the Permission is implied by this object, false otherwise + */ +public boolean +implies(Permission perm) +{ + SocketPermission p; + + // First make sure we are the right object type + if (perm instanceof SocketPermission) + p = (SocketPermission)perm; + else + return(false); + + // Next check the actions + String ourlist = getActions(); + String theirlist = p.getActions(); + + if (!ourlist.startsWith(theirlist)) + return(false); + + // Now check ports + int ourfirstport = 0, ourlastport = 0, theirfirstport = 0, theirlastport = 0; + + // Get ours + if (hostport.indexOf(":") == -1) + { + ourfirstport = 0; + ourlastport = 65535; + } + else + { + // This will dump if hostport if all sorts of bad data was passed to + // the constructor + String range = hostport.substring(hostport.indexOf(":")+1); + if (range.startsWith("-")) + ourfirstport = 0; + else if (range.indexOf("-") == -1) + ourfirstport = Integer.parseInt(range); + else + ourfirstport = Integer.parseInt(range.substring(0,range.indexOf("-"))); + + if (range.endsWith("-")) + ourlastport=65535; + else if (range.indexOf("-") == -1) + ourlastport = Integer.parseInt(range); + else + ourlastport = Integer.parseInt(range.substring(range.indexOf("-")+1, + range.length())); + } + + // Get theirs + if (p.hostport.indexOf(":") == -1) + { + theirfirstport = 0; + ourlastport = 65535; + } + else + { + // This will dump if hostport if all sorts of bad data was passed to + // the constructor + String range = p.hostport.substring(hostport.indexOf(":")+1); + if (range.startsWith("-")) + theirfirstport = 0; + else if (range.indexOf("-") == -1) + theirfirstport = Integer.parseInt(range); + else + theirfirstport = Integer.parseInt(range.substring(0,range.indexOf("-"))); + + if (range.endsWith("-")) + theirlastport=65535; + else if (range.indexOf("-") == -1) + theirlastport = Integer.parseInt(range); + else + theirlastport = Integer.parseInt(range.substring(range.indexOf("-")+1, + range.length())); + } + + // Now check them + if ((theirfirstport < ourfirstport) || (theirlastport > ourlastport)) + return(false); + + // Finally we can check the hosts + String ourhost, theirhost; + + // Get ours + if (hostport.indexOf(":") == -1) + ourhost = hostport; + else + ourhost = hostport.substring(0, hostport.indexOf(":")); + + // Get theirs + if (p.hostport.indexOf(":") == -1) + theirhost = p.hostport; + else + theirhost = p.hostport.substring(0, p.hostport.indexOf(":")); + + // Are they equal? + if (ourhost.equals(theirhost)) + return(true); + + // Try the canonical names + String ourcanonical = null, theircanonical = null; + try + { + ourcanonical = InetAddress.getByName(ourhost).getHostName(); + theircanonical = InetAddress.getByName(theirhost).getHostName(); + } + catch (UnknownHostException e) + { + // Who didn't resolve? Just assume current address is canonical enough + // Is this ok to do? + if (ourcanonical == null) + ourcanonical = ourhost; + if (theircanonical == null) + theircanonical = theirhost; + } + + if (ourcanonical.equals(theircanonical)) + return(true); + + // Well, last chance. Try for a wildcard + if (ourhost.indexOf("*.") != -1) + { + String wild_domain = ourhost.substring(ourhost.indexOf("*" + 1)); + if (theircanonical.endsWith(wild_domain)) + return(true); + } + + // Didn't make it + return(false); +} + +} // class SocketPermission + diff --git a/java/net/TODO b/java/net/TODO new file mode 100644 index 000000000..08215b6a6 --- /dev/null +++ b/java/net/TODO @@ -0,0 +1,26 @@ +-- DNS cache purging. + +-- Implement ContentHandler chaining (non-JDK feature) + +-- Implement MIME type by file determination chaining using external + disk files. (non-JDK feature) + +-- Implement determining MIME type from an InputStream + +-- Datagram peek()'s + +-- Finalize methods for sockets + +-- HTTP - caching (supported by JDK?) + +-- HTTP - all protocol support beyond basic GET functionality + +-- Fix call to Date(String) in URLConnection.getHeaderFieldDate() when + I figure out why DateFormat isn't working. + +-- Implement URLConnection.getPermission() + +-- Finish off all JDK 1.2 permissions stuff + +-- Write URLClassLoader + diff --git a/java/net/URL.java b/java/net/URL.java new file mode 100644 index 000000000..79bae9824 --- /dev/null +++ b/java/net/URL.java @@ -0,0 +1,690 @@ +/************************************************************************* +/* URL.java -- Uniform Resource Locator Class +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.InputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.StringTokenizer; +import java.util.Hashtable; + +/** + * This final class represents an Internet Uniform Resource Locator (URL). + * For details on the syntax of URL's and what they can be used for, + * refer to RFC 1738, available from <a + * href="http://ds.internic.net/rfcs/rfc1738.txt">http://ds.internic.net/rfcs/rfc1738.txt</a> + * <p> + * There are a great many protocols supported by URL's such as "http", + * "ftp", and "file". This object can handle any arbitrary URL for which + * a URLStreamHandler object can be written. Default protocol handlers + * are provided for the "http" and "ftp" protocols. Additional protocols + * handler implementations may be provided in the future. In any case, + * an application or applet can install its own protocol handlers that + * can be "chained" with other protocol hanlders in the system to extend + * the base functionality provided with this class. (Note, however, that + * unsigned applets cannot access properties by default or install their + * own protocol handlers). + * <p> + * This chaining is done via the system property java.protocol.handler.pkgs + * If this property is set, it is assumed to be a "|" separated list of + * package names in which to attempt locating protocol handlers. The + * protocol handler is searched for by appending the string + * ".<protocol>.Handler" to each packed in the list until a hander is found. + * If a protocol handler is not found in this list of packages, or if the + * property does not exist, then the default protocol handler of + * "gnu.java.net.<protocol>.Handler" is tried. If this is + * unsuccessful, a MalformedURLException is thrown. + * <p> + * All of the constructor methods of URL attempt to load a protocol + * handler and so any needed protocol handlers must be installed when + * the URL is constructed. + * <p> + * Here is an example of how URL searches for protocol handlers. Assume + * the value of java.protocol.handler.pkgs is "com.foo|com.bar" and the + * URL is "news://comp.lang.java.programmer". URL would looking the + * following places for protocol handlers: + * <p><pre> + * com.foo.news.Handler + * com.bar.news.Handler + * gnu.java.net.news.Handler + * </pre><p> + * If the protocol handler is not found in any of those locations, a + * MalformedURLException would be thrown. + * <p> + * Please note that a protocol handler must be a subclass of + * URLStreamHandler. + * <p> + * Normally, this class caches protocol handlers. Once it finds a handler + * for a particular protocol, it never tries to look up a new handler + * again. However, if the system property + * gnu.java.net.nocache_protocol_handlers is set, then this + * caching behavior is disabled. This property is specific to this + * implementation. Sun's JDK may or may not do protocol caching, but it + * almost certainly does not examine this property. + * <p> + * Please also note that an application can install its own factory for + * loading protocol handlers (see setURLStreamHandlerFactory). If this is + * done, then the above information is superseded and the behavior of this + * class in loading protocol handlers is dependent on that factory. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see URLStreamHandler + */ +public final class URL implements Serializable //, Comparable +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * If an application installs in own protocol handler factory, this is + * where we keep track of it. + */ +protected static URLStreamHandlerFactory factory; + +/** + * This a table where we cache protocol handlers to avoid the overhead + * of looking them up each time. + */ +protected static Hashtable ph_cache = new Hashtable(); + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * The name of the protocol for this URL + */ +protected String protocol; + +/** + * The hostname or IP address of this protocol + */ +protected String host; + +/** + * The port number of this protocol or -1 if the port number used is + * the default for this protocol. + */ +protected int port = -1; + +/** + * The "file" portion of the URL + */ +protected String file; + +/** + * The anchor portion of the URL + */ +protected String anchor; + +/** + * The protocol handler in use for this URL + */ +protected URLStreamHandler ph; + +/*************************************************************************/ + +/* + * Static Methods + */ + +/** + * Sets the URLStreamHandlerFactory for this class. This factory is + * responsible for returning the appropriate protocol handler for + * a given URL. + * + * @param fac The URLStreamHandlerFactory class to use + * + * @exception Error If the factory is alread set. + */ +public static synchronized void +setURLStreamHandlerFactory(URLStreamHandlerFactory fac) +{ + if (factory == null) + factory = fac; + else + throw new Error("URLStreamHandlerFactory alread set"); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Constructs a URL and loads a protocol handler for the values passed as + * arguments. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address to connect to + * @param port The port number to use, or -1 to use the protocol's default port + * @param file The "file" portion of the URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded + */ +public +URL(String protocol, String host, int port, String file) + throws MalformedURLException +{ + this.protocol = protocol.toLowerCase(); + this.host = host; + this.port = port; + this.file = file; + + ph = getProtocolHandler(protocol); +} + +/*************************************************************************/ + +/** + * Constructs a URL and loads a protocol handler for the values passed in + * as arugments. Uses the default port for the protocol. + * + * @param protocol The protocol for this URL ("http", "ftp", etc) + * @param host The hostname or IP address for this URL + * @param file The "file" portion of this URL. + * + * @exception MalformedURLException If a protocol handler cannot be loaded + */ +public +URL(String protocol, String host, String file) throws MalformedURLException +{ + this(protocol, host, -1, file); +} + +/*************************************************************************/ + +/** + * This method parses a String representation of a URL within the + * context of an existing URL. Principally this means that any fields + * not present the URL are inheritied from the context URL. This allows + * relative URL's to be easily constructed (***true?***). If the + * context argument is null, then a complete URL must be specified in the + * URL string. If the protocol parsed out of the URL is different + * from the context URL's protocol, then then URL String is also + * expected to be a complete URL. + * + * @param context The context URL + * @param url A String representing this URL + * + * @exception MalformedURLException If a protocol handler cannot be found or the URL cannot be parsed + */ +public +URL(URL context, String url) throws MalformedURLException +{ + int end, start = -1; + + int colon_index = url.indexOf(":"); + int slash_index = url.indexOf("/"); + + // Find a protocol name in the string if there is one + if (colon_index != -1) + if ((slash_index == -1) ||((slash_index != -1) && + (colon_index < slash_index))) + { + protocol = url.substring(0, colon_index); + start = colon_index + 1; // Used for parsing later + } + + // Handle defaulting of protocol from context. If no protocol and no + // context, then no URL. + if (protocol == null) + if (context == null) + throw new MalformedURLException(url); + else + protocol = context.getProtocol(); + + protocol = protocol.toLowerCase(); + + // Default in items as necessary + if (context != null) + if (context.getProtocol().equals(protocol)) + { + host = context.getHost(); + port = context.getPort(); + file = context.getFile(); + } + + // Get the protocol handler and parse the rest of the URL string. + ph = getProtocolHandler(protocol); + + if (start == -1) + start = 0; + + // We are supposed to stop parsing at the "#" char and treat everything + // after that as an anchor. + end = url.indexOf("#"); + if (end == -1) + end = url.length(); + if (end != (url.length())) + anchor = url.substring(end + 1); + + ph.parseURL(this, url, start, end); +} + +/*************************************************************************/ + +/** + * Initializes a URL from a complete string specification such as + * "http://www.urbanophile.com/arenn/". First the protocol name is parsed + * out of the string. Then a handler is located for that protocol and + * the parseURL() method of that protocol handler is used to parse the + * remaining fields. + * + * @param url The complete String representation of a URL + * + * @exception MalformedURLException If a protocol handler cannot be found or the URL cannot be parsed + */ +public +URL(String url) throws MalformedURLException +{ + this(null, url); +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * This internal method is used in two different constructors to load + * a protocol handler for this URL. + * + * @param The protocol to load a handler for + * + * @return A URLStreamHandler for this protocol + * + * @exception MalformedURLException If the protocol can't be loaded. + */ +private URLStreamHandler +getProtocolHandler(String protocol) throws MalformedURLException +{ + URLStreamHandler ph; + + // First check the factory and use that if set + if (factory != null) + { + ph = factory.createURLStreamHandler(protocol); + if (ph == null) + throw new MalformedURLException(protocol); + } + + // Next check the cache unless the user has disabled caches + String nocache = null; + try + { + nocache = System.getProperty("gnu.java.net.nocache_protocol_handlers"); + } + catch (SecurityException e) { ; } + + if (nocache == null) + { + Class cls = (Class)ph_cache.get(protocol); + if (cls != null) + { + try + { + ph = (URLStreamHandler)cls.newInstance(); + return(ph); + } + catch (InstantiationException e) { ; } + catch (IllegalAccessException e) { ; } + } + } + + // Next, get the protocol handler package list property + String pkglist = null; + try + { + pkglist = System.getProperty("java.protocol.handler.pkgs"); + } + catch (SecurityException e) { ; } + + // Tack our default package on at the ends + if (pkglist != null) + pkglist = pkglist + "|" + "gnu.java.net"; + else + pkglist = "gnu.java.net"; + + // Now loop through looking for a match + if (pkglist != null) + { + StringTokenizer st = new StringTokenizer(pkglist, "|"); + while (st.hasMoreTokens()) + { + String clsname = st.nextToken() + "." + protocol + ".Handler"; + + try + { + Class cls = Class.forName(clsname); + Object obj = cls.newInstance(); + if (!(obj instanceof URLStreamHandler)) + continue; + else + ph = (URLStreamHandler)obj; + + if (nocache != null) + ph_cache.put(protocol, cls); + + return(ph); + } + catch (ClassNotFoundException e) { ; } + catch (InstantiationException e) { ; } + catch (IllegalAccessException e) { ; } + } + } + + // Still here, which is bad new + throw new MalformedURLException(protocol); +} + +/*************************************************************************/ + +/** + * This protected method is used by protocol handlers to set the values + * of the fields in this URL. This might be done in the parseURL method + * of that class. + * + * @param protocol The protocol name for this URL + * @param host The hostname or IP address for this URL + * @param port The port number of this URL + * @param file The "file" portion of this URL. + * @param anchor The anchor portion of this URL. + */ +protected synchronized void +set(String protocol, String host, int port, String file, String anchor) +{ + //*** Should we ignore null'd fields? Assume not for now. + + this.protocol = protocol; + this.host = host; + this.port = port; + this.file = file; + this.anchor = anchor; +} + +/*************************************************************************/ + +/** + * Returns the protocol name of this URL + * + * @return The protocol + */ +public String +getProtocol() +{ + return(protocol); +} + +/*************************************************************************/ + +/** + * Returns the hostname or IP address for this protocol + * + * @return The hostname + */ +public String +getHost() +{ + return(host); +} + +/*************************************************************************/ + +/** + * Returns the port number of this URL or -1 if the default port number is + * being used + * + * @return The port number + */ +public int +getPort() +{ + return(port); +} + +/*************************************************************************/ + +/** + * Returns the "file" portion of this URL + * + * @return The file portion + */ +public String +getFile() +{ + return(file); +} + +/*************************************************************************/ + +/** + * Returns the anchor (sometimes called the "reference") portion of the + * URL + * + * @return The anchor + */ +public String +getRef() +{ + return(anchor); +} + +/*************************************************************************/ + +/** + * Test another URL for equality with this one. This will be true only if + * the argument is non-null and all of the fields in the URL's match + * exactly (ie, protocol, host, port, file, and anchor). Overrides + * Object.equals(). + * + * @param url The URL to compare with + * + * @return true if the URL is equal, false otherwise + */ +public boolean +equals(Object url) +{ + // Is it null? + if (url == null) + return(false); + + // Is it a URL? + if (!(url instanceof URL)) + return(false); + + URL u = (URL)url; + + // Check everything but the anchor + if (!sameFile(u)) + return(false); + + // Do the anchor's match + String s = u.getRef(); + if (s != null) + if (!s.equals(getRef())) + return(false); + else if (getRef() != null) + return(false); + + // Still here so everything must be ok + return(true); +} + +/*************************************************************************/ + +/** + * Tests whether or not another URL refers to the same "file" as this one. + * This will be true if and only if the passed object is not null, is a + * URL, and matches all fields but the anchor (ie, protocol, host, port, + * and file); + * + * @param url The URL object to test with + * + * @return true if URL matches this URL's file, false otherwise + */ +public boolean +sameFile(URL url) +{ + if (url == null) + return(false); + + // Do the protocol's match? + String s = url.getProtocol(); + if (s != null) + if (!s.equals(getProtocol())) + return(false); + else if (getProtocol() != null) + return(false); + + // Do the hostname's match? + s = url.getHost(); + if (s != null) + if (!s.equals(getHost())) + return(false); + else if (getHost() != null) + return(false); + + // Do the port's match? + if (url.getPort() != getPort()) + return(false); + + // Do the file's match? + s = url.getFile(); + if (s != null) + if (!s.equals(getFile())) + return(false); + else if (getFile() != null) + return(false); + + // We're still here, so everything must be ok! + return(true); +} + +/*************************************************************************/ + +/** + * This is the implementation of the Comparable interface for URL's. It + * will return a negative int, 0, or a positive int depending on whether + * a URL is less than, equal to, or greater than this URL respectively. + * This is done by returning the compareTo result on this string + * representations of these URL's. + * + * @param url The URL to compare against + * + * @return An int indicating whether a URL is less than, equal to, or greater than this URL + */ +public int +compareTo(Object url) +{ + return(toExternalForm().compareTo(((URL)url).toExternalForm())); +} + +/*************************************************************************/ + +/** + * Returns a String representing this URL. The String returned is + * created by calling the protocol handler's toExternalForm() method. + * + * @return A string for this URL + */ +public String +toExternalForm() +{ + return(ph.toExternalForm(this)); +} + +/*************************************************************************/ + +/** + * Returns a String representing this URL. Identical to toExternalForm(). + * The value returned is created by the protocol handler's + * toExternalForm method. Overrides Object.toString() + * + * @return A string for this URL + */ +public String +toString() +{ + return(toExternalForm()); +} + +/*************************************************************************/ + +/** + * Returns a URLConnection for this object created by calling the + * openConnection() method of the protocol handler + * + * @return A URLConnection for this URL + * + * @exception IOException If an error occurs + */ +public synchronized URLConnection +openConnection() throws IOException +{ + return(ph.openConnection(this)); +} + +/*************************************************************************/ + +/** + * This method returns an InputStream for this URL by first opening the + * connection, then calling the getInputStream() method against the + * connection. + * + * @return An InputStream for this URL + * + * @exception IOException If an error occurs + */ +public final synchronized InputStream +openStream() throws IOException +{ + return(openConnection().getInputStream()); +} + +/*************************************************************************/ + +/** + * Returns the contents of this URL as an object by first opening a + * connection, then calling the getContent() method against the connection + * + * @return A content object for this URL + * + * @exception IOException If an error occurs + */ +public final synchronized Object +getContent() throws IOException +{ + return(openConnection().getContent()); +} + +} // class URL + diff --git a/java/net/URLConnection.java b/java/net/URLConnection.java new file mode 100644 index 000000000..131f1830b --- /dev/null +++ b/java/net/URLConnection.java @@ -0,0 +1,912 @@ +/************************************************************************* +/* URLConnection.java -- Abstract superclass for reading from URL's +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +//import java.security.Permission; +import java.text.DateFormat; +import java.text.ParsePosition; +import java.text.ParseException; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Vector; + +/** + * This class models a connection that retrieves the information pointed + * to by a URL object. This is typically a connection to a remote node + * on the network, but could be a simple disk read. + * <p> + * A URLConnection object is normally created by calling the openConnection() + * method of a URL object. This method is somewhat misnamed because it does + * not actually open the connection. Instead, it return an unconnected + * instance of this object. The caller then has the opportunity to set + * various connection options prior to calling the actual connect() method. + * <p> + * After the connection has been opened, there are a number of methods in + * this class that access various attributes of the data, typically + * represented by headers sent in advance of the actual data itself. + * <p> + * Also of note are the getInputStream and getContent() methods which allow + * the caller to retrieve the actual data from the connection. Note that + * for some types of connections, writing is also allowed. The setDoOutput() + * method must be called prior to connecing in order to enable this, then + * the getOutputStream method called after the connection in order to + * obtain a stream to write the output to. + * <p> + * The getContent() method is of particular note. This method returns an + * Object that encapsulates the data returned. There is no way do determine + * the type of object that will be returned in advance. This is determined + * by the actual content handlers as described in the description of that + * method. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract class URLConnection +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * This is an object that maps filenames to MIME types. The interface + * to do this is implemented by this class, so just create an empty + * instance and store it here. + */ +public static FileNameMap fileNameMap = new MimeTypeMapper(); + +/** + * This is the ContentHandlerFactory set by the caller, if any + */ +private static ContentHandlerFactory factory; + +/** + * This is the default value that will be used to determine whether or + * not user interaction should be allowed. + */ +private static boolean def_allow_user_inter; + +/** + * This is the default flag indicating whether or not to use caches to + * store the data returned from a server + */ +private static boolean def_use_caches; + +/** + * This is a Hashable for setting default request properties + */ +private static Hashtable def_req_props = new Hashtable(); + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This variable determines whether or not interaction is allowed with + * the user. For example, to prompt for a username and password. + */ +protected boolean allowUserInteraction; + +/** + * Indicates whether or not a connection has been established to the + * destination specified in the URL + */ +protected boolean connected; + +/** + * Indicates whether or not input can be read from this URL + */ +protected boolean doInput; + +/** + * Indicates whether or not output can be sent to this URL + */ +protected boolean doOutput; + +/** + * Determines whether or not caches should be used for this URL. + * Setting this to true does not guarantee that caching will take place, + * only that caching will take place if possible + */ +protected boolean useCaches; + +/** + * If this value is non-zero, then the connection will only attempt to + * fetch the document pointed to by the URL if the document has been + * modified more recently than the date set in this variable. That date + * should be specified as the number of seconds since 1/1/1970 GMT. + */ +protected long ifModifiedSince; + +/** + * This is the URL associated with this connection + */ +protected URL url; + +/** + * This is the list of header tag or key fields. Use a Vector instead of + * a Hastable because we need to reference by index. Use the "standard" + * Java naming in the hope that it is compatible with Sun's implementation + * Use Vector for JDK 1.1.5 compat. + */ +protected Vector headerKeys = new Vector(10); + +/** + * This is the list of header value fields. Use a Vector instead of + * a Hastable because we need to reference by index. Use the "standard" + * Java naming in the hope that it is compatible with Sun's implementation + * Use Vector for JDK 1.1.5 compat. + */ +protected Vector headerValues = new Vector(10); + +/** + * The list of request properties for this connection + */ +private Hashtable req_props = new Hashtable(10); + +/*************************************************************************/ + +/* + * Class Methods + */ + +/** + * Set's the ContentHandlerFactory for an application. This can be called + * once and only once. If it is called again, then an Error is thrown. + * Unlike for other set factory methods, this one does not do a security + * check prior to setting the factory. + * + * @param factory The ContentHandlerFactory for this application + * + * @error Error If the factory is already set + */ +public static synchronized void +setContentHandlerFactory(ContentHandlerFactory fac) +{ + if (factory != null) + throw new Error("The ContentHandlerFactory is already set"); + + factory = fac; +} + +/*************************************************************************/ + +/** + * Returns the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overidden + * + * @return true if user interaction is allowed, false otherwise + */ +public static boolean +getDefaultAllowUserInteraction() +{ + return(def_allow_user_inter); +} + +/*************************************************************************/ + +/** + * Sets the default flag for whether or not interaction with a user + * is allowed. This will be used for all connections unless overridden + * + * @param allow true to allow user interaction, false otherwise + */ +public static synchronized void +setDefaultAllowUserInteraction(boolean allow) +{ + def_allow_user_inter = allow; +} + +/*************************************************************************/ + +/** + * Returns the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @return true if caches will be used, false otherwise + */ +public static boolean +getDefaultUseCaches() +{ + return(def_use_caches); +} + +/*************************************************************************/ + +/** + * Sets the default value used to determine whether or not caching + * of documents will be done when possible. + * + * @param use true to use caches if possible by default, false otherwise + */ +public static synchronized void +setDefaultUseCaches(boolean use) +{ + def_use_caches = use; +} + +/*************************************************************************/ + +/** + * Returns the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property to return the default value of + * + * @return The default request property + */ +public static String +getDefaultRequestProperty(String key) +{ + return((String)def_req_props.get(key.toLowerCase())); +} + +/*************************************************************************/ + +/** + * Sets the default value of a request property. This will be used + * for all connections unless the value of the property is manually + * overridden. + * + * @param key The request property name the default is being set for + * @param value The value to set the default to + */ +public static synchronized void +setDefaultRequestProperty(String key, String value) +{ + def_req_props.put(key.toLowerCase(), value); +} + +/*************************************************************************/ + +/** + * Returns the MIME type of a file based on the name of the file. This + * works by searching for the file's extension in a list of file extensions + * and returning the MIME type associated with it. If no type is found, + * then a MIME type of "application/octet-stream" will be returned. + * + * @param filename The filename to determine the MIME type for + * + * @return The MIME type String + */ +protected static String +guessContentTypeFromName(String filename) +{ + return(fileNameMap.getContentTypeFor(filename.toLowerCase())); +} + +/*************************************************************************/ + +/** + * Returns the MIME type of a stream based on the first few characters + * at the beginning of the stream. This routine can be used to determine + * the MIME type if a server is believed to be returning an incorrect + * MIME type. This method returns "application/octet-stream" if it + * cannot determine the MIME type. + * <p> + * NOTE: Overriding MIME types sent from the server can be obnoxious + * to user's. See Internet Exploder 4 if you don't believe me. + * + * @param is The InputStream to determine the MIME type from + * + * @return The MIME type + * + * @exception IOException If an error occurs + */ +public static String +guessContentTypeFromStream(InputStream is) throws IOException +{ + return("application/octet-stream"); +} + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This constructs a URLConnection from a URL object + * + * @param url The URL for this connection + */ +protected +URLConnection(URL url) +{ + // Set up all our instance variables + this.url = url; + allowUserInteraction = def_allow_user_inter; + useCaches = def_use_caches; + + Enumeration e = def_req_props.keys(); + while (e.hasMoreElements()) + { + String key = (String)e.nextElement(); + String value = (String)def_req_props.get(key); + + req_props.put(key, value); + } +} + +/*************************************************************************/ + +/** + * Returns a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @return true if user interaction is allowed, false otherwise + */ +public boolean +getAllowUserInteraction() +{ + return(allowUserInteraction); +} + +/*************************************************************************/ + +/** + * Sets a boolean flag indicating whether or not user interaction is + * allowed for this connection. (For example, in order to prompt for + * username and password info. + * + * @param allow true if user interaction should be allowed, false otherwise + */ +public void +setAllowUserInteraction(boolean allow) +{ + allowUserInteraction = allow; +} + +/*************************************************************************/ + +/** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @return true if input is to be done, false otherwise + */ +public boolean +getDoInput() +{ + return(doInput); +} + +/*************************************************************************/ + +/** + * Returns the value of a flag indicating whether or not input is going + * to be done for this connection. This default to true unless the + * doOutput flag is set to false, in which case this defaults to false. + * + * @param input true if input is to be done, false otherwise + */ +public void +setDoInput(boolean input) +{ + doInput = input; +} + +/*************************************************************************/ + +/** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. This defaults to false. + * + * @return true if output is to be done, false otherwise + */ +public boolean +getDoOutput() +{ + return(doOutput); +} + +/*************************************************************************/ + +/** + * Returns a boolean flag indicating whether or not output will be done + * on this connection. The default value is false, so this method can + * be used to override the default + * + * @param output ture if output is to be done, false otherwise + */ +public void +setDoOutput(boolean output) +{ + doOutput = output; +} + +/*************************************************************************/ + +/** + * Returns a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @return true if caching should be used if possible, false otherwise + */ +public boolean +getUseCaches() +{ + return(useCaches); +} + +/*************************************************************************/ + +/** + * Sets a boolean flag indicating whether or not caching will be used + * (if possible) to store data downloaded via the connection. + * + * @param use_cache true if caching should be used if possible, false otherwise + */ +public void +setUseCaches(boolean use_caches) +{ + useCaches = use_caches; +} + +/*************************************************************************/ + +/** + * Returns the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * returned will be 0 if this feature is disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise + * + * @return The ifModifiedSince value + */ +public long +getIfModifiedSince() +{ + return(ifModifiedSince); +} + +/*************************************************************************/ + +/** + * Sets the ifModified since instance variable. If this value is non + * zero and the underlying protocol supports it, the actual document will + * not be fetched unless it has been modified since this time. The value + * passed should be 0 if this feature is to be disabled or the time expressed + * as the number of seconds since midnight 1/1/1970 GMT otherwise. + * + * @param modified_since The new ifModifiedSince value + */ +public void +setIfModifiedSince(long modified_since) +{ + ifModifiedSince = modified_since; +} + +/*************************************************************************/ + +/** + * Returns the value of the named request property. + * + * @param key The name of the property + * + * @return The value of the property + */ +public String +getRequestProperty(String key) +{ + return((String)req_props.get(key.toLowerCase())); +} + +/*************************************************************************/ + +/** + * Sets the value of the named request property + * + * @param key The name of the property + * @param value The value of the property + */ +public synchronized void +setRequestProperty(String key, String value) +{ + req_props.put(key.toLowerCase(), value); +} + +/*************************************************************************/ + +/** + * Returns the URL object associated with this connection + * + * @return The URL for this connection. + */ +public URL +getURL() +{ + return(url); +} + +/*************************************************************************/ + +/** + * Establishes the actual connection to the URL associated with this + * connection object + */ +public abstract void +connect() throws IOException; + +/*************************************************************************/ + +/** + * Returns an InputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An InputStream for this connection + * + * @exception IOException If an error occurs + */ +public InputStream +getInputStream() throws IOException +{ + return(null); +} + +/*************************************************************************/ + +/** + * Returns an OutputStream for this connection. As this default + * implementation returns null, subclasses should override this method + * + * @return An OutputStream for this connection + * + * @exception IOException If an error occurs + */ +public OutputStream +getOutputStream() throws IOException +{ + return(null); +} + +/*************************************************************************/ + +/** + * Returns the value of the content-encoding field or null if it is not + * known or not present. + * + * @return The content-encoding field + */ +public String +getContentEncoding() +{ + return(getHeaderField("content-encoding")); +} + +/*************************************************************************/ + +/** + * Returns the value of the content-length header field or -1 if the value + * is not known or not present. + * + * @return The content-length field + */ +public int +getContentLength() +{ + return(getHeaderFieldInt("content-length", -1)); +} + +/*************************************************************************/ + +/** + * Returns the the content-type of the data pointed to by the URL. This + * method first tries looking for a content-type header. If that is not + * present, it attempts to use the file name to determine the content's + * MIME type. If that is unsuccessful, the method returns null. The caller + * may then still attempt to determine the MIME type by a call to + * guessContentTypeFromStream() + * + * @return The content MIME type + */ +public String +getContentType() +{ + String type = getHeaderField("content-type"); + if (type == null) + type = guessContentTypeFromName(getURL().getFile()); + + return(type); +} + +/*************************************************************************/ + +/** + * Returns the date of the document pointed to by the URL as reported in + * the date field of the header or 0 if the value is not present or not + * known. If populated, the return value is number of seconds since + * midnight on 1/1/1970 GMT. + * + * @return The document date + */ +public long +getDate() +{ + return(getHeaderFieldDate("date", 0)); +} + +/*************************************************************************/ + +/** + * Returns the value of the expires header or 0 if not known or present. + * If populated, the return value is number of seconds since midnight + * on 1/1/1970 GMT. + * + * @return The expiration time. + */ +public long +getExpiration() +{ + return(getHeaderFieldDate("expires", 0)); +} + +/*************************************************************************/ + +/** + * Returns the value of the last-modified header field or 0 if not known known + * or not present. If populated, the return value is the number of seconds + * since midnight on 1/1/1970. + * + * @return The last modified time + */ +public long +getLastModified() +{ + return(getHeaderFieldDate("last-modified", 0)); +} + +/*************************************************************************/ + +/** + * Returns a String representing the header key at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * getHeaderField(int) method allows access to the corresponding value for + * this tag. + * + * @param index The index into the header field list to retrieve the key for. + * + * @return The header field key or null if index is past the end of the headers + */ +public String +getHeaderFieldKey(int index) +{ + String key = null; + + try + { + key = (String)headerKeys.elementAt(index); + } + catch (ArrayIndexOutOfBoundsException e) { ; } + + return(key); +} + +/*************************************************************************/ + +/** + * Return a String representing the header value at the specified index. + * This allows the caller to walk the list of header fields. The analogous + * getHeaderFieldKey(int) method allows access to the corresponding key + * for this header field + * + * @param index The index into the header field list to retrieve the value for + * + * @return The header value or null if index is past the end of the headers + */ +public String +getHeaderField(int index) +{ + String value = null; + + try + { + value = (String)headerValues.elementAt(index); + } + catch (ArrayIndexOutOfBoundsException e) { ; } + + return(value); +} + +/*************************************************************************/ + +/** + * Returns a String representing the value of the header field having + * the named key. Returns null if the header field does not exist. + * + * @param The key of the header field + * + * @return The value of the header field as a String + */ +public String +getHeaderField(String name) +{ + for (int i = 0; ; i++) + { + String key = getHeaderFieldKey(i); + if (key == null) + return(null); + + if (key.equals(name.toLowerCase())) + return(getHeaderField(i)); + } +} + +/*************************************************************************/ + +/** + * Returns the value of the named header field as a date. This date will + * be the number of seconds since midnight 1/1/1970 GMT or the default + * value if the field is not present or cannot be converted to a date. + * + * @param key The header field key to lookup + * @param def The default value if the header field is not found or can't be converted + */ +public long +getHeaderFieldDate(String key, long def) +{ + String value = getHeaderField(key); + if (value == null) + return(def); + + // This needs to change since Date(String) is deprecated, but DateFormat + // doesn't seem to be working for some reason + //DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, Locale.US); + //df.setLenient(true); + + //Date d = df.parse(value, new ParsePosition(0)); + Date d = new Date(value); + + if (d == null) + return(def); + + return(d.getTime() / 1000); +} + +/*************************************************************************/ + +/** + * Returns the value of the named header field as an int. If the field + * is not present or cannot be parsed as an integer, the default value + * will be returned. + * + * @param key The header field key to lookup + * @param def The defaule value if the header field is not found or can't be parsed + */ +public int +getHeaderFieldInt(String key, int def) +{ + String value = getHeaderField(key); + if (value == null) + return(def); + + int retval = def; + try + { + retval = Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return(def); + } + + return(retval); +} + +/*************************************************************************/ + +/** + * What is this method supposed to do? It's not documented, so just + * throw an exception if called. + * + * @return A Permission object + * + * @exception IOException If an error occurs + */ +/* +public Permission +getPermission() throws IOException +{ + throw new IOException("I don't know what this method is supposed to do"); +} +*/ + +/*************************************************************************/ + +/** + * This method returns the content of the document pointed to by the URL + * as an Object. The type of object depends on the MIME type of the + * object and particular content hander loaded. Most text type content + * handlers will return a subclass of InputStream. Images usually return + * a class that implements ImageProducer. There is not guarantee what + * type of object will be returned, however. + * <p> + * This class first determines the MIME type of the content, then creates + * a ContentHandler object to process the input. If the ContentHandlerFactory + * is set, then that object is called to load a content handler, otherwise + * a class called gnu.java.net.content.<content_type> is tried. + * The default class will also be used if the content handler factory returns + * a null content handler. + * + * @exception IOException If an error occurs. + */ +public Object +getContent() throws IOException +{ +// connect(); + String type = getContentType(); + + // First try the factory + ContentHandler ch = null; + if (factory != null) + ch = factory.createContentHandler(type); + + if (ch != null) + return(ch.getContent(this)); + + // Then try our default class + try + { + Class cls = Class.forName("gnu.java.net.content." + + type.replace('/', '.')); + + Object obj = cls.newInstance(); + if (!(obj instanceof ContentHandler)) + throw new UnknownServiceException(type); + + ch = (ContentHandler)obj; + return(ch.getContent(this)); + } + catch (ClassNotFoundException e) { ; } + catch (InstantiationException e) { ; } + catch (IllegalAccessException e) { ; } + + throw new UnknownServiceException(type); +} + +/*************************************************************************/ + +/** + * The methods prints the value of this object as a String by calling the + * toString() method of its associated URL. Overrides Object.toString() + * + * @return A String representation of this object + */ +public String +toString() +{ + return(url.toString()); +} + +} // class URLConnection + diff --git a/java/net/URLEncoder.java b/java/net/URLEncoder.java new file mode 100644 index 000000000..31ca6a138 --- /dev/null +++ b/java/net/URLEncoder.java @@ -0,0 +1,110 @@ +/************************************************************************* +/* URLEncoder.java -- Class to convert strings to a properly encoded URL +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This utility class contains one static method that converts a + * string into a fully encoded URL string in x-www-form-urlencoded + * format. This format replaces certain disallowed characters with + * encoded equivalents. All upper case and lower case letters in the + * US alphabet remain as is, the space character (' ') is replaced with + * '+' sign, and all other characters are converted to a "%XX" format + * where XX is the hexadecimal representation of that character. Note + * that since unicode characters are 16 bits, and this metho encodes only + * 8 bits of information, the lower 8 bits of the character are used. + * <p> + * This method is very useful for encoding strings to be sent to CGI scripts + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class URLEncoder +{ + +/*************************************************************************/ + +/* + * Class Variables + */ + +/** + * A lookup table array of hexadecimal character equivalents + */ +protected static final char[] hexchars = { '0', '1', '2', '3', '4', '5', '6', + '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F' }; + +/*************************************************************************/ + +/* + * Class Methods + */ + +/** + * This method translates the passed in string into x-www-form-urlencoded + * format and returns it. + * + * @param source The String to convert + * + * @return The converted String + */ +public static String +encode(String source) +{ + StringBuffer result = new StringBuffer(""); + + for (int i = 0; i < source.length(); i++) + { + // Get the low 8 bits of the next char + char c = source.charAt(i); + c &= 0xFF; + + // Handle regular characters + if ((c >= 'A') && (c <= 'Z')) + { + result.append(c); + continue; + } + + if ((c >= 'a') && (c <= 'z')) + { + result.append(c); + continue; + } + + // Handle spaces + if (c == ' ') + { + result.append('+'); + continue; + } + + // Handle everything else + result.append('%'); + result.append(hexchars[ c / 16 ]); + result.append(hexchars[ c % 16 ]); + } + + return(result.toString()); +} + +} // class URLEncoder + diff --git a/java/net/URLStreamHandler.java b/java/net/URLStreamHandler.java new file mode 100644 index 000000000..af55513cb --- /dev/null +++ b/java/net/URLStreamHandler.java @@ -0,0 +1,251 @@ +/************************************************************************* +/* URLStreamHandler.java -- Abstract superclass for all protocol handlers +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +import java.io.IOException; + +/** + * This class is the superclass of all URL protocol handlers. The URL + * class loads the appropriate protocol handler to establish a connection + * to a (possibly) remote service (eg, "http", "ftp") and to do protocol + * specific parsing of URL's. Refer to the URL class documentation for + * details on how that class locates and loads protocol handlers. + * <p> + * A protocol handler implementation should override the openConnection() + * method, and optionally override the parseURL() and toExternalForm() + * methods if necessary. (The default implementations will parse/write all + * URL's in the same form as http URL's). A protocol specific subclass + * of URLConnection will most likely need to be created as well. + * <p> + * Note that the instance methods in this class are called as if they + * were static methods. That is, a URL object to act on is passed with + * every call rather than the caller assuming the URL is stored in an + * instance variable of the "this" object. + * <p> + * The methods in this class are protected and accessible only to subclasses. + * URLStreamConnection objects are intended for use by the URL class only, + * not by other classes (unless those classes are implementing protocols). + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + * + * @see URL + */ +public abstract class URLStreamHandler +{ + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Do nothing constructor for subclass + */ +public +URLStreamHandler() +{ + ; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns a URLConnection for the passed in URL. Note that this should + * not actually create the connection to the (possibly) remote host, but + * rather simply return a URLConnection object. The connect() method of + * URL connection is used to establish the actual connection, possibly + * after the caller sets up various connection options. + * + * @param url The URL to get a connection object for + * + * @return A URLConnection object for the given URL + * + * @exception IOException If an error occurs + */ +protected abstract synchronized URLConnection +openConnection(URL url) throws IOException; + +/*************************************************************************/ + +/** + * This method parses the string passed in as a URL and set's the + * instance data fields in the URL object passed in to the various values + * parsed out of the string. The start parameter is the position to start + * scanning the string. This is usually the position after the ":" which + * terminates the protocol name. The end parameter is the position to + * stop scanning. This will be either the end of the String, or the + * position of the "#" character, which separates the "file" portion of + * the URL from the "anchor" portion. + * <p> + * This method assumes URL's are formatted like http protocol URL's, so + * subclasses that implement protocols with URL's the follow a different + * syntax should override this method. The lone exception is that if + * the protocol name set in the URL is "file", this method will accept + * a an empty hostname (i.e., "file:///"), which is legal for that protocol + * + * @param url The URL object in which to store the results + * @param url_string The String-ized URL to parse + * @param start The position in the string to start scanning from + * @param end The position in the string to stop scanning + */ +protected void +parseURL(URL url, String url_string, int start, int end) +{ + // This method does not throw an exception or return a value. Thus our + // strategy when we encounter an error in parsing is to return without + // doing anything. + + // Bunches of things should be true. Make sure. + if (end < start) + return; + if ((end - start) < 2) + return; + if (start > url_string.length()) + return; + if (end > url_string.length()) + end = url_string.length(); // This should be safe + + // Turn end into an offset from the end of the string instead of + // the beginning + end = url_string.length() - end; + + // Skip remains of protocol + url_string = url_string.substring(start); + if (!url_string.startsWith("//")) + return; + url_string = url_string.substring(2); + + // Declare some variables + String host = null; + int port = -1; + String file = null; + String anchor = null; + + // Process host and port + int slash_index = url_string.indexOf("/"); + int colon_index = url_string.indexOf(":"); + + if (slash_index > (url_string.length() - end)) + return; + else if (slash_index == -1) + slash_index = url_string.length() - end; + + if ((colon_index == -1) || (colon_index > slash_index)) + { + host = url_string.substring(0, slash_index); + } + else + { + host = url_string.substring(0, colon_index); + + String port_str = url_string.substring(colon_index + 1, slash_index); + try + { + port = Integer.parseInt(port_str); + } + catch (NumberFormatException e) + { + return; + } + } + if (slash_index < (url_string.length() - 1)) + url_string = url_string.substring(slash_index + 1); + else + url_string = ""; + + // Process file and anchor + if (end == 0) + { + file = "/" + url_string; + anchor = null; + } + else + { + file = "/" + url_string.substring(0, url_string.length() - end); + + // Only set anchor if end char is a '#'. Otherwise assume we're + // just supposed to stop scanning for some reason + if (url_string.charAt(url_string.length() - end) == '#') + anchor = url_string.substring((url_string.length() - end) + 1, + url_string.length()); + else + anchor = null; + } + if ((file == null) || (file == "")) + file = "/"; + + // Now set the values + setURL(url, url.getProtocol(), host, port, file, anchor); +} + +/*************************************************************************/ + +/** + * This method converts a URL object into a String. This method creates + * Strings in the mold of http URL's, so protocol handlers which use URL's + * that have a different syntax should override this method + * + * @param url The URL object to convert + */ +protected String +toExternalForm(URL url) +{ + String protocol = url.getProtocol(); + String host = url.getHost(); + int port = url.getPort(); + String file = url.getFile(); + String anchor = url.getRef(); + + return(((protocol != null) ? (protocol + "://") : "") + + ((host != null) ? host : "") + + ((port != -1) ? (":" + port) : "") + + ((file != null) ? file : "/") + + ((anchor != null) ? ("#" + anchor) : "")); +} + +/*************************************************************************/ + +/** + * This methods sets the instance variables representing the various fields + * of the URL to the values passed in. + * + * @param url The URL in which to set the values + * @param protocol The protocol name + * @param host The host name + * @param port The port number + * @param file The file portion + * @param anchor The anchor portion + */ +protected void +setURL(URL url, String protocol, String host, int port, String file, + String anchor) +{ + url.set(protocol, host, port, file, anchor); +} + +} // class URLStreamHandler + diff --git a/java/net/URLStreamHandlerFactory.java b/java/net/URLStreamHandlerFactory.java new file mode 100644 index 000000000..bbc9f64c0 --- /dev/null +++ b/java/net/URLStreamHandlerFactory.java @@ -0,0 +1,44 @@ +/************************************************************************* +/* URLStreamHandlerFactory.java -- Maps protocols to URLStreamHandlers +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This interface contains one method which maps the protocol portion of + * a URL (eg, "http" in "http://www.urbanophile.com/arenn/") to a + * URLStreamHandler object. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface URLStreamHandlerFactory +{ +/** + * This method maps the protocol portion of a URL to a URLStreamHandler + * object. + * + * @param protocol The protocol name to map ("http", "ftp", etc). + * + * @return The URLStreamHandler for the specified protocol + */ +public abstract URLStreamHandler +createURLStreamHandler(String protocol); + +} // interface URLStreamHandlerFactory diff --git a/java/net/UnknownHostException.java b/java/net/UnknownHostException.java new file mode 100644 index 000000000..f8029771c --- /dev/null +++ b/java/net/UnknownHostException.java @@ -0,0 +1,63 @@ +/************************************************************************* +/* UnknownHostException.java -- The hostname is not unknown +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * This exception indicates that an attempt was made to reference a hostname + * or IP address that is not valid. This could possibly indicate that a + * DNS problem has occurred, but most often means that the host was not + * correctly specified. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class UnknownHostException extends java.io.IOException +{ + +/* + * Constructors + */ + +/** + * Constructs a new UnknownHostException with no descriptive message. + */ +public +UnknownHostException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new UnknownHostException with a descriptive message (such as the + * text from strerror(3)) passed in as an argument + * + * @param message A message describing the error that occurs + */ +public +UnknownHostException(String message) +{ + super(message); +} + +} // class UnknownHostException + diff --git a/java/net/UnknownServiceException.java b/java/net/UnknownServiceException.java new file mode 100644 index 000000000..5d74406cd --- /dev/null +++ b/java/net/UnknownServiceException.java @@ -0,0 +1,63 @@ +/************************************************************************* +/* UnknownServiceException.java -- A service error occured +/* +/* Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) +/* +/* This program is free software; you can redistribute it and/or modify +/* it under the terms of the GNU Library General Public License as published +/* by the Free Software Foundation, version 2. (see COPYING.LIB) +/* +/* This program 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 this program; if not, write to the Free Software Foundation +/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA +/*************************************************************************/ + +package java.net; + +/** + * Contrary to what you might think, this does not indicate that the + * TCP/IP service name specified was invalid. Instead it indicates that + * the MIME type returned from a URL could not be determined or that an + * attempt was made to write to a read-only URL. + * + * @version 0.5 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class UnknownServiceException extends java.io.IOException +{ + +/* + * Constructors + */ + +/** + * Constructs a new UnknownServiceException with no descriptive message. + */ +public +UnknownServiceException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Constructs a new UnknownServiceException with a descriptive message (such as the + * text from strerror(3)) passed in as an argument + * + * @param message A message describing the error that occurs + */ +public +UnknownServiceException(String message) +{ + super(message); +} + +} // class UnknownServiceException + diff --git a/native/Makefile.am b/native/Makefile.am new file mode 100644 index 000000000..778b94985 --- /dev/null +++ b/native/Makefile.am @@ -0,0 +1,8 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = java.net + +noinst_HEADERS = config.h config.h.in + +EXTRA_DIST = config.h.in + diff --git a/native/config.h b/native/config.h new file mode 100644 index 000000000..a0e8de831 --- /dev/null +++ b/native/config.h @@ -0,0 +1 @@ +/* native/config.h. Generated automatically by configure. */ diff --git a/native/config.h.in b/native/config.h.in new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/native/config.h.in diff --git a/native/java.net/ChangeLog b/native/java.net/ChangeLog new file mode 100644 index 000000000..60525e511 --- /dev/null +++ b/native/java.net/ChangeLog @@ -0,0 +1,43 @@ +Wed Apr 22 21:39:11 1998 arenn's Development Account <devel@larissa.foo.com> + + * javanet.c: Finished off native method support for multicast sockets + +Tue Apr 21 00:01:55 1998 arenn's Development Account <devel@larissa.foo.com> + + * javanet.c: Lots of bug fixes to make datagram sockets work. + + * PlainDatagramSocketImpl.c: Finished off receive(). Misc bug fixes + to get datagram sockets to work. + +Sun Apr 19 17:19:40 1998 arenn's Development Account <devel@larissa.foo.com> + + * PlainDatagramSocketImpl.c: Wrote initial versions of all native + methods except receive + +Sat Apr 18 22:15:59 1998 arenn's Development Account <devel@larissa.foo.com> + + * javanet.c: Completed all functionality needed for stream sockets + and tested basic functionality. + +Thu Apr 16 19:44:39 1998 arenn's Development Account <devel@larissa.foo.com> + + * InetAddress.c: Cleaned up INADDR_ANY handling + +Wed Apr 15 23:24:06 1998 arenn's Development Account <devel@larissa.foo.com> + + * PlainSocketImpl.c: Finished module. Mostly wrote methods here and + then copied them to javanet.c and redirected all calls from here + to those functions. Got a clean compile. + + * javanet.h: New header file with decls for javanet.c + + * javanet.c: Pulled the socket functions from PlainSocketImpl to here + to facilitate sharing with Datagram sockets. Implemented the + get/set functions for option handling. Modified the _javanet_throw* + function to take an addtional arg specifying the exception. + +Sun Apr 12 14:19:26 1998 arenn's Development Account <devel@larissa.foo.com> + + * InetAddress.c: Created native library routines for InetAddress + class. Clean compile, install and test. + diff --git a/native/java.net/InetAddress.c b/native/java.net/InetAddress.c new file mode 100644 index 000000000..c5c2dc105 --- /dev/null +++ b/native/java.net/InetAddress.c @@ -0,0 +1,190 @@ +/************************************************************************* + * InetAddress.c - Native methods for InetAddress class + * + * Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation, version 2. (see COPYING.LIB) + * + * This program 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 this program; if not, write to the Free Software Foundation + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA + *************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <netinet/in.h> + +#include <jni.h> + +#include "java_net_InetAddress.h" +#include "java_net_InetAddress_stubs.c" + +#include "javanet.h" + +/*************************************************************************/ + +/* + * Function to return the local hostname + */ +JNIEXPORT jstring JNICALL +Java_java_net_InetAddress_getLocalHostName(JNIEnv *env, jclass class) +{ + char buf[255]; + jstring retval; + + if (gethostname(buf, sizeof(buf) - 1) == -1) + strcpy(buf, "localhost"); + + retval = (*env)->NewStringUTF(env, buf); + + return(retval); +} + +/*************************************************************************/ + +/* + * Returns the value of the special IP address INADDR_ANY + */ +JNIEXPORT jarray JNICALL +Java_java_net_InetAddress_lookupInaddrAny(JNIEnv *env, jclass class) +{ + jarray arr; + int *octets; + + /* Allocate an array for the IP address */ + arr = (*env)->NewIntArray(env, 4); + if (!arr) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Internal Error")); + + /* Copy in the values */ + octets = (*env)->GetIntArrayElements(env, arr, 0); + + octets[0] = (INADDR_ANY & 0xFF000000) >> 24; + octets[1] = (INADDR_ANY & 0x00FF0000) >> 16; + octets[2] = (INADDR_ANY & 0x0000FF00) >> 8; + octets[3] = (INADDR_ANY & 0x000000FF); + + (*env)->ReleaseIntArrayElements(env, arr, octets, 0); + + return(arr); +} + +/*************************************************************************/ + +/* + * Function to return the canonical hostname for a given IP address passed + * in as a byte array + */ +JNIEXPORT jstring JNICALL +Java_java_net_InetAddress_getHostByAddr(JNIEnv *env, jclass class, jarray arr) +{ + jint *octets; + jsize len; + int addr; + struct hostent *hp; + jstring retval; + + /* Grab the byte[] array with the IP out of the input data */ + len = (*env)->GetArrayLength(env, arr); + if (len != 4) + return((int)_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Bad IP Address")); + + octets = (*env)->GetIntArrayElements(env, arr, 0); + if (!octets) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Bad IP Address")); + + /* Convert it to a 32 bit address */ + addr = (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3]; + addr = htonl(addr); + + /* Release some memory */ + (*env)->ReleaseIntArrayElements(env, arr, octets, 0); + + /* Resolve the address and return the name */ + hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); + if (!hp) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Bad IP Address")); + + retval = (*env)->NewStringUTF(env, hp->h_name); + + return(retval); +} + +/*************************************************************************/ + +JNIEXPORT jobjectArray JNICALL +Java_java_net_InetAddress_getHostByName(JNIEnv *env, jclass class, jstring host) +{ + const char *hostname; + struct hostent *hp; + int i, ip, *octets; + jsize num_addrs; + jclass arr_class; + jobjectArray addrs; + jarray ret_octets; + + /* Grab the hostname string */ + hostname = (*env)->GetStringUTFChars(env, host, 0); + if (!hostname) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Null hostname")); + + /* Look up the host */ + hp = gethostbyname(hostname); + if (!hp) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + hostname)); + (*env)->ReleaseStringUTFChars(env, host, hostname); + + /* Figure out how many addresses there are and allocate a return array */ + for (num_addrs = 0, i = 0; hp->h_addr_list[i] ; i++) + ++num_addrs; + + arr_class = (*env)->FindClass(env,"[I"); + if (!arr_class) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Internal Error")); + + addrs = (*env)->NewObjectArray(env, num_addrs, arr_class, 0); + if (!addrs) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Internal Error")); + + /* Now loop and copy in each address */ + for (i = 0; i < num_addrs; i++) + { + ret_octets = (*env)->NewIntArray(env, 4); + if (!ret_octets) + return(_javanet_throw_exception(env, UNKNOWN_HOST_EXCEPTION, + "Internal Error")); + + octets = (*env)->GetIntArrayElements(env, ret_octets, 0); + + ip = ntohl(*(int*)(hp->h_addr_list[i])); + octets[0] = (ip & 0xFF000000) >> 24; + octets[1] = (ip & 0x00FF0000) >> 16; + octets[2] = (ip & 0x0000FF00) >> 8; + octets[3] = (ip & 0x000000FF); + + (*env)->ReleaseIntArrayElements(env, ret_octets, octets, 0); + (*env)->SetObjectArrayElement(env, addrs, i, ret_octets); + } + + return(addrs); +} + + diff --git a/native/java.net/Makefile.am b/native/java.net/Makefile.am new file mode 100644 index 000000000..a4864abbd --- /dev/null +++ b/native/java.net/Makefile.am @@ -0,0 +1,39 @@ +## Input file for automake to generate the Makefile.in used by configure + +lib_LTLIBRARIES = libjaphar_javanet.la + +libjaphar_javanet_la_SOURCES = InetAddress.c PlainDatagramSocketImpl.c \ + PlainSocketImpl.c javanet.c javanet.h + +libjaphar_javanet_la_LDFLAGS = -version-info 0:0:0 + +INCLUDES += -I$(prefix)/include -I$(prefix)/include/japhar + +InetAddress.c: java_net_InetAddress.h java_net_InetAddress_stubs.c + +PlainDatagramSocketImpl.c: java_net_PlainDatagramSocketImpl.h \ + java_net_PlainDatagramSocketImpl_stubs.c + +PlainSocketImpl.c: java_net_PlainSocketImpl.h java_net_PlainSocketImpl_stubs.c + +# How to build stubs +# How to build JNI *.h files + +java_net_InetAddress_stubs.c: + japharh -classpath ../.. -stubs -d $(srcdir) java.net.InetAddress + +java_net_InetAddress.h: + javah -jni -d $(srcdir) java.net.InetAddress + +java_net_PlainDatagramSocketImpl_stubs.c: + japharh -classpath ../.. -stubs -d $(srcdir) java.net.PlainDatagramSocketImpl + +java_net_PlainDatagramSocketImpl.h: + javah -jni -d $(srcdir) java.net.PlainDatagramSocketImpl + +java_net_PlainSocketImpl_stubs.c: + japharh -classpath ../.. -stubs -d $(srcdir) java.net.PlainSocketImpl + +java_net_PlainSocketImpl.h: + javah -jni -d $(srcdir) java.net.PlainSocketImpl + diff --git a/native/java.net/PlainDatagramSocketImpl.c b/native/java.net/PlainDatagramSocketImpl.c new file mode 100644 index 000000000..e2497dca7 --- /dev/null +++ b/native/java.net/PlainDatagramSocketImpl.c @@ -0,0 +1,258 @@ +/************************************************************************* + * PlainDatagramSocketImpl.c - Native methods for PlainDatagramSocketImpl class + * + * Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation, version 2. (see COPYING.LIB) + * + * This program 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 this program; if not, write to the Free Software Foundation + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA + *************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <jni.h> + +#include "java_net_PlainDatagramSocketImpl.h" +#include "java_net_PlainDatagramSocketImpl_stubs.c" + +#include "javanet.h" + +/* + * Note that most of the functions in this module simply redirect to another + * internal function. Why? Because many of these functions are shared + * with PlainSocketImpl. + */ + +/*************************************************************************/ + +/* + * Creates a new datagram socket + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_create(JNIEnv *env, jobject this) +{ + _javanet_create(env, this, 0); +} + +/*************************************************************************/ + +/* + * Close the socket. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_close(JNIEnv *env, jobject this) +{ + _javanet_close(env, this, 0); +} + +/*************************************************************************/ + +/* + * This method binds the specified address to the specified local port. + * Note that we have to set the local address and local port public instance + * variables. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_bind(JNIEnv *env, jobject this, + jint port, jobject addr) +{ + _javanet_bind(env, this, addr, port, 0); +} + +/*************************************************************************/ + +/* + * This method sets the specified option for a socket + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_setOption(JNIEnv *env, jobject this, + jint option_id, jobject val) +{ + _javanet_set_option(env, this, option_id, val); +} + +/*************************************************************************/ + +/* + * This method sets the specified option for a socket + */ +JNIEXPORT jobject JNICALL +Java_java_net_PlainDatagramSocketImpl_getOption(JNIEnv *env, jobject this, + jint option_id) +{ + return(_javanet_get_option(env, this, option_id)); +} + +/*************************************************************************/ + +/* + * Reads a buffer from a remote host + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_receive(JNIEnv *env, jobject this, + jobject packet) +{ + unsigned int addr = 0, port = 0, len = 0, bytes_read = 0; + jclass cls, addr_cls; + jmethodID mid; + jarray arr; + jbyte *buf; + char ip_str[16]; + jobject ip_str_obj, addr_obj; + + /* Get the buffer from the packet */ + cls = (*env)->GetObjectClass(env, packet); + mid = (*env)->GetMethodID(env, cls, "getData", "()[B"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + arr = (*env)->CallObjectMethod(env, packet, mid); + if (!arr) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + /* Now get the length from the packet */ + mid = (*env)->GetMethodID(env, cls, "getLength", "()I"); + if (!mid) + { + (*env)->ReleaseByteArrayElements(env, arr, buf, 0); + _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); + return; + } + len = (*env)->CallIntMethod(env, packet, mid); + DBG("Got the length\n"); + + /* Receive the packet */ + /* should we try some sort of validation on the length? */ + bytes_read = _javanet_recvfrom(env, this, arr, 0, len, &addr, &port); + if (bytes_read == -1) + { + /* Taking a chance here because there is a pending exception */ + (*env)->ReleaseByteArrayElements(env, arr, buf, 0); + return; + } + DBG("Received packet\n"); + + /* Store the address */ + addr = ntohl(addr); + sprintf(ip_str, "%d.%d.%d.%d", (addr & 0xFF000000) >> 24, + (addr & 0x00FF0000) >> 16, (addr & 0x0000FF00) >> 8, + (addr & 0x000000FF)); + ip_str_obj = (*env)->NewStringUTF(env, ip_str); + + addr_cls = (*env)->FindClass(env, "java/net/InetAddress"); + if (!addr_cls) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Found InetAddress class\n"); + + mid = (*env)->GetStaticMethodID(env, addr_cls, "getByName", + "(Ljava/lang/String;)Ljava/net/InetAddress;"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Found InetAddress.getByName method\n"); + + addr_obj = (*env)->CallStaticObjectMethod(env, addr_cls, mid, ip_str_obj); + + mid = (*env)->GetMethodID(env, cls, "setAddress", + "(Ljava/net/InetAddress;)V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + (*env)->CallVoidMethod(env, packet, mid, addr_obj); + DBG("Stored the address\n"); + + /* Store the port */ + port = ntohs(((unsigned short)port)); + + mid = (*env)->GetMethodID(env, cls, "setPort", "(I)V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + (*env)->CallVoidMethod(env, packet, mid, port); + DBG("Stored the port\n"); + + /* Store back the length */ + mid = (*env)->GetMethodID(env, cls, "setLength", "(I)V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + (*env)->CallVoidMethod(env, packet, mid, bytes_read); + DBG("Stored the length\n"); + + return; +} + +/*************************************************************************/ + +/* + * Writes a buffer to the remote host + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_sendto(JNIEnv *env, jobject this, + jobject addr, jint port, jarray buf, + jint len) +{ + _javanet_sendto(env, this, buf, 0, len, _javanet_get_netaddr(env, addr), + htons(((unsigned short)port))); +} + +/*************************************************************************/ + +/* + * Joins a multicast group + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_join(JNIEnv *env, jobject this, + jobject addr) +{ + int rc; + struct ip_mreq ipm; + + memset(&ipm, 0, sizeof(ipm)); + ipm.imr_multiaddr.s_addr = _javanet_get_netaddr(env, addr); + ipm.imr_interface.s_addr = INADDR_ANY; + + rc = setsockopt(_javanet_get_int_field(env, this, "native_fd"), + SOL_IP, IP_ADD_MEMBERSHIP, &ipm, sizeof(ipm)); + + if (rc == -1) + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); +} + +/*************************************************************************/ + +/* + * Leaves a multicast group + */ +JNIEXPORT void JNICALL +Java_java_net_PlainDatagramSocketImpl_leave(JNIEnv *env, jobject this, + jobject addr) +{ + int rc; + struct ip_mreq ipm; + + memset(&ipm, 0, sizeof(ipm)); + ipm.imr_multiaddr.s_addr = _javanet_get_netaddr(env, addr); + ipm.imr_interface.s_addr = INADDR_ANY; + + rc = setsockopt(_javanet_get_int_field(env, this, "native_fd"), + SOL_IP, IP_DROP_MEMBERSHIP, &ipm, sizeof(ipm)); + + if (rc == -1) + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); +} + + diff --git a/native/java.net/PlainSocketImpl.c b/native/java.net/PlainSocketImpl.c new file mode 100644 index 000000000..685e1dafe --- /dev/null +++ b/native/java.net/PlainSocketImpl.c @@ -0,0 +1,154 @@ +/************************************************************************* + * PlainSocketImpl.c - Native methods for PlainSocketImpl class + * + * Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation, version 2. (see COPYING.LIB) + * + * This program 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 this program; if not, write to the Free Software Foundation + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA + *************************************************************************/ + +#include <jni.h> + +#include "java_net_PlainSocketImpl.h" +#include "java_net_PlainSocketImpl_stubs.c" + +#include "javanet.h" + +/* + * Note that the functions in this module simply redirect to another + * internal function. Why? Because many of these functions are shared + * with PlainDatagramSocketImpl. The unshared ones were done the same + * way for consistency. + */ + +/*************************************************************************/ + +/* + * Creates a new stream or datagram socket + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_create(JNIEnv *env, jobject this, jboolean stream) +{ + _javanet_create(env, this, stream); +} + +/*************************************************************************/ + +/* + * Close the socket. Any underlying streams will be closed by this + * action as well. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_close(JNIEnv *env, jobject this) +{ + _javanet_close(env, this, 1); +} + +/*************************************************************************/ + +/* + * Connects to the specified destination. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_connect(JNIEnv *env, jobject this, + jobject addr, jint port) +{ + _javanet_connect(env, this, addr, port); +} + +/*************************************************************************/ + +/* + * This method binds the specified address to the specified local port. + * Note that we have to set the local address and local port public instance + * variables. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_bind(JNIEnv *env, jobject this, jobject addr, + jint port) +{ + _javanet_bind(env, this, addr, port, 1); +} + +/*************************************************************************/ + +/* + * Starts listening on a socket with the specified number of pending + * connections allowed. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_listen(JNIEnv *env, jobject this, jint queuelen) +{ + _javanet_listen(env, this, queuelen); +} + +/*************************************************************************/ + +/* + * Accepts a new connection and assigns it to the passed in SocketImpl + * object. Note that we assume this is a PlainSocketImpl just like us. + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_accept(JNIEnv *env, jobject this, jobject impl) +{ + _javanet_accept(env, this, impl); +} + +/*************************************************************************/ + +/* + * This method sets the specified option for a socket + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_setOption(JNIEnv *env, jobject this, + jint option_id, jobject val) +{ + _javanet_set_option(env, this, option_id, val); +} + +/*************************************************************************/ + +/* + * This method sets the specified option for a socket + */ +JNIEXPORT jobject JNICALL +Java_java_net_PlainSocketImpl_getOption(JNIEnv *env, jobject this, + jint option_id) +{ + return(_javanet_get_option(env, this, option_id)); +} + +/*************************************************************************/ + +/* + * Reads a buffer from a remote host + */ +JNIEXPORT jint JNICALL +Java_java_net_PlainSocketImpl_read(JNIEnv *env, jobject this, jarray buf, + jint offset, jint len) +{ + return(_javanet_recvfrom(env, this, buf, offset, len, 0, 0)); +} + +/*************************************************************************/ + +/* + * Writes a buffer to the remote host + */ +JNIEXPORT void JNICALL +Java_java_net_PlainSocketImpl_write(JNIEnv *env, jobject this, jarray buf, + jint offset, jint len) +{ + _javanet_sendto(env, this, buf, offset, len, 0, 0); +} + diff --git a/native/java.net/javanet.c b/native/java.net/javanet.c new file mode 100644 index 000000000..f1a5965a5 --- /dev/null +++ b/native/java.net/javanet.c @@ -0,0 +1,1012 @@ +/************************************************************************* + * javanet.c - Common internal functions for the java.net package + * + * Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation, version 2. (see COPYING.LIB) + * + * This program 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 this program; if not, write to the Free Software Foundation + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA + *************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <jni.h> + +#include "javanet.h" + +/* Need to have some value for SO_TIMEOUT */ +#ifndef SO_TIMEOUT +#ifndef SO_RCVTIMEO +#warning Neither SO_TIMEOUT or SO_RCVTIMEO are defined! +#warning This will cause all get/setOption calls with that value to throw an exception +#else +#define SO_TIMEOUT SO_RCVTIMEO +#endif /* not SO_RCVTIMEO */ +#endif /* not SO_TIMEOUT */ + +/*************************************************************************/ + +/* + * This is a common function to throw exceptions. + */ +int +_javanet_throw_exception(JNIEnv *env, const char *exception, const char *msg) +{ + jclass ecls; + + /* Find our exception class */ + ecls = (*env)->FindClass(env, exception); + if (!ecls) + return(0); + + /* Clean any pending exceptions */ + if ((*env)->ExceptionOccurred(env)) + (*env)->ExceptionClear(env); + + /* Throw the exception and return */ + (*env)->ThrowNew(env, ecls, msg); + + return(0); +} + +/*************************************************************************/ + +/* + * Sets an integer field in the specified object. + */ +static void +_javanet_set_int_field(JNIEnv *env, jobject obj, char *class, char *field, + int val) +{ + jclass cls; + jfieldID fid; + + cls = (*env)->FindClass(env, class); + fid = (*env)->GetFieldID(env, cls, field, "I"); + if (!fid) + return; + + (*env)->SetIntField(env, obj, fid, val); + + return; +} + +/*************************************************************************/ + +/* + * Returns the value of the specified integer instance variable field or + * -1 if an error occurs. + */ +int +_javanet_get_int_field(JNIEnv *env, jobject obj, const char *field) +{ + jclass cls = 0; + jfieldID fid; + int fd; + + DBG("Entered _javanet_get_int_field\n"); + + cls = (*env)->GetObjectClass(env, obj); + fid = (*env)->GetFieldID(env, cls, field, "I"); + if (!fid) + return(-1); + DBG("Found field id\n"); + + fd = (*env)->GetIntField(env, obj, fid); + + return(fd); +} + +/*************************************************************************/ + +/* + * Creates a FileDescriptor object in the parent class. It is not used + * by this implementation, but the docs list it as a variable, so we + * need to include it. + */ +static void +_javanet_create_localfd(JNIEnv *env, jobject this) +{ + jclass this_cls, fd_cls; + jfieldID fid; + jmethodID mid; + jobject fd_obj; + + DBG("Entered _javanet_create_localfd\n"); + + /* Look up the fd field */ + this_cls = (*env)->FindClass(env, "java/net/SocketImpl"); + fid = (*env)->GetFieldID(env, this_cls, "fd", "Ljava/io/FileDescriptor;"); + if (!fid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Found fd variable\n"); + + /* Create a FileDescriptor */ + fd_cls = (*env)->FindClass(env, "java/io/FileDescriptor"); + if (!fd_cls) + { + _javanet_throw_exception(env, IO_EXCEPTION, "Can't load FileDescriptor class"); + return; + } + DBG("Found FileDescriptor class\n"); + + mid = (*env)->GetMethodID(env, fd_cls, "<init>", "()V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Found FileDescriptor constructor\n"); + + fd_obj = (*env)->NewObject(env, fd_cls, mid); + if (!fd_obj) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Created FileDescriptor\n"); + + /* Now set the pointer to the new FileDescriptor */ + (*env)->SetObjectField(env, this, fid, fd_obj); + DBG("Set fd field\n"); + + return; +} + +/*************************************************************************/ + +/* + * Returns a Boolean object with the specfied value + */ +static jobject +_javanet_create_boolean(JNIEnv *env, jboolean val) +{ + jclass cls; + jmethodID mid; + jobject obj; + + cls = (*env)->FindClass(env, "java/lang/Boolean"); + if (!cls) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + mid = (*env)->GetMethodID(env, cls, "<init>", "(Z)V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + obj = (*env)->NewObject(env, cls, mid, val); + if (!obj) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + return(obj); +} + +/*************************************************************************/ + +/* + * Returns an Integer object with the specfied value + */ +static jobject +_javanet_create_integer(JNIEnv *env, jint val) +{ + jclass cls; + jmethodID mid; + jobject obj; + + cls = (*env)->FindClass(env, "java/lang/Integer"); + if (!cls) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + mid = (*env)->GetMethodID(env, cls, "<init>", "(I)V"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + obj = (*env)->NewObject(env, cls, mid, val); + if (!obj) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + return(obj); +} + +/*************************************************************************/ + +/* + * Builds an InetAddress object from a 32 bit address in host byte order + */ +static jobject +_javanet_create_inetaddress(JNIEnv *env, int netaddr) +{ + char buf[16]; + jclass ia_cls; + jmethodID mid; + jstring ip_str; + jobject ia; + + /* Build a string IP address */ + sprintf(buf, "%d.%d.%d.%d", ((netaddr & 0xFF000000) >> 24), + ((netaddr & 0x00FF0000) >> 16), ((netaddr &0x0000FF00) >> 8), + (netaddr & 0x000000FF)); + DBG("Created ip addr string\n"); + + /* Get an InetAddress object for this IP */ + ia_cls = (*env)->FindClass(env, "java/net/InetAddress"); + if (!ia_cls) + { + _javanet_throw_exception(env, IO_EXCEPTION, "Can't load InetAddress class"); + return(0); + } + DBG("Found InetAddress class\n"); + + mid = (*env)->GetStaticMethodID(env, ia_cls, "getByName", + "(Ljava/lang/String;)Ljava/net/InetAddress;"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + DBG("Found getByName method\n"); + + ip_str = (*env)->NewStringUTF(env, buf); + if (!ip_str) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + + ia = (*env)->CallStaticObjectMethod(env, ia_cls, mid, ip_str); + if (!ia) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return(0); } + DBG("Called getByName method\n"); + + return(ia); +} + +/*************************************************************************/ + +/* + * Set's the value of the "addr" field in PlainSocketImpl with a new + * InetAddress for the specified addr + */ +static void +_javanet_set_remhost(JNIEnv *env, jobject this, int netaddr) +{ + jclass this_cls; + jfieldID fid; + jobject ia; + + DBG("Entered _javanet_set_remhost\n"); + + /* Get an InetAddress object */ + ia = _javanet_create_inetaddress(env, netaddr); + if (!ia) + return; + + /* Set the variable in the object */ + this_cls = (*env)->FindClass(env, "java/net/SocketImpl"); + fid = (*env)->GetFieldID(env, this_cls, "address", "Ljava/net/InetAddress;"); + if (!fid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + DBG("Found address field\n"); + + (*env)->SetObjectField(env, this, fid, ia); + DBG("Set field\n"); +} + +/*************************************************************************/ + +/* + * Returns a 32 bit Internet address for the passed in InetAddress object + */ +int +_javanet_get_netaddr(JNIEnv *env, jobject addr) +{ + jclass cls = 0; + jmethodID mid; + jarray arr = 0; + jbyte *octets; + int netaddr, len; + + DBG("Entered _javanet_get_netaddr\n"); + + /* Call the getAddress method on the object to retrieve the IP address */ + cls = (*env)->GetObjectClass(env, addr); + mid = (*env)->GetMethodID(env, cls, "getAddress", "()[B"); + if (!mid) + return(_javanet_throw_exception(env, IO_EXCEPTION, "Internal Error")); + DBG("Got getAddress method\n"); + + arr = (*env)->CallObjectMethod(env, addr, mid); + if (!arr) + return(_javanet_throw_exception(env, IO_EXCEPTION, "Internal Error")); + DBG("Got the address\n"); + + /* Turn the IP address into a 32 bit Internet address in network byte order */ + len = (*env)->GetArrayLength(env, arr); + if (len != 4) + return(_javanet_throw_exception(env, IO_EXCEPTION, "Internal Error")); + DBG("Length ok\n"); + + octets = (*env)->GetByteArrayElements(env, arr, 0); + if (!octets) + return(_javanet_throw_exception(env, IO_EXCEPTION, "Internal Error")); + DBG("Grabbed bytes\n"); + + netaddr = (((unsigned char)octets[0]) << 24) + + (((unsigned char)octets[1]) << 16) + + (((unsigned char)octets[2]) << 8) + + ((unsigned char)octets[3]); + + netaddr = htonl(netaddr); + + (*env)->ReleaseByteArrayElements(env, arr, octets, 0); + DBG("Done getting addr\n"); + + return(netaddr); +} + +/*************************************************************************/ + +/* + * Creates a new stream or datagram socket + */ +void +_javanet_create(JNIEnv *env, jobject this, jboolean stream) +{ + int fd; + + if (stream) + fd = socket(AF_INET, SOCK_STREAM, 0); + else + fd = socket(AF_INET, SOCK_DGRAM, 0); + + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); return; } + + if (stream) + _javanet_set_int_field(env, this, "java/net/PlainSocketImpl", + "native_fd", fd); + else + _javanet_set_int_field(env, this, "java/net/PlainDatagramSocketImpl", + "native_fd", fd); +} + +/*************************************************************************/ + +/* + * Close the socket. Any underlying streams will be closed by this + * action as well. + */ +void +_javanet_close(JNIEnv *env, jobject this, int stream) +{ + int fd = -1; + + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + return; + + close(fd); + + if (stream) + _javanet_set_int_field(env, this, "java/net/PlainSocketImpl", + "native_fd", -1); + else + _javanet_set_int_field(env, this, "java/net/PlainDatagramSocketImpl", + "native_fd", -1); +} + +/*************************************************************************/ + +/* + * Connects to the specified destination. + */ +void +_javanet_connect(JNIEnv *env, jobject this, jobject addr, jint port) +{ + int netaddr, fd = -1, rc, addrlen; + struct sockaddr_in si; + + DBG("Entered _javanet_connect\n"); + + /* Pre-process input variables */ + netaddr = _javanet_get_netaddr(env, addr); + if ((*env)->ExceptionOccurred(env)) + return; + + if (port == -1) + port = 0; + DBG("Got network address\n"); + + /* Grab the real socket file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Socket not yet created"); return; } + DBG("Got native fd\n"); + + /* Connect up */ + memset(&si, 0, sizeof(struct sockaddr_in)); + si.sin_family = AF_INET; + si.sin_addr.s_addr = netaddr; + si.sin_port = htons(((short)port)); + + rc = connect(fd, &si, sizeof(struct sockaddr_in)); + if (rc == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); return; } + DBG("Connected successfully\n"); + + /* Populate instance variables */ + addrlen = sizeof(struct sockaddr_in); + rc = getsockname(fd, &si, &addrlen); + if (rc == -1) + { + close(fd); + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); + return; + } + + _javanet_create_localfd(env, this); + if ((*env)->ExceptionOccurred(env)) + return; + DBG("Created fd\n"); + + _javanet_set_int_field(env, this, "java/net/SocketImpl", "localport", + ntohs(si.sin_port)); + if ((*env)->ExceptionOccurred(env)) + return; + DBG("Set the local port\n"); + + addrlen = sizeof(struct sockaddr_in); + rc = getpeername(fd, &si, &addrlen); + if (rc == -1) + { + close(fd); + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); + return; + } + + _javanet_set_remhost(env, this, ntohl(si.sin_addr.s_addr)); + if ((*env)->ExceptionOccurred(env)) + return; + DBG("Set the remote host\n"); + + _javanet_set_int_field(env, this, "java/net/SocketImpl", "port", + ntohs(si.sin_port)); + if ((*env)->ExceptionOccurred(env)) + return; + DBG("Set the remote port\n"); +} + +/*************************************************************************/ + +/* + * This method binds the specified address to the specified local port. + * Note that we have to set the local address and local + * port public instance variables. + */ +void +_javanet_bind(JNIEnv *env, jobject this, jobject addr, jint port, int stream) +{ + jclass cls; + jmethodID mid; + jbyteArray arr = 0; + jbyte *octets; + jint fd; + struct sockaddr_in si; + int namelen; + + DBG("Entering native bind()\n"); + + /* Get the address to connect to */ + cls = (*env)->GetObjectClass(env, addr); + mid = (*env)->GetMethodID(env, cls, "getAddress", "()[B"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal error") ; return; } + DBG("Past getAddress method id\n"); + + arr = (*env)->CallObjectMethod(env, addr, mid); + if (!arr || (*env)->ExceptionOccurred(env)) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal error") ; return; } + DBG("Past call object method\n"); + + octets = (*env)->GetByteArrayElements(env, arr, 0); + if (!octets) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal error") ; return; } + DBG("Past grab array\n"); + + /* Get the native socket file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { + (*env)->ReleaseByteArrayElements(env, arr, octets, 0); + _javanet_throw_exception(env, IO_EXCEPTION, "Internal error"); + return; + } + DBG("Past native_fd lookup\n"); + + /* Bind the socket */ + memset(&si, 0, sizeof(struct sockaddr_in)); + + si.sin_family = AF_INET; + si.sin_addr.s_addr = *(int *)octets; /* Already in network byte order */ + if (port == -1) + si.sin_port = 0; + else + si.sin_port = htons(port); + + (*env)->ReleaseByteArrayElements(env, arr, octets, 0); + + if (bind(fd, &si, sizeof(struct sockaddr_in)) == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); return; } + DBG("Past bind\n"); + + /* Update instance variables, specifically the local port number */ + namelen = sizeof(struct sockaddr_in); + getsockname(fd, &si, &namelen); + + if (stream) + _javanet_set_int_field(env, this, "java/net/SocketImpl", + "localport", ntohs(si.sin_port)); + else + _javanet_set_int_field(env, this, "java/net/DatagramSocketImpl", + "localPort", ntohs(si.sin_port)); + DBG("Past update port number\n"); + + return; +} + +/*************************************************************************/ + +/* + * Starts listening on a socket with the specified number of pending + * connections allowed. + */ +void +_javanet_listen(JNIEnv *env, jobject this, jint queuelen) +{ + int fd = -1, rc; + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Internal Error"); return; } + + /* Start listening */ + rc = listen(fd, queuelen); + if (rc == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); return; } + + return; +} + +/*************************************************************************/ + +/* + * Accepts a new connection and assigns it to the passed in SocketImpl + * object. Note that we assume this is a PlainSocketImpl just like us + */ +void +_javanet_accept(JNIEnv *env, jobject this, jobject impl) +{ + int fd = -1, newfd, addrlen, rc; + struct sockaddr_in si; + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + /* Accept the connection */ + addrlen = sizeof(struct sockaddr_in); + memset(&si, 0, addrlen); + + /******* Do we need to look for EINTR? */ + newfd = accept(fd, &si, &addrlen); + if (newfd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + /* Populate instance variables */ + _javanet_set_int_field(env, impl, "java/net/PlainSocketImpl", "native_fd", + newfd); + + rc = getsockname(newfd, &si, &addrlen); + if (rc == -1) + { + close(newfd); + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); + return; + } + + _javanet_create_localfd(env, impl); + if ((*env)->ExceptionOccurred(env)) + return; + + _javanet_set_int_field(env, impl, "java/net/SocketImpl", "localport", + ntohs(si.sin_port)); + if ((*env)->ExceptionOccurred(env)) + return; + + addrlen = sizeof(struct sockaddr_in); + rc = getpeername(newfd, &si, &addrlen); + if (rc == -1) + { + close(newfd); + _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); + return; + } + + _javanet_set_remhost(env, impl, ntohl(si.sin_addr.s_addr)); + if ((*env)->ExceptionOccurred(env)) + return; + + _javanet_set_int_field(env, impl, "java/net/SocketImpl", "port", + ntohs(si.sin_port)); + if ((*env)->ExceptionOccurred(env)) + return; +} + +/*************************************************************************/ + +/* + * Receives a buffer from a remote host. The args are: + * + * buf - The byte array into which the data received will be written + * offset - Offset into the byte array to start writing + * len - The number of bytes to read. + * addr - Pointer to 32 bit net address of host to receive from. If null, + * this parm is ignored. If pointing to an address of 0, the + * actual address read is stored here + * port - Pointer to the port to receive from. If null, this parm is ignored. + * If it is 0, the actual remote port received from is stored here + * + * The actual number of bytes read is returned. + */ +int +_javanet_recvfrom(JNIEnv *env, jobject this, jarray buf, int offset, int len, + int *addr, int *port) +{ + int fd, rc, si_len; + jbyte *p; + struct sockaddr_in si; + + DBG("Entered _javanet_recvfrom\n"); + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { return(_javanet_throw_exception(env, IO_EXCEPTION, "No Socket")); } + DBG("Got native_fd\n"); + + /* Get a pointer to the buffer */ + p = (*env)->GetByteArrayElements(env, buf, 0); + if (!p) + { return(_javanet_throw_exception(env, IO_EXCEPTION, "Internal Error")); } + DBG("Got buffer\n"); + + /* Read the data */ + for (;;) + { + if (!addr) + rc = recvfrom(fd, p + offset, len, 0, 0, 0); + else + { + memset(&si, 0, sizeof(struct sockaddr_in)); + si_len = sizeof(struct sockaddr_in); + rc = recvfrom(fd, p + offset, len, 0, &si, &si_len); + } + + if ((rc == -1) && (errno == EINTR)) + continue; + + break; + } + + (*env)->ReleaseByteArrayElements(env, buf, p, 0); + + if (rc == -1) + { return(_javanet_throw_exception(env, IO_EXCEPTION, strerror(errno))); } + + /* Handle return addr case */ + if (addr) + { + *addr = si.sin_addr.s_addr; + if (port) + *port = si.sin_port; + } + + return(rc); +} + +/*************************************************************************/ + +/* + * Sends a buffer to a remote host. The args are: + * + * buf - A byte array + * offset - Index into the byte array to start sendign + * len - The number of bytes to write + * addr - The 32bit address to send to (may be 0) + * port - The port number to send to (may be 0) + */ +void +_javanet_sendto(JNIEnv *env, jobject this, jarray buf, int offset, int len, + int addr, int port) +{ + int fd, rc; + jbyte *p; + struct sockaddr_in si; + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, "No Socket"); return; } + + /* Get a pointer to the buffer */ + p = (*env)->GetByteArrayElements(env, buf, 0); + if (!p) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + /* Send the data */ + if (!addr) + rc = send(fd, p + offset, len, 0); + else + { + memset(&si, 0, sizeof(struct sockaddr_in)); + si.sin_family = AF_INET; + si.sin_addr.s_addr = addr; + si.sin_port = (unsigned short)port; + + DBG("Sending....\n"); + rc = sendto(fd, p + offset, len, 0, &si, sizeof(struct sockaddr_in)); + } + + (*env)->ReleaseByteArrayElements(env, buf, p, 0); + + /***** Do we need to check EINTR? */ + if (rc == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, strerror(errno)); return; } + + return; +} + +/*************************************************************************/ + +/* + * Sets the specified option for a socket + */ +void +_javanet_set_option(JNIEnv *env, jobject this, jint option_id, jobject val) +{ + int fd = -1, rc; + int optval; + jclass cls; + jmethodID mid; + struct linger linger; + struct sockaddr_in si; + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, IO_EXCEPTION, "Internal Error"); return; } + + /* We need a class object for all cases below */ + cls = (*env)->GetObjectClass(env, val); + + /* Process the option request */ + switch (option_id) + { + /* TCP_NODELAY case. val is a Boolean that tells us what to do */ + case SOCKOPT_TCP_NODELAY: + mid = (*env)->GetMethodID(env, cls, "booleanValue", "()Z"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Internal Error"); return; } + + /* Should be a 0 or a 1 */ + optval = (*env)->CallBooleanMethod(env, val, mid); + + rc = setsockopt(fd, SOL_TCP, TCP_NODELAY, &optval, sizeof(int)); + break; + + /* SO_LINGER case. If val is a boolean, then it will always be set + to false indicating disable linger, otherwise it will be an + integer that contains the linger value */ + case SOCKOPT_SO_LINGER: + memset(&linger, 0, sizeof(struct linger)); + + mid = (*env)->GetMethodID(env, cls, "booleanValue", "()Z"); + if (mid) + { + /* We are disabling linger */ + linger.l_onoff = 0; + } + else + { + mid = (*env)->GetMethodID(env, cls, "intValue", "()I"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Internal Error"); return; } + + linger.l_linger = (*env)->CallIntMethod(env, val, mid); + linger.l_onoff = 1; + } + rc = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, + sizeof(struct linger)); + break; + + /* SO_TIMEOUT case. Val will be an integer with the new value */ + case SOCKOPT_SO_TIMEOUT: +#ifdef SO_TIMEOUT + mid = (*env)->GetMethodID(env, cls, "intValue", "()I"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Internal Error"); return; } + + optval = (*env)->CallIntMethod(env, val, mid); + + rc = setsockopt(fd, SOL_SOCKET, SO_TIMEOUT, &optval, sizeof(int)); +#else + _javanet_throw_exception(env, SOCKET_EXCEPTION, + "SO_TIMEOUT not supported on this platform"); + return; +#endif + break; + + /* TTL case. Val with be an Integer with the new time to live value */ + case SOCKOPT_IP_TTL: + mid = (*env)->GetMethodID(env, cls, "intValue", "()I"); + if (!mid) + { _javanet_throw_exception(env, IO_EXCEPTION, + "Internal Error"); return; } + + optval = (*env)->CallIntMethod(env, val, mid); + + rc = setsockopt(fd, SOL_IP, IP_TTL, &optval, sizeof(int)); + break; + + /* Multicast Interface case - val is InetAddress object */ + case SOCKOPT_IP_MULTICAST_IF: + memset(&si, 0, sizeof(struct sockaddr_in)); + si.sin_family = AF_INET; + si.sin_addr.s_addr = _javanet_get_netaddr(env, val); + + if ((*env)->ExceptionOccurred(env)) + return; + + rc = setsockopt(fd, SOL_IP, IP_MULTICAST_IF, &si, + sizeof(struct sockaddr_in)); + break; + + default: + _javanet_throw_exception(env, SOCKET_EXCEPTION, "Unrecognized option"); + return; + } + + /* Check to see if above operations succeeded */ + if (rc == -1) + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + + return; +} + +/*************************************************************************/ + +/* + * Retrieves the specified option values for a socket + */ +jobject +_javanet_get_option(JNIEnv *env, jobject this, jint option_id) +{ + int fd = -1, rc; + int optval, optlen; + struct linger linger; + struct sockaddr_in si; + + /* Get the real file descriptor */ + fd = _javanet_get_int_field(env, this, "native_fd"); + if (fd == -1) + { _javanet_throw_exception(env, SOCKET_EXCEPTION, "Internal Error"); return(0); } + + /* Process the option requested */ + switch (option_id) + { + /* TCP_NODELAY case. Return a Boolean indicating on or off */ + case SOCKOPT_TCP_NODELAY: + optlen = sizeof(optval); + rc = getsockopt(fd, SOL_TCP, TCP_NODELAY, &optval, &optlen); + if (rc == -1) + { + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + if (optval) + return(_javanet_create_boolean(env, 1)); + else + return(_javanet_create_boolean(env, 0)); + + break; + + /* SO_LINGER case. If disabled, return a Boolean object that represents + false, else return an Integer that is the value of SO_LINGER */ + case SOCKOPT_SO_LINGER: + memset(&linger, 0, sizeof(struct linger)); + optlen = sizeof(struct linger); + + rc = getsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, &optlen); + if (rc == -1) + { + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + if (linger.l_onoff) + return(_javanet_create_integer(env, linger.l_linger)); + else + return(_javanet_create_boolean(env, 0)); + + break; + + /* SO_TIMEOUT case. Return an Integer object with the timeout value */ + case SOCKOPT_SO_TIMEOUT: +#ifdef SO_TIMEOUT + optlen = sizeof(int); + + rc = getsockopt(fd, SOL_SOCKET, SO_TIMEOUT, &optval, &optlen); +#else + _javanet_throw_exception(env, SOCKET_EXCEPTION, + "SO_TIMEOUT not supported on this platform"); + return(0); +#endif /* not SO_TIMEOUT */ + + if (rc == -1) + { + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + return(_javanet_create_integer(env, optval)); + break; + + /* The TTL case. Return an Integer with the Time to Live value */ + case SOCKOPT_IP_TTL: + optlen = sizeof(int); + + rc = getsockopt(fd, SOL_IP, IP_TTL, &optval, &optlen); + if (rc == -1) + { + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + return(_javanet_create_integer(env, optval)); + break; + + /* Multicast interface case */ + case SOCKOPT_IP_MULTICAST_IF: + memset(&si, 0, sizeof(struct sockaddr_in)); + optlen = sizeof(struct sockaddr_in); + + rc = getsockopt(fd, SOL_IP, IP_MULTICAST_IF, &si, &optlen); + if (rc == -1) + { + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + return(_javanet_create_inetaddress(env, ntohl(si.sin_addr.s_addr))); + break; + + default: + _javanet_throw_exception(env, SOCKET_EXCEPTION, strerror(errno)); + return(0); + } + + return(0); +} + diff --git a/native/java.net/javanet.h b/native/java.net/javanet.h new file mode 100644 index 000000000..cfc4b6b07 --- /dev/null +++ b/native/java.net/javanet.h @@ -0,0 +1,83 @@ +/************************************************************************* + * javanet.h - Declarations for common functions for the java.net package + * + * Copyright (c) 1998 by Aaron M. Renn (arenn@urbanophile.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published + * by the Free Software Foundation, version 2. (see COPYING.LIB) + * + * This program 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 this program; if not, write to the Free Software Foundation + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA + *************************************************************************/ + +#ifndef _JAVANET_LOADED +#define _JAVANET_LOADED + +#include <jni.h> + +/*************************************************************************/ + +/* + * Defined constants + */ + +/* Exception Classes */ +#define IO_EXCEPTION "java/io/IOException" +#define SOCKET_EXCEPTION "java/net/SocketException" +#define UNKNOWN_HOST_EXCEPTION "java/net/UnknownHostException" + +/* Socket Option Identifiers - Don't change or binary compatibility with + the JDK will be broken! These also need to + be kept compatible with java.net.SocketOptions */ +#define SOCKOPT_TCP_NODELAY 1 +#define SOCKOPT_SO_LINGER 128 +#define SOCKOPT_SO_TIMEOUT 4102 + +/* Internal option identifiers. Not needed for JDK compatibility */ +#define SOCKOPT_IP_TTL 7777 +#define SOCKOPT_IP_MULTICAST_IF 7778 + +/*************************************************************************/ + +/* + * Macros + */ + +/* Simple debug macro */ +#ifdef DEBUG +#define DBG(x) fprintf(stderr, (x)); +#else +#define DBG(x) +#endif + +/*************************************************************************/ + +/* + * Function Prototypes + */ + +extern int _javanet_throw_exception(JNIEnv *, const char *, const char *); +extern int _javanet_get_int_field(JNIEnv *, jobject, const char *); +extern int _javanet_get_netaddr(JNIEnv *, jobject); +extern void _javanet_create(JNIEnv *, jobject, jboolean); +extern void _javanet_close(JNIEnv *, jobject, int); +extern void _javanet_connect(JNIEnv *, jobject, jobject, jint); +extern void _javanet_bind(JNIEnv *, jobject, jobject, jint, int); +extern void _javanet_listen(JNIEnv *, jobject, jint); +extern void _javanet_accept(JNIEnv *, jobject, jobject); +extern int _javanet_recvfrom(JNIEnv *, jobject, jarray, int, int, int *, int *); +extern void _javanet_sendto(JNIEnv *, jobject, jarray, int, int, int, int); +extern jobject _javanet_get_option(JNIEnv *, jobject, jint); +extern void _javanet_set_option(JNIEnv *, jobject, jint, jobject); + +/*************************************************************************/ + +#endif /* not _JAVANET_H_LOADED */ + diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 000000000..1376de35c --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,4 @@ +## Input file for automake to generate the Makefile.in used by configure + +SUBDIRS = java.net + diff --git a/test/java.net/ClientDatagram.java b/test/java.net/ClientDatagram.java new file mode 100644 index 000000000..6ec98258e --- /dev/null +++ b/test/java.net/ClientDatagram.java @@ -0,0 +1,112 @@ +/* Class to test Datagrams from a client perspective */ + +import java.io.*; +import java.net.*; + +public class ClientDatagram +{ + +public static void +main(String[] argv) throws IOException +{ + System.out.println("Starting datagram tests"); + + byte[] buf = new byte[2048]; + DatagramPacket p = new DatagramPacket(buf, buf.length); + InetAddress addr = InetAddress.getByName("localhost"); + + /* Execute the daytime UDP service on localhost. You may need to + enable this in inetd to make the test work */ + System.out.println("Test 1: Simple daytime test"); + try + { + + DatagramSocket s = new DatagramSocket(); + + System.out.println("Socket bound to " + s.getLocalAddress() + + ":" + s.getLocalPort()); + + byte[] sbuf = { 'H', 'I' }; + DatagramPacket spack = new DatagramPacket(sbuf, sbuf.length, addr, 13); + + s.send(spack); + s.receive(p); + + System.out.println("Received " + p.getLength() + " bytes from " + + p.getAddress() + ":" + p.getPort()); + + byte[] tmp = new byte[p.getLength()]; + for (int i = 0; i < p.getLength(); i++) + tmp[i] = buf[i]; + + System.out.print("Data: " + new String(tmp)); + + s.close(); + System.out.println("PASSED simple datagram test"); + } + catch(Exception e) + { + System.out.println("FAILED simple datagram test: " + e); + } + + System.out.println("Test 2: Specific host/port binding"); + try + { + DatagramSocket s = new DatagramSocket(8765, addr); + if (s.getLocalPort() != 8765) + throw new IOException("Bound to wrong port: " + s.getLocalPort()); + + if (!s.getLocalAddress().equals(addr)) + throw new IOException("Bound to wrong host:" + s.getLocalAddress()); + + s.close(); + System.out.println("PASSED specific host/port binding test"); + } + catch (Exception e) + { + System.out.println("FAILED specific host/port binding: " + e); + } + + System.out.println("Test 3: Socket Options test"); + try + { + DatagramSocket s = new DatagramSocket(); + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + System.out.println("Setting SO_TIMEOUT to 170"); + s.setSoTimeout(170); + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + System.out.println("Setting SO_TIMEOUT to 0"); + s.setSoTimeout(0); + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + s.close(); + } + catch(Exception e) + { + System.out.println("WARNING: Problem with SO_TIMEOUT test: " + e.getMessage()); + System.out.println("This is ok on Linux"); + } + + System.out.println("Test 4: Max values test"); + try + { +// ServerDatagram sd = new ServerDatagram(37900); +// sd.run(); + + DatagramSocket s = new DatagramSocket(); + byte[] sbuf = new byte[65332]; + DatagramPacket spack = new DatagramPacket(sbuf, sbuf.length, + addr, 37900); + + s.send(spack); + s.close(); + } + catch (Exception e) + { + System.out.println("FAILED max values test: " + e); + } + + System.out.println("Datagram testing complete"); +} + +} + diff --git a/test/java.net/ClientSocket.java b/test/java.net/ClientSocket.java new file mode 100644 index 000000000..53a498b34 --- /dev/null +++ b/test/java.net/ClientSocket.java @@ -0,0 +1,189 @@ +/* A class to test my client TCP socket implementation */ + +import java.net.*; +import java.io.*; + +public class ClientSocket extends Object +{ +public static void +main(String[] argv) throws IOException +{ + System.out.println("Starting client stream socket test"); + + /* Simple connection and read test */ + System.out.println("Test 1: Connection to daytime port on local host"); + try + { + InetAddress addr = InetAddress.getByName("127.0.0.1"); + + Socket s = new Socket(addr, 13); + + InputStream is = s.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + + for (String str = br.readLine(); ; str = br.readLine()) + { + if (str == null) + break; + System.out.println(str); + } + s.close(); + System.out.println("PASSED: daytime test"); + } + catch(IOException e) + { + System.out.println("FAILED: daytime test: " + e); + } + + /* Simple connection refused test */ + System.out.println("Test 2: Connection refused test"); + try + { + InetAddress addr = InetAddress.getByName("127.0.0.1"); + + Socket s = new Socket(addr, 47); + s.close(); + + System.out.print("WARNING: Cannot perform connection refused test"); + System.out.println(" because someone is listening on localhost:47"); + } + catch(IOException e) + { + System.out.println("PASSED: connection refused test: " + e.getMessage()); + } + + /* Socket attributes test */ + System.out.println("Test 3: Connection attributes"); + try + { + Socket s = new Socket("www.netscape.com", 80); + + String laddr = s.getLocalAddress().getHostName(); + int lport = s.getLocalPort(); + String raddr = s.getInetAddress().getHostName(); + int rport = s.getPort(); + + System.out.println("Local Address is: " + laddr); + System.out.println("Local Port is: " + lport); + System.out.println("Remote Address is: " + raddr); + System.out.println("Remote Port is: " + rport); + System.out.println("Socket.toString is: " + s); + + if ( (laddr == null) || + ((lport < 0) || (lport > 65535)) || + (raddr.indexOf("netscape.com") == -1) || + (rport != 80)) + System.out.println("FAILED: connection attribute test"); + else + System.out.println("PASSED: connection attribute test"); + + s.close(); + } + catch(IOException e) + { + System.out.println("FAILED: connection attributes test: " + e.getMessage()); + } + + /* Socket options test */ + System.out.println("Test 4: Socket options"); + Socket s = new Socket("127.0.0.1", 23); + + try + { + // SO_TIMEOUT + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + System.out.println("Setting SO_TIMEOUT to 142"); + s.setSoTimeout(142); + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + System.out.println("Setting SO_TIMEOUT to 0"); + s.setSoTimeout(0); + System.out.println("SO_TIMEOUT = " + s.getSoTimeout()); + } + catch (IOException e) + { + System.out.println("WARNING: SO_TIMEOUT problem: " + e.getMessage()); + System.out.println("This is ok on Linux"); + } + try + { + // Try TCP_NODELAY + System.out.println("TCP_NODELAY = " + s.getTcpNoDelay()); + System.out.println("Setting TCP_NODELAY to true"); + s.setTcpNoDelay(true); + System.out.println("TCP_NODELAY = " + s.getTcpNoDelay()); + System.out.println("Setting TCP_NODELAY to false"); + s.setTcpNoDelay(false); + System.out.println("TCP_NODELAY = " + s.getTcpNoDelay()); + + // Try SO_LINGER + System.out.println("SO_LINGER = " + s.getSoLinger()); + System.out.println("Setting SO_LINGER to 100"); + s.setSoLinger(true, 100); + System.out.println("SO_LINGER = " + s.getSoLinger()); + System.out.println("Setting SO_LINGER to off"); + s.setSoLinger(false, 0); + System.out.println("SO_LINGER = " + s.getSoLinger()); + + System.out.println("PASSED: socket options test"); + } + catch(IOException e) + { + System.out.println("FAILED: socket options test: " + e.getMessage()); + } + s.close(); + + /* Simple read/write test */ + System.out.println("Test 5: Simple read/write test"); + try + { + System.out.println("Downloading the Transmeta homepage"); + s = new Socket("www.transmeta.com", 80); + + BufferedReader in = new BufferedReader(new + InputStreamReader(s.getInputStream())); + PrintWriter out = new PrintWriter(new + OutputStreamWriter(s.getOutputStream())); + + out.print("GET /\r\n"); + out.flush(); + + for (String str = in.readLine(); ; str = in.readLine()) + { + if (str == null) + break; + System.out.println(str); + } + + s.close(); + System.out.println("PASSED: simple read/write test"); + } + catch(IOException e) + { + System.out.println("FAILED: simple read/write test: " + e.getMessage()); + } + + /* Connect to our server socket */ + System.out.println("Test 6: Connect to ServerSocket"); + try + { + s = new Socket("localhost", 9999); + + PrintWriter out = new PrintWriter(new + OutputStreamWriter(s.getOutputStream())); + + out.println("Hello, there server socket"); + out.print("I'm dun"); + out.flush(); + s.close(); + System.out.println("PASSED: connect to server socket"); + } + catch(Exception e) + { + System.out.println("FAILED: connect to server socket: " + e); + } + + System.out.println("Client stream socket test complete"); +} + +} + diff --git a/test/java.net/Makefile.am b/test/java.net/Makefile.am new file mode 100644 index 000000000..f57f67fbb --- /dev/null +++ b/test/java.net/Makefile.am @@ -0,0 +1,9 @@ +## Input file for automake to generate the Makefile.in used by configure + +JAVAROOT = . + +check_JAVA = ClientDatagram.java ClientSocket.java MulticastClient.java \ + MulticastServer.java ServerDatagram.java ServerSocketTest.java \ + SubSocket.java TestNameLookups.java URLTest.java + +EXTRA_DIST = BUGS runtest diff --git a/test/java.net/MulticastClient.java b/test/java.net/MulticastClient.java new file mode 100644 index 000000000..8dc18fe82 --- /dev/null +++ b/test/java.net/MulticastClient.java @@ -0,0 +1,65 @@ +/* Test Multicast Sockets */ + +import java.net.*; +import java.io.*; + +public class MulticastClient +{ + +public static void +main(String[] argv) throws IOException +{ + System.out.println("Starting multicast tests"); + System.out.println("NOTE: You need to do an 'ifconfig <interface> " + + "multicast' or this will fail on linux"); + + byte[] buf = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o','r','l','d' }; + + /* Simple Send */ + System.out.println("Test 1: Multicast send/receive test"); + try + { + InetAddress addr = InetAddress.getByName("234.0.0.1"); + + MulticastSocket s = new MulticastSocket(); + DatagramPacket p = new DatagramPacket(buf, buf.length, addr, 3333); + + s.joinGroup(addr); + s.send(p); + s.close(); + } + catch(IOException e) + { + System.out.println("FAILED: simple multicast send: " + e); + } + + /* Options */ + System.out.println("Test 2: Multicast socket options"); + try + { + InetAddress addr; + MulticastSocket s = new MulticastSocket(); + + System.out.println("TTL = " + s.getTTL()); + System.out.println("Setting TTT to 121"); + s.setTTL((byte)12); + System.out.println("TTL = " + s.getTTL()); + + InetAddress oaddr = s.getInterface(); + System.out.println("Multicast Interface = " + oaddr); + System.out.println("Setting interface to localhost"); + addr = InetAddress.getByName("198.211.138.177"); + s.setInterface(addr); + System.out.println("Multicast Interface = " + s.getInterface()); + System.out.println("Setting interface to " + oaddr); + s.setInterface(oaddr); + System.out.println("Multicast Interface = " + s.getInterface()); + } + catch(IOException e) + { + System.out.println("FAILED: multicast options: " + e); + } +} + +} + diff --git a/test/java.net/MulticastServer.java b/test/java.net/MulticastServer.java new file mode 100644 index 000000000..1a54f99eb --- /dev/null +++ b/test/java.net/MulticastServer.java @@ -0,0 +1,57 @@ +/* Mulitcast Server Socket for testing */ + +import java.io.*; +import java.net.*; + +public class MulticastServer +{ + +private MulticastSocket s; + +public static void +main(String[] argv) throws IOException +{ + MulticastServer ms = new MulticastServer(3333); + ms.run(); +} + +public +MulticastServer(int port) throws IOException +{ + s = new MulticastSocket(port); + System.out.println("Server multicast socket created"); +} + +public void +run() +{ + try + { + byte[] buf = new byte[255]; + + DatagramPacket p = new DatagramPacket(buf, buf.length); + InetAddress addr = InetAddress.getByName("234.0.0.1"); + + p.setLength(buf.length); + + System.out.println("Joining multicast group"); + s.joinGroup(addr); + System.out.print("Receiving ..."); + s.receive(p); + System.out.println(""); + s.leaveGroup(addr); + System.out.println("ServerDatagram: received " + p.getLength() + + " bytes from " + p.getAddress().getHostName() + ":" + + p.getPort()); + System.out.println("Data: " + new String(p.getData())); + + System.out.println("PASSED multicast server test"); + } + catch (IOException e) + { + System.out.println("FAILED: MulticastServer caught an exception: " + e); + } +} + +} + diff --git a/test/java.net/ServerDatagram.java b/test/java.net/ServerDatagram.java new file mode 100644 index 000000000..17ce44a27 --- /dev/null +++ b/test/java.net/ServerDatagram.java @@ -0,0 +1,52 @@ +/* Server Datagram Socket for testing */ + +import java.io.*; +import java.net.*; + +public class ServerDatagram implements Runnable +{ + +private DatagramSocket s; + +public static void +main(String[] argv) throws IOException +{ + ServerDatagram sd = new ServerDatagram(37900); + sd.run(); +} + +public +ServerDatagram(int port) throws SocketException +{ + s = new DatagramSocket(port); + System.out.println("Server datagram socket created"); +} + +public void +run() +{ + try + { + byte[] buf = new byte[65535]; + + DatagramPacket p = new DatagramPacket(buf, buf.length); + + p.setLength(buf.length); + + s.receive(p); + System.out.println("ServerDatagram: received " + p.getLength() + + " bytes from " + p.getAddress().getHostName() + ":" + + p.getPort()); + + if (p.getLength() != 65332) + throw new IOException("Incorrect data size"); + System.out.println("PASSED max values test"); + } + catch (IOException e) + { + System.out.print("FAILED: ServerDatagram caught an exception: " + e); + } +} + +} + diff --git a/test/java.net/ServerSocketTest.java b/test/java.net/ServerSocketTest.java new file mode 100644 index 000000000..f444963a9 --- /dev/null +++ b/test/java.net/ServerSocketTest.java @@ -0,0 +1,53 @@ +/* Class to test server sockets */ + +import java.io.*; +import java.net.*; + +public class ServerSocketTest extends ServerSocket +{ + +public +ServerSocketTest(int port) throws IOException +{ + super(port); +} + +public static void +main(String[] argv) +{ + System.out.println("Starting up server socket"); + + try { + ServerSocketTest ss = new ServerSocketTest(9999); + + System.out.println("Created server socket bound to port " + + ss.getLocalPort() + " on local address " + + ss.getInetAddress()); + + SubSocket s = new SubSocket(); + ss.implAccept(s); +// Socket s = ss.accept(); + + System.out.println("Got a connection from " + s.getInetAddress() + + " on port " + s.getPort()); + + BufferedReader br = new BufferedReader(new + InputStreamReader(s.getInputStream())); + + for (String str = br.readLine(); ; str = br.readLine()) + { + if (str == null) + break; + System.out.println(str); + } + s.close(); + ss.close(); + System.out.println("PASSED: server socket test"); + } + catch (Exception e) { + System.out.println("FAILED: server socket test: " + e); + } +} + +} + diff --git a/test/java.net/SubSocket.java b/test/java.net/SubSocket.java new file mode 100644 index 000000000..97da4471c --- /dev/null +++ b/test/java.net/SubSocket.java @@ -0,0 +1,6 @@ +/* Quick and dirty Socket subclass */ + +public class SubSocket extends java.net.Socket +{ +} + diff --git a/test/java.net/TestNameLookups.java b/test/java.net/TestNameLookups.java new file mode 100644 index 000000000..592d9e01f --- /dev/null +++ b/test/java.net/TestNameLookups.java @@ -0,0 +1,103 @@ +/* A class to test my java.net.InetAddress implementation */ + +import java.net.*; + +public class TestNameLookups extends Object +{ +public static void +main(String[] argv) throws UnknownHostException +{ + InetAddress addr; + + System.out.println("Started address lookup test"); + + /* Test local host */ + try + { + addr = InetAddress.getLocalHost(); + + System.out.println("The local hostname is " + addr.getHostName() + + " with an IP address of " + addr.getHostAddress()); + } + catch(UnknownHostException e) + { + System.out.println("WARNING: Can't resolve local hostname"); + } + + /* Test simple lookup by IP */ + addr = InetAddress.getByName("18.159.0.42"); + + System.out.println("Looked up IP addres 18.159.0.42 and got back a " + + "hostname of " + addr.getHostName()); + + /* Test failed reverse lookup of IP */ + addr = InetAddress.getByName("194.72.246.154"); + + System.out.println("Looked up IP addres 194.72.246.154 and got back a " + + "hostname of " + addr.getHostName()); + + /* Test mangled/invalid IP's */ + try { addr = InetAddress.getByName("122.24.1."); } + catch (UnknownHostException e) { + System.out.println("Passed bad IP test 1"); + } + + try { addr = InetAddress.getByName("122.24.52"); } + catch (UnknownHostException e) { + System.out.println("Passed bad IP test 2"); + } + + try { addr = InetAddress.getByName("122.256.52.1"); } + catch (UnknownHostException e) { + System.out.println("Passed bad IP test 3"); + } + + /* Test simple lookup by name with external info */ + addr = InetAddress.getByName("www.starnews.com"); + System.out.println("Looked up host www.starnews.com and got back an " + + "IP address of " + addr.getHostAddress()); + byte[] octets = addr.getAddress(); + System.out.println("Raw Address Bytes: octet1=" + (int)octets[0] + + " octets2=" + (int)octets[1] + " octet3=" + (int)octets[2] + + " octets4=" + (int)octets[3]); + System.out.println("toString() returned: " + addr.toString()); + System.out.println("isMulticastAddress returned: " + + addr.isMulticastAddress()); + + /* Test complex lookup */ + System.out.println("Looking up all addresses for indiana.edu ..."); + InetAddress[] list = InetAddress.getAllByName("indiana.edu"); + for (int i = 0; i < list.length; i++) + { + addr = list[i]; + + System.out.println(" Hostname: " + addr.getHostName() + + " IP Address: " + addr.getHostAddress()); + } + + /* Test equality */ + InetAddress addr1 = InetAddress.getByName("www.urbanophile.com"); + InetAddress addr2 = InetAddress.getByName("www.urbanophile.com"); + + if (addr1.equals(addr2) && addr2.equals(addr1)) + System.out.println("Passed equality test #1"); + else + System.out.println("Failed equality test #1"); + + addr1 = InetAddress.getByName("www.ac.com"); + addr2 = InetAddress.getByName("www.hungry.com"); + + if (!addr1.equals(addr2) && !addr2.equals(addr1)) + System.out.println("Passed equality test #2"); + else + System.out.println("Failed equality test #2"); + + /* Quick test to see if it looks like we're caching things */ + addr1 = InetAddress.getByName("www.urbanophile.com"); + System.out.println("Got " + addr1.getHostName() + " " + addr1.getHostAddress()); + addr2 = InetAddress.getByName("www.hungry.com"); + System.out.println("Got " + addr2.getHostName() + " " + addr2.getHostAddress()); +} + +} + diff --git a/test/java.net/URLTest.java b/test/java.net/URLTest.java new file mode 100644 index 000000000..82d0929f4 --- /dev/null +++ b/test/java.net/URLTest.java @@ -0,0 +1,150 @@ +/* Test URL's */ + +import java.net.*; +import java.io.*; + +public class URLTest +{ + +public static void +main(String argv[]) +{ + System.out.println("Starting URL tests"); + + /* Simple URL test */ + + System.out.println("Test 1: Simple URL test"); + + try + { + URL url = new URL("http", "www.fsf.org", 80, "/"); + + if (!url.getProtocol().equals("http") || + !url.getHost().equals("www.fsf.org") || + url.getPort() != 80 || + !url.getFile().equals("/")) + System.out.println("FAILED: Simple URL test"); + + System.out.println("URL is: " + url.toString()); + + URLConnection uc = url.openConnection(); + + if (uc instanceof HttpURLConnection) + System.out.println("Got the expected connection type"); + + HttpURLConnection hc = (HttpURLConnection)uc; + + hc.connect(); + + System.out.println("Dumping response headers"); + for (int i = 0; ; i++) + { + String key = hc.getHeaderFieldKey(i); + if (key == null) + break; + + System.out.println(key + ": " + hc.getHeaderField(i)); + } + + System.out.println("Dumping contents"); + BufferedReader br = new BufferedReader(new + InputStreamReader(hc.getInputStream())); + + for (String str = br.readLine(); str != null; str = br.readLine()) + System.out.println(str); + + hc.disconnect(); + + System.out.println("Content Type: " + hc.getContentType()); + System.out.println("Content Encoding: " + hc.getContentEncoding()); + System.out.println("Content Length: " + hc.getContentLength()); + System.out.println("Date: " + hc.getDate()); + System.out.println("Expiration: " + hc.getExpiration()); + System.out.println("Last Modified: " + hc.getLastModified()); + + System.out.println("PASSED: Simple URL test"); + } + catch(IOException e) + { + System.out.println("FAILED: Simple URL test: " + e); + } + + // Parsing test + System.out.println("Test 2: URL parsing test"); + try + { + URL url = new URL("http://www.urbanophile.com/arenn/trans/trans.html#mis"); + if (!url.toString().equals( + "http://www.urbanophile.com/arenn/trans/trans.html#mis")) + System.out.println("FAILED: Parse URL test: " + url.toString()); + else { + System.out.println("Parsed ok: " + url.toString()); + url = new URL("http://www.foo.com:8080/#"); + if (!url.toString().equals("http://www.foo.com:8080/#")) + System.out.println("FAILED: Parse URL test: " + url.toString()); + else { + System.out.println("Parsed ok: " + url.toString()); + url = new URL("http://www.bar.com/test:file/"); + if (!url.toString().equals("http://www.bar.com/test:file/")) + System.out.println("FAILED: Parse URL test: " + url.toString()); + else { + System.out.println("Parsed ok: " + url.toString()); + url = new URL("http://www.gnu.org"); + if (!url.toString().equals("http://www.gnu.org/")) + System.out.println("FAILED: Parse URL test: " + url.toString()); + else { + System.out.println("Parsed ok: " + url.toString()); + url = new URL("HTTP://www.fsf.org/"); + if (!url.toString().equals("http://www.fsf.org/")) + System.out.println("FAILED: Parse URL test: " + url.toString()); + else { + System.out.println("Parsed ok: " + url.toString()); + System.out.println("PASSED: URL parse test"); + } + } + } + } + } + } + catch (IOException e) + { + System.out.println("FAILED: URL parsing test: " + e); + } + + // getContent test + System.out.println("Test 3: getContent test"); + try + { + URL url = new URL("http://localhost/~arenn/services.txt"); + + Object obj = url.getContent(); + System.out.println("Object type is: " + obj.getClass().getName()); + + if (obj instanceof InputStream) + { + System.out.println("Got InputStream, so dumping contents"); + BufferedReader br = new BufferedReader(new + InputStreamReader((InputStream)obj)); + + for (String str = br.readLine(); str != null; str = br.readLine()) + System.out.println(str); + + br.close(); + } + else + { + System.out.println("FAILED: Object is not an InputStream"); + } + + System.out.println("PASSED: getContent test"); + } + catch (IOException e) + { + System.out.println("FAILED: getContent test: " + e); + } + + System.out.println("URL test complete"); +} + +} + diff --git a/test/java.net/runtest b/test/java.net/runtest new file mode 100755 index 000000000..4439aa099 --- /dev/null +++ b/test/java.net/runtest @@ -0,0 +1,32 @@ +#!/bin/sh + +echo "Running java.net tests" +echo "I assume japhar is in /usr/local/japhar" +echo "Please make sure background processes don't block on a tty write" +echo "Please do an 'ifconfig multicast' and 'ifconfig allmulti' on your" +echo "network interfaces" + +export CLASSPATH=.:/usr/local/japhar/share +export PATH=$PATH:/usr/local/japhar/bin + +echo "Executing name lookup test ..." +japhar TestNameLookups + +echo "Executing stream socket tests ..." +japhar ServerSocketTest & +japhar ClientSocket + +echo "Executing datagram socket tests ..." +japhar ServerDatagram & +japhar ClientDatagram + +echo "Executing multicast socket tests ..." +japhar MulticastServer & +japhar MulticastClient + +echo "Executing URL tests ..." +japhar URLTest + +echo "java.net Testing complete." +exit 0 + |