1 /*
   2  * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.net.www;
  27 
  28 import java.net.URL;
  29 import java.util.*;
  30 
  31 /**
  32  * A class to represent an active connection to an object
  33  * represented by a URL.
  34  * @author  James Gosling
  35  */
  36 
  37 abstract public class URLConnection extends java.net.URLConnection {
  38 
  39     /** The URL that it is connected to */
  40 
  41     private String contentType;
  42     private int contentLength = -1;
  43 
  44     protected MessageHeader properties;
  45 
  46     /** Create a URLConnection object.  These should not be created directly:
  47         instead they should be created by protocol handers in response to
  48         URL.openConnection.
  49         @param  u       The URL that this connects to.
  50      */
  51     public URLConnection (URL u) {
  52         super(u);
  53         properties = new MessageHeader();
  54     }
  55 
  56     /** Call this routine to get the property list for this object.
  57      * Properties (like content-type) that have explicit getXX() methods
  58      * associated with them should be accessed using those methods.  */
  59     public MessageHeader getProperties() {
  60         return properties;
  61     }
  62 
  63     /** Call this routine to set the property list for this object. */
  64     public void setProperties(MessageHeader properties) {
  65         this.properties = properties;
  66     }
  67 
  68     public void setRequestProperty(String key, String value) {
  69         if(connected)
  70             throw new IllegalAccessError("Already connected");
  71         if (key == null)
  72             throw new NullPointerException ("key cannot be null");
  73         properties.set(key, value);
  74     }
  75 
  76     /**
  77      * The following three methods addRequestProperty, getRequestProperty,
  78      * and getRequestProperties were copied from the superclass implementation
  79      * before it was changed by CR:6230836, to maintain backward compatibility.
  80      */
  81     public void addRequestProperty(String key, String value) {
  82         if (connected)
  83             throw new IllegalStateException("Already connected");
  84         if (key == null)
  85             throw new NullPointerException ("key is null");
  86     }
  87 
  88     public String getRequestProperty(String key) {
  89         if (connected)
  90             throw new IllegalStateException("Already connected");
  91         return null;
  92     }
  93 
  94     public Map<String,List<String>> getRequestProperties() {
  95         if (connected)
  96             throw new IllegalStateException("Already connected");
  97         return Collections.emptyMap();
  98     }
  99 
 100     public String getHeaderField(String name) {
 101         try {
 102             getInputStream();
 103         } catch (Exception e) {
 104             return null;
 105         }
 106         return properties == null ? null : properties.findValue(name);
 107     }
 108 
 109     /**
 110      * Return the key for the nth header field. Returns null if
 111      * there are fewer than n fields.  This can be used to iterate
 112      * through all the headers in the message.
 113      */
 114     public String getHeaderFieldKey(int n) {
 115         try {
 116             getInputStream();
 117         } catch (Exception e) {
 118             return null;
 119         }
 120         MessageHeader props = properties;
 121         return props == null ? null : props.getKey(n);
 122     }
 123 
 124     /**
 125      * Return the value for the nth header field. Returns null if
 126      * there are fewer than n fields.  This can be used in conjunction
 127      * with getHeaderFieldKey to iterate through all the headers in the message.
 128      */
 129     public String getHeaderField(int n) {
 130         try {
 131             getInputStream();
 132         } catch (Exception e) {
 133             return null;
 134         }
 135         MessageHeader props = properties;
 136         return props == null ? null : props.getValue(n);
 137     }
 138 
 139     /** Call this routine to get the content-type associated with this
 140      * object.
 141      */
 142     public String getContentType() {
 143         if (contentType == null)
 144             contentType = getHeaderField("content-type");
 145         if (contentType == null) {
 146             String ct = null;
 147             try {
 148                 ct = guessContentTypeFromStream(getInputStream());
 149             } catch(java.io.IOException e) {
 150             }
 151             String ce = properties.findValue("content-encoding");
 152             if (ct == null) {
 153                 ct = properties.findValue("content-type");
 154 
 155                 if (ct == null)
 156                     if (url.getFile().endsWith("/"))
 157                         ct = "text/html";
 158                     else
 159                         ct = guessContentTypeFromName(url.getFile());
 160             }
 161 
 162             /*
 163              * If the Mime header had a Content-encoding field and its value
 164              * was not one of the values that essentially indicate no
 165              * encoding, we force the content type to be unknown. This will
 166              * cause a save dialog to be presented to the user.  It is not
 167              * ideal but is better than what we were previously doing, namely
 168              * bringing up an image tool for compressed tar files.
 169              */
 170 
 171             if (ct == null || ce != null &&
 172                     !(ce.equalsIgnoreCase("7bit")
 173                       || ce.equalsIgnoreCase("8bit")
 174                       || ce.equalsIgnoreCase("binary")))
 175                 ct = "content/unknown";
 176             setContentType(ct);
 177         }
 178         return contentType;
 179     }
 180 
 181     /**
 182      * Set the content type of this URL to a specific value.
 183      * @param   type    The content type to use.  One of the
 184      *                  content_* static variables in this
 185      *                  class should be used.
 186      *                  eg. setType(URL.content_html);
 187      */
 188     public void setContentType(String type) {
 189         contentType = type;
 190         properties.set("content-type", type);
 191     }
 192 
 193     /** Call this routine to get the content-length associated with this
 194      * object.
 195      */
 196     public int getContentLength() {
 197         try {
 198             getInputStream();
 199         } catch (Exception e) {
 200             return -1;
 201         }
 202         int l = contentLength;
 203         if (l < 0) {
 204             try {
 205                 l = Integer.parseInt(properties.findValue("content-length"));
 206                 setContentLength(l);
 207             } catch(Exception e) {
 208             }
 209         }
 210         return l;
 211     }
 212 
 213     /** Call this routine to set the content-length associated with this
 214      * object.
 215      */
 216     protected void setContentLength(int length) {
 217         contentLength = length;
 218         properties.set("content-length", String.valueOf(length));
 219     }
 220 
 221     /**
 222      * Returns true if the data associated with this URL can be cached.
 223      */
 224     public boolean canCache() {
 225         return url.getFile().indexOf('?') < 0   /* && url.postData == null
 226                 REMIND */ ;
 227     }
 228 
 229     /**
 230      * Call this to close the connection and flush any remaining data.
 231      * Overriders must remember to call super.close()
 232      */
 233     public void close() {
 234         url = null;
 235     }
 236 
 237     private static Map proxiedHosts = new HashMap();
 238 
 239     public synchronized static void setProxiedHost(String host) {
 240         proxiedHosts.put(host.toLowerCase(), null);
 241     }
 242 
 243     public synchronized static boolean isProxiedHost(String host) {
 244         return proxiedHosts.containsKey(host.toLowerCase());
 245     }
 246 }