home *** CD-ROM | disk | FTP | other *** search
/ Chip 1997 October / Chip_1997-10_cd.bin / tema / sybase / powerj / java.z / URLConnection.java < prev    next >
Text File  |  1996-05-03  |  22KB  |  631 lines

  1. /*
  2.  * @(#)URLConnection.java    1.22 96/04/08
  3.  * 
  4.  * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify, and distribute this software and its
  7.  * documentation for NON-COMMERCIAL purposes and without fee is hereby
  8.  * granted provided that this copyright notice appears in all copies. Please
  9.  * refer to the file "copyright.html" for further important copyright and
  10.  * licensing information.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
  15.  * OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
  16.  * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
  17.  * ITS DERIVATIVES.
  18.  */
  19.  
  20. package java.net;
  21.  
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.OutputStream;
  25. import java.util.Hashtable;
  26. import java.util.Date;
  27. import java.util.StringTokenizer;
  28.  
  29. /**
  30.  * A class to represent an active connection to an object
  31.  * represented by a URL. It is an abstract class that must be
  32.  * subclassed to implement a connection.
  33.  *
  34.  * @version     1.22, 08 Apr 1996
  35.  * @author  James Gosling
  36.  */
  37. abstract public class URLConnection {
  38.     protected URL url;
  39.  
  40.     protected boolean doInput = true;
  41.     protected boolean doOutput = false;
  42.     private static boolean defaultAllowUserInteraction = false;
  43.     protected boolean allowUserInteraction = defaultAllowUserInteraction;
  44.     private static boolean defaultUseCaches = true;
  45.     protected boolean useCaches = defaultUseCaches;
  46.     protected long ifModifiedSince = 0;
  47.  
  48.     protected boolean connected = false;
  49.  
  50.     /**
  51.      * URLConnection objects go through two phases: first they are
  52.      * created, then they are connected.  After being created, and
  53.      * before being connected, various options can be specified
  54.      * (eg. doInput, UseCaches, ...).  After connecting, it is an
  55.      * Error to try to set them.  Operations that depend on being
  56.      * connected, like getContentLength, will implicitly perform the
  57.      * connection if necessary.  Connecting when already connected
  58.      * does nothing.
  59.      */
  60.     abstract public void connect() throws IOException;
  61.  
  62.  
  63.     /**
  64.      * Constructs a URL connection to the specified URL.
  65.      * @param url the specified URL
  66.      */
  67.     protected URLConnection (URL url) {
  68.     this.url = url;
  69.     }
  70.  
  71.     /**
  72.      * Gets the URL for this connection.
  73.      */
  74.     public URL getURL() {
  75.     return url;
  76.     }
  77.  
  78.     /**
  79.      * Gets the content length. Returns -1 if not known.
  80.      */
  81.     public int getContentLength() {
  82.     return getHeaderFieldInt("content-length", -1);
  83.     }
  84.  
  85.     /**
  86.      * Gets the content type. Returns null if not known.
  87.      */
  88.     public String getContentType() {
  89.     return getHeaderField("content-type");
  90.     }
  91.  
  92.     /**
  93.      * Gets the content encoding. Returns null if not known.
  94.      */
  95.     public String getContentEncoding() {
  96.     return getHeaderField("content-encoding");
  97.     }
  98.  
  99.     /**
  100.      * Gets the expriation date of the object. Returns 0 if not known.
  101.      */
  102.     public long getExpiration() {
  103.     return getHeaderFieldDate("expires", 0);
  104.     }
  105.  
  106.     /**
  107.      * Gets the sending date of the object. Returns 0 if not known.
  108.      */
  109.     public long getDate() {
  110.     return getHeaderFieldDate("date", 0);
  111.     }
  112.  
  113.     /**
  114.      * Gets the last modified date of the object. Returns 0 if not known.
  115.      */
  116.     public long getLastModified() {
  117.     return getHeaderFieldDate("last-modified", 0);
  118.     }
  119.  
  120.     /**
  121.      * Gets a header field by name. Returns null if not known.
  122.      * @param name the name of the header field
  123.      */
  124.     public String getHeaderField(String name) {
  125.     return null;
  126.     }
  127.  
  128.     /**
  129.      * Gets a header field by name. Returns null if not known.
  130.      * The field is parsed as an integer.  This form of
  131.      * getHeaderField exists because some connection types
  132.      * (e.g. http-ng) have pre-parsed headers and  this allows them
  133.      * to override this method and short-circuit the parsing.
  134.      * @param name the name of the header field
  135.      * @param Default the value to return if the field is missing
  136.      *    or malformed.
  137.      */
  138.     public int getHeaderFieldInt(String name, int Default) {
  139.     try {
  140.         return Integer.parseInt(getHeaderField(name));
  141.     } catch(Throwable t) {}
  142.     return Default;
  143.     }
  144.  
  145.     /**
  146.      * Gets a header field by name. Returns null if not known.
  147.      * The field will be parsed as a date.  This form of
  148.      * getHeaderField exists because some connection types
  149.      * (eg. http-ng) have pre-parsed headers. This allows them
  150.      * to override this method and short-circuit the parsing.
  151.      * @param name the name of the header field
  152.      * @param Default the value to return if the field is missing
  153.      *    or malformed.
  154.      */
  155.     public long getHeaderFieldDate(String name, long Default) {
  156.     try {
  157.         return Date.parse(getHeaderField(name));
  158.     } catch(Throwable t) {}
  159.     return Default;
  160.     }
  161.  
  162.     /**
  163.      * Returns the key for the nth header field. Returns null if
  164.      * there are fewer than n fields.  This can be used to iterate
  165.      * through all the headers in the message.
  166.      */
  167.     public String getHeaderFieldKey(int n) {
  168.     return null;
  169.     }
  170.  
  171.     /**
  172.      * Returns the value for the nth header field. Returns null if
  173.      * there are fewer than n fields.  This can be used in conjunction
  174.      * with getHeaderFieldKey to iterate through all the headers in the message.
  175.      */
  176.     public String getHeaderField(int n) {
  177.     return null;
  178.     }
  179.  
  180.     /**
  181.      * Gets the object referred to by this URL.  For example, if it
  182.      * refers to an image the object will be some subclass of
  183.      * Image.  The instanceof operator should be used to determine
  184.      * what kind of object was returned.
  185.      * @return    the object that was fetched.
  186.      * @exception UnknownServiceException If the protocol does not
  187.      * support content.
  188.      */
  189.     public Object getContent() throws IOException {
  190.     return getContentHandler().getContent(this);
  191.     }
  192.  
  193.     /**
  194.      * Calls this routine to get an InputStream that reads from the object.
  195.      * Protocol implementors should use this if appropriate.
  196.      * @exception UnknownServiceException If the protocol does not
  197.      * support input.
  198.      */
  199.     public InputStream getInputStream() throws IOException {
  200.     throw new UnknownServiceException("protocol doesn't support input");
  201.     }
  202.  
  203.     /**
  204.      * Calls this routine to get an OutputStream that writes to the object.
  205.      * Protocol implementors should use this if appropriate.
  206.      * @exception UnknownServiceException If the protocol does not
  207.      * support output.
  208.      */
  209.     public OutputStream getOutputStream() throws IOException {
  210.     throw new UnknownServiceException("protocol doesn't support output");
  211.     }
  212.  
  213.     /**
  214.      * Returns the String representation of the URL connection.
  215.      */
  216.     public String toString() {
  217.     return this.getClass().getName() + ":" + url;
  218.     }
  219.  
  220.  
  221.     /** A URL connection can be used for input and/or output.  Set the DoInput
  222.         flag to true if you intend to use the URL connection for input,
  223.         false if not.  The default is true unless DoOutput is explicitly
  224.         set to true, in which case DoInput defaults to false.  */
  225.     public void setDoInput(boolean doinput) {
  226.     if (connected)
  227.         throw new IllegalAccessError("Already connected");
  228.     doInput = doinput;
  229.     }
  230.     public boolean getDoInput() {
  231.     return doInput;
  232.     }
  233.  
  234.     /** A URL connection can be used for input and/or output.  Set the DoOutput
  235.         flag to true if you intend to use the URL connection for output,
  236.         false if not.  The default is false. */
  237.     public void setDoOutput(boolean dooutput) {
  238.     if (connected)
  239.         throw new IllegalAccessError("Already connected");
  240.     doOutput = dooutput;
  241.     }
  242.     public boolean getDoOutput() {
  243.     return doOutput;
  244.     }
  245.  
  246.     /** Some URL connections occasionally need to to interactions with the
  247.         user.  For example, the http protocol may need to pop up an authentication
  248.         dialog.  But this is only appropriate if the application is running
  249.         in a context where there <i>is</i> a user.  The allowUserInteraction
  250.         flag allows these interactions when true.  When it is false, they are
  251.         not allowed and an exception is tossed. The default value can be
  252.         set/gotten using setDefaultAllowUserInteraction, which defaults to false. */
  253.     public void setAllowUserInteraction(boolean allowuserinteraction) {
  254.     if (connected)
  255.         throw new IllegalAccessError("Already connected");
  256.     allowUserInteraction = allowuserinteraction;
  257.     }
  258.     public boolean getAllowUserInteraction() {
  259.     return allowUserInteraction;
  260.     }
  261.  
  262.     /** Sets/gets the default value of the allowUserInteraction flag.  This default
  263.         is "sticky", being a part of the static state of all URLConnections.  This
  264.         flag applies to the next, and all following URLConnections that are created. */
  265.     public static void setDefaultAllowUserInteraction(boolean defaultallowuserinteraction) {
  266.     defaultAllowUserInteraction = defaultallowuserinteraction;
  267.     }
  268.     public static boolean getDefaultAllowUserInteraction() {
  269.     return defaultAllowUserInteraction;
  270.     }
  271.  
  272.     /** Some protocols do caching of documents.  Occasionally, it is important to be
  273.         able to "tunnel through" and ignore the caches (e.g. the "reload" button in
  274.         a browser).  If the UseCaches flag on a connection is true, the connection is
  275.         allowed to use whatever caches it can.  If false, caches are to be ignored.
  276.         The default value comes from DefaultUseCaches, which defaults to true. */
  277.     public void setUseCaches(boolean usecaches) {
  278.     if (connected)
  279.         throw new IllegalAccessError("Already connected");
  280.     useCaches = usecaches;
  281.     }
  282.     public boolean getUseCaches() {
  283.     return useCaches;
  284.     }
  285.  
  286.     /** Some protocols support skipping fetching unless the object is newer than some amount of time.
  287.     The ifModifiedSince field may be set/gotten to define this time. */
  288.     public void setIfModifiedSince(long ifmodifiedsince) {
  289.     if (connected)
  290.         throw new IllegalAccessError("Already connected");
  291.     ifModifiedSince = ifmodifiedsince;
  292.     }
  293.     public long getIfModifiedSince() {
  294.     return ifModifiedSince;
  295.     }
  296.  
  297.     /** Sets/gets the default value of the UseCaches flag.  This default
  298.         is "sticky", being a part of the static state of all URLConnections.  This
  299.         flag applies to the next, and all following, URLConnections that are created. */
  300.     public boolean getDefaultUseCaches() {
  301.     return defaultUseCaches;
  302.     }
  303.     public void setDefaultUseCaches(boolean defaultusecaches) {
  304.     defaultUseCaches = defaultusecaches;
  305.     }
  306.  
  307.     /**
  308.      * Sets/gets a general request property.
  309.      * @param key The keyword by which the request is known (eg "accept")
  310.      * @param value The value associated with it.
  311.      */
  312.     public void setRequestProperty(String key, String value) {
  313.     if (connected)
  314.         throw new IllegalAccessError("Already connected");
  315.     }
  316.     public String getRequestProperty(String key) {
  317.     if (connected)
  318.         throw new IllegalAccessError("Already connected");
  319.     return null;
  320.     }
  321.  
  322.     /**
  323.      * Sets/gets the default value of a general request property. When a
  324.      * URLConnection is created, it is initialized with these properties.
  325.      * @param key The keyword by which the request is known (eg "accept")
  326.      * @param value The value associated with it.
  327.      */
  328.     public static void setDefaultRequestProperty(String key, String value) {
  329.     }
  330.     public static String getDefaultRequestProperty(String key) {
  331.     return null;
  332.     }
  333.  
  334.     /**
  335.      * The ContentHandler factory.
  336.      */
  337.     static ContentHandlerFactory factory;
  338.  
  339.     /**
  340.      * Sets the ContentHandler factory.
  341.      * @param fac the desired factory
  342.      * @exception Error If the factory has already been defined.
  343.      */
  344.     public static synchronized void setContentHandlerFactory(ContentHandlerFactory fac) {
  345.     if (factory != null) {
  346.         throw new Error("factory already defined");
  347.     }
  348.     SecurityManager security = System.getSecurityManager();
  349.     if (security != null) {
  350.         security.checkSetFactory();
  351.     }
  352.     factory = fac;
  353.     }
  354.  
  355.     private static Hashtable handlers = new Hashtable();
  356.     private static ContentHandler UnknownContentHandlerP = new UnknownContentHandler();
  357.  
  358.     /**
  359.      * Gets the Content Handler appropriate for this connection.
  360.      * @param connection the connection to use.
  361.      */
  362.     synchronized ContentHandler getContentHandler()
  363.     throws UnknownServiceException
  364.     {
  365.     String contentType = getContentType();
  366.     ContentHandler handler = null;
  367.     if (contentType == null)
  368.         throw new UnknownServiceException("no content-type");
  369.     try {
  370.         handler = (ContentHandler) handlers.get(contentType);
  371.         if (handler != null)
  372.         return handler;
  373.     } catch(Exception e) {
  374.     }
  375.     if (factory != null)
  376.         handler = factory.createContentHandler(contentType);
  377.     if (handler == null) {
  378.         try {
  379.         handler = lookupContentHandlerClassFor(contentType);
  380.         } catch(Exception e) {
  381.         e.printStackTrace();
  382.         handler = UnknownContentHandlerP;
  383.         }
  384.         handlers.put(contentType, handler);
  385.     }
  386.     return handler;
  387.     }
  388.  
  389.     private static final String contentClassPrefix = "sun.net.www.content";
  390.     private static final String contentPathProp = "java.content.handler.pkgs";
  391.  
  392.     /**
  393.      * Looks for a content handler in a user-defineable set of places.
  394.      * By default it looks in sun.net.www.content, but users can define a 
  395.      * vertical-bar delimited set of class prefixes to search through in 
  396.      * addition by defining the java.content.handler.pkgs property.
  397.      * The class name must be of the form:
  398.      * <pre>
  399.      *     {package-prefix}.{major}.{minor}
  400.      * e.g.
  401.      *     YoyoDyne.experimental.text.plain
  402.      * </pre>
  403.      */
  404.     private ContentHandler lookupContentHandlerClassFor(String contentType)
  405.     throws InstantiationException, IllegalAccessException, ClassNotFoundException {
  406.     String contentHandlerClassName = typeToPackageName(contentType);
  407.  
  408.     String contentHandlerPkgPrefixes = getContentHandlerPkgPrefixes();
  409.  
  410.     StringTokenizer packagePrefixIter =
  411.         new StringTokenizer(contentHandlerPkgPrefixes, "|");
  412.     
  413.     while (packagePrefixIter.hasMoreTokens()) {
  414.         String packagePrefix = packagePrefixIter.nextToken().trim();
  415.  
  416.         try {
  417.         String name = packagePrefix + "." + contentHandlerClassName;
  418.         ContentHandler handler =
  419.             (ContentHandler) Class.forName(name).newInstance();
  420.         return handler;
  421.         } catch(Exception e) {
  422.         }
  423.     }
  424.     
  425.     return UnknownContentHandlerP;
  426.     }
  427.  
  428.     /**
  429.      * Utility function to map a MIME content type into an equivalent
  430.      * pair of class name components.  For example: "text/html" would
  431.      * be returned as "text.html"
  432.      */
  433.     private String typeToPackageName(String contentType) {
  434.     int len = contentType.length();
  435.     char nm[] = new char[len];
  436.     contentType.getChars(0, len, nm, 0);
  437.     for (int i = 0; i < len; i++) {
  438.         char c = nm[i];
  439.         if (c == '/') {
  440.         nm[i] = '.';
  441.         } else if (!('A' <= c && c <= 'Z' ||
  442.                'a' <= c && c <= 'z' ||
  443.                '0' <= c && c <= '9')) {
  444.         nm[i] = '_';
  445.         }
  446.     }
  447.     return new String(nm);
  448.     }
  449.  
  450.  
  451.     /**
  452.      * Returns a vertical bar separated list of package prefixes for potential
  453.      * content handlers.  Tries to get the java.content.handler.pkgs property
  454.      * to use as a set of package prefixes to search.  Whether or not
  455.      * that property has been defined, the sun.net.www.content is always
  456.      * the last one on the returned package list.
  457.      */
  458.     private String getContentHandlerPkgPrefixes() {
  459.     String packagePrefixList = System.getProperty(contentPathProp, "");
  460.     if (packagePrefixList != "") {
  461.         packagePrefixList += "|";
  462.     }
  463.     
  464.     return packagePrefixList + contentClassPrefix;
  465.     }
  466.  
  467.     /**
  468.      * A useful utility routine that tries to guess the content-type
  469.      * of an object based upon its extension.
  470.      */
  471.     protected static String guessContentTypeFromName(String fname) {
  472.     String ext = "";
  473.     int i = fname.lastIndexOf('#');
  474.  
  475.     if (i != -1)
  476.         fname = fname.substring(0, i - 1);
  477.     i = fname.lastIndexOf('.');
  478.     i = Math.max(i, fname.lastIndexOf('/'));
  479.     i = Math.max(i, fname.lastIndexOf('?'));
  480.  
  481.     if (i != -1 && fname.charAt(i) == '.') {
  482.         ext = fname.substring(i).toLowerCase();
  483.     }
  484.     return (String) extension_map.get(ext);
  485.     }
  486.  
  487.     static Hashtable extension_map = new Hashtable();
  488.  
  489.     static {
  490.     setSuffix("", "content/unknown");
  491.     setSuffix(".uu", "application/octet-stream");
  492.     setSuffix(".saveme", "application/octet-stream");
  493.     setSuffix(".dump", "application/octet-stream");
  494.     setSuffix(".hqx", "application/octet-stream");
  495.     setSuffix(".arc", "application/octet-stream");
  496.     setSuffix(".o", "application/octet-stream");
  497.     setSuffix(".a", "application/octet-stream");
  498.     setSuffix(".bin", "application/octet-stream");
  499.     setSuffix(".exe", "application/octet-stream");
  500.     /* Temporary only. */
  501.     setSuffix(".z", "application/octet-stream");
  502.     setSuffix(".gz", "application/octet-stream");
  503.  
  504.     setSuffix(".oda", "application/oda");
  505.     setSuffix(".pdf", "application/pdf");
  506.     setSuffix(".eps", "application/postscript");
  507.     setSuffix(".ai", "application/postscript");
  508.     setSuffix(".ps", "application/postscript");
  509.     setSuffix(".rtf", "application/rtf");
  510.     setSuffix(".dvi", "application/x-dvi");
  511.     setSuffix(".hdf", "application/x-hdf");
  512.     setSuffix(".latex", "application/x-latex");
  513.     setSuffix(".cdf", "application/x-netcdf");
  514.     setSuffix(".nc", "application/x-netcdf");
  515.     setSuffix(".tex", "application/x-tex");
  516.     setSuffix(".texinfo", "application/x-texinfo");
  517.     setSuffix(".texi", "application/x-texinfo");
  518.     setSuffix(".t", "application/x-troff");
  519.     setSuffix(".tr", "application/x-troff");
  520.     setSuffix(".roff", "application/x-troff");
  521.     setSuffix(".man", "application/x-troff-man");
  522.     setSuffix(".me", "application/x-troff-me");
  523.     setSuffix(".ms", "application/x-troff-ms");
  524.     setSuffix(".src", "application/x-wais-source");
  525.     setSuffix(".wsrc", "application/x-wais-source");
  526.     setSuffix(".zip", "application/zip");
  527.     setSuffix(".bcpio", "application/x-bcpio");
  528.     setSuffix(".cpio", "application/x-cpio");
  529.     setSuffix(".gtar", "application/x-gtar");
  530.     setSuffix(".shar", "application/x-shar");
  531.     setSuffix(".sh", "application/x-shar");
  532.     setSuffix(".sv4cpio", "application/x-sv4cpio");
  533.     setSuffix(".sv4crc", "application/x-sv4crc");
  534.     setSuffix(".tar", "application/x-tar");
  535.     setSuffix(".ustar", "application/x-ustar");
  536.     setSuffix(".snd", "audio/basic");
  537.     setSuffix(".au", "audio/basic");
  538.     setSuffix(".aifc", "audio/x-aiff");
  539.     setSuffix(".aif", "audio/x-aiff");
  540.     setSuffix(".aiff", "audio/x-aiff");
  541.     setSuffix(".wav", "audio/x-wav");
  542.     setSuffix(".gif", "image/gif");
  543.     setSuffix(".ief", "image/ief");
  544.     setSuffix(".jfif", "image/jpeg");
  545.     setSuffix(".jfif-tbnl", "image/jpeg");
  546.     setSuffix(".jpe", "image/jpeg");
  547.     setSuffix(".jpg", "image/jpeg");
  548.     setSuffix(".jpeg", "image/jpeg");
  549.     setSuffix(".tif", "image/tiff");
  550.     setSuffix(".tiff", "image/tiff");
  551.     setSuffix(".ras", "image/x-cmu-rast");
  552.     setSuffix(".pnm", "image/x-portable-anymap");
  553.     setSuffix(".pbm", "image/x-portable-bitmap");
  554.     setSuffix(".pgm", "image/x-portable-graymap");
  555.     setSuffix(".ppm", "image/x-portable-pixmap");
  556.     setSuffix(".rgb", "image/x-rgb");
  557.     setSuffix(".xbm", "image/x-xbitmap");
  558.     setSuffix(".xpm", "image/x-xpixmap");
  559.     setSuffix(".xwd", "image/x-xwindowdump");
  560.     setSuffix(".htm", "text/html");
  561.     setSuffix(".html", "text/html");
  562.     setSuffix(".text", "text/plain");
  563.     setSuffix(".c", "text/plain");
  564.     setSuffix(".cc", "text/plain");
  565.     setSuffix(".c++", "text/plain");
  566.     setSuffix(".h", "text/plain");
  567.     setSuffix(".pl", "text/plain");
  568.     setSuffix(".txt", "text/plain");
  569.     setSuffix(".java", "text/plain");
  570.     setSuffix(".rtx", "application/rtf");
  571.     setSuffix(".tsv", "text/tab-separated-values");
  572.     setSuffix(".etx", "text/x-setext");
  573.     setSuffix(".mpg", "video/mpeg");
  574.     setSuffix(".mpe", "video/mpeg");
  575.     setSuffix(".mpeg", "video/mpeg");
  576.     setSuffix(".mov", "video/quicktime");
  577.     setSuffix(".qt", "video/quicktime");
  578.     setSuffix(".avi", "application/x-troff-msvideo");
  579.     setSuffix(".movie", "video/x-sgi-movie");
  580.     setSuffix(".mv", "video/x-sgi-movie");
  581.     setSuffix(".mime", "message/rfc822");
  582.     }
  583.  
  584.     static private void setSuffix(String ext, String ct) {
  585.     extension_map.put(ext, ct);
  586.     }
  587.  
  588.     /**
  589.      * This method is used to check for files that have some type
  590.      * that can be determined by inspection.  The bytes at the beginning
  591.      * of the file are examined loosely.  In an ideal world, this routine
  592.      * would not be needed, but in a world where http servers lie
  593.      * about content-types and extensions are often non-standard,
  594.      * direct inspection of the bytes can make the system more robust.
  595.      * The stream must support marks (e.g. have a BufferedInputStream
  596.      * somewhere).
  597.      */
  598.     static protected String guessContentTypeFromStream(InputStream is) throws IOException
  599.     {
  600.     is.mark(10);
  601.     int c1 = is.read();
  602.     int c2 = is.read();
  603.     int c3 = is.read();
  604.     int c4 = is.read();
  605.     int c5 = is.read();
  606.     int c6 = is.read();
  607.     is.reset();
  608.     if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8')
  609.         return "image/gif";
  610.     if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f')
  611.         return "image/x-bitmap";
  612.     if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' && c5 == 'M' && c6 == '2')
  613.         return "image/x-pixmap";
  614.     if (c1 == '<')
  615.         if (c2 == '!'
  616.             || (c6 == '>'
  617.             && (c2 == 'h' && (c3 == 't' && c4 == 'm' && c5 == 'l' ||
  618.                       c3 == 'e' && c4 == 'a' && c5 == 'd')
  619.               || c2 == 'b' && c3 == 'o' && c4 == 'd' && c5 == 'y')))
  620.         return "text/html";
  621.     return null;
  622.     }
  623.  
  624. }
  625.  
  626. class UnknownContentHandler extends ContentHandler {
  627.     public Object getContent(URLConnection uc) throws IOException {
  628.     return uc.getInputStream();
  629.     }
  630. }
  631.