View Javadoc

1   /*
2    * @(#)HttpURLConnection.java				0.3-3 06/05/2001
3    *
4    *  This file is part of the HTTPClient package
5    *  Copyright (C) 1996-2001 Ronald Tschalär
6    *
7    *  This library is free software; you can redistribute it and/or
8    *  modify it under the terms of the GNU Lesser General Public
9    *  License as published by the Free Software Foundation; either
10   *  version 2 of the License, or (at your option) any later version.
11   *
12   *  This library is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   *  Lesser General Public License for more details.
16   *
17   *  You should have received a copy of the GNU Lesser General Public
18   *  License along with this library; if not, write to the Free
19   *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20   *  MA 02111-1307, USA
21   *
22   *  For questions, suggestions, bug-reports, enhancement-requests etc.
23   *  I may be contacted at:
24   *
25   *  ronald@innovation.ch
26   *
27   *  The HTTPClient's home page is located at:
28   *
29   *  http://www.innovation.ch/java/HTTPClient/ 
30   *
31   * This file contains modifications for use with "The Grinder"
32   * (http://grinder.sourceforge.net) under the terms of the LGPL. They
33   * are marked below with the comment "GRINDER MODIFICATION".
34   */
35  
36  package HTTPClient;
37  
38  import java.net.URL;
39  import java.net.ProtocolException;
40  import java.io.IOException;
41  import java.io.InputStream;
42  import java.io.OutputStream;
43  import java.io.ByteArrayOutputStream;
44  import java.util.Date;
45  import java.util.Hashtable;
46  import java.util.Enumeration;
47  
48  
49  /**
50   * This class is a wrapper around HTTPConnection providing the interface
51   * defined by java.net.URLConnection and java.net.HttpURLConnection.
52   *
53   * <P>This class can be used to replace the HttpClient in the JDK with this
54   * HTTPClient by defining the property
55   * <code>java.protocol.handler.pkgs=HTTPClient</code>.
56   *
57   * <P>One difference between Sun's HttpClient and this one is that this
58   * one will provide you with a real output stream if possible. This leads
59   * to two changes: you should set the request property "Content-Length",
60   * if possible, before invoking getOutputStream(); and in many cases
61   * getOutputStream() implies connect(). This should be transparent, though,
62   * apart from the fact that you can't change any headers or other settings
63   * anymore once you've gotten the output stream.
64   * So, for large data do:
65   * <PRE>
66   *   HttpURLConnection con = (HttpURLConnection) url.openConnection();
67   *
68   *   con.setDoOutput(true);
69   *   con.setRequestProperty("Content-Length", ...);
70   *   OutputStream out = con.getOutputStream();
71   *
72   *   out.write(...);
73   *   out.close();
74   *
75   *   if (con.getResponseCode() != 200)
76   *       ...
77   * </PRE>
78   *
79   * <P>The HTTPClient will send the request data using the chunked transfer
80   * encoding when no Content-Length is specified and the server is HTTP/1.1
81   * compatible. Because cgi-scripts can't usually handle this, you may
82   * experience problems trying to POST data. For this reason, whenever
83   * the Content-Type is application/x-www-form-urlencoded getOutputStream()
84   * will buffer the data before sending it so as prevent chunking. If you
85   * are sending requests with a different Content-Type and are experiencing
86   * problems then you may want to try setting the system property
87   * <var>HTTPClient.dontChunkRequests</var> to <var>true</var> (this needs
88   * to be done either on the command line or somewhere in the code before
89   * the first URLConnection.openConnection() is invoked).
90   *
91   * <P>A second potential incompatibility is that the HTTPClient aggresively
92   * resuses connections, and can do so more often that Sun's client. This
93   * can cause problems if you send multiple requests, and the first one has
94   * a long response. In this case (assuming the server allows the connection
95   * to be used for multiple requests) the responses to second, third, etc
96   * request won't be received until the first response has been completely
97   * read. With Sun's client on the other hand you may not experience this,
98   * as it may not be able to keep the connection open and there may create
99   * multiple connections for the requests. This allows the responses to the
100  * second, third, etc requests to be read before the first response has
101  * completed. <strong>Note:</strong> whether this will happen depends on
102  * details of the resource being requested and the server. In many cases
103  * the HTTPClient and Sun's client will exhibit the same behaviour. Also,
104  * applications which depend on being able to read the second response
105  * before the first one has completed must be considered broken, because
106  * A) this behaviour cannot be relied upon even in Sun's current client,
107  * and B) Sun's implementation will exhibit the same problem if they ever
108  * switch to HTTP/1.1.
109  *
110  * @version	0.3-3  06/05/2001
111  * @author	Ronald Tschalär
112  * @since	V0.3
113  */
114 public class HttpURLConnection extends java.net.HttpURLConnection
115 {
116     /** the cache of HTTPConnections */
117     protected static Hashtable  connections = new Hashtable();
118 
119     /** the current connection */
120     protected HTTPConnection    con;
121 
122     /** the cached url.toString() */
123     private String            urlString;
124 
125     /** the resource */
126     private String            resource;
127 
128     /** the current method */
129     private String            method;
130 
131     /** has the current method been set via setRequestMethod()? */
132     private boolean           method_set;
133 
134     /** the default request headers */
135     private static NVPair[]   default_headers = new NVPair[0];
136 
137     /** the request headers */
138     private NVPair[]          headers;
139 
140     /** the response */
141     protected HTTPResponse      resp;
142 
143     /** is the redirection module activated for this instance? */
144     private boolean           do_redir;
145 
146     /** the RedirectionModule class */
147     private static Class      redir_mod;
148 
149     /** the output stream used for POST and PUT */
150     private OutputStream      output_stream;
151 
152 
153     static
154     {
155 	// The default allowUserAction in java.net.URLConnection is
156 	// false.
157 	try
158 	{
159 	    if (Boolean.getBoolean("HTTPClient.HttpURLConnection.AllowUI"))
160 		setDefaultAllowUserInteraction(true);
161 	}
162 	catch (SecurityException se)
163 	    { }
164 
165 	// get the RedirectionModule class
166 	try
167 	    { redir_mod = Class.forName("HTTPClient.RedirectionModule"); }
168 	catch (ClassNotFoundException cnfe)
169 	    { throw new NoClassDefFoundError(cnfe.getMessage()); }
170 
171 	// Set the User-Agent if the http.agent property is set
172 	try
173 	{
174 	    String agent = System.getProperty("http.agent");
175 	    if (agent != null)
176 		setDefaultRequestProperty("User-Agent", agent);
177 	}
178 	catch (SecurityException se)
179 	    { }
180     }
181 
182 
183     // Constructors
184 
185     private static String non_proxy_hosts = "";
186     private static String proxy_host = "";
187     private static int    proxy_port = -1;
188 
189     /**
190      * Construct a connection to the specified url. A cache of
191      * HTTPConnections is used to maximize the reuse of these across
192      * multiple HttpURLConnections.
193      *
194      * <BR>The default method is "GET".
195      *
196      * @param url the url of the request
197      * @exception ProtocolNotSuppException if the protocol is not supported
198      */
199     public HttpURLConnection(URL url)
200 	    throws ProtocolNotSuppException, IOException
201     {
202 	super(url);
203 
204 	// first read proxy properties and set
205         try
206         {
207             String hosts = System.getProperty("http.nonProxyHosts", "");
208 	    if (!hosts.equalsIgnoreCase(non_proxy_hosts))
209 	    {
210 		connections.clear();
211 		non_proxy_hosts = hosts;
212 		String[] list = Util.splitProperty(hosts);
213 		for (int idx=0; idx<list.length; idx++)
214 		    HTTPConnection.dontProxyFor(list[idx]);
215 	    }
216         }
217         catch (ParseException pe)
218 	    { throw new IOException(pe.toString()); }
219         catch (SecurityException se)
220             { }
221 
222 	try
223 	{
224 	    String host = System.getProperty("http.proxyHost", "");
225 	    int port = Integer.getInteger("http.proxyPort", -1).intValue();
226 	    if (!host.equalsIgnoreCase(proxy_host)  ||  port != proxy_port)
227 	    {
228 		connections.clear();
229 		proxy_host = host;
230 		proxy_port = port;
231 		HTTPConnection.setProxyServer(host, port);
232 	    }
233 	}
234 	catch (SecurityException se)
235 	    { }
236 
237 	// now setup stuff
238 	con           = getConnection(url);
239 	method        = "GET";
240 	method_set    = false;
241 	resource      = url.getFile();
242 	headers       = default_headers;
243 	do_redir      = getFollowRedirects();
244 	output_stream = null;
245 
246 	urlString     = url.toString();
247     }
248 
249 
250     /**
251      * Returns an HTTPConnection. A cache of connections is kept and first
252      * consulted; only when the cache lookup fails is a new one created
253      * and added to the cache.
254      *
255      * @param url the url
256      * @return an HTTPConnection
257      * @exception ProtocolNotSuppException if the protocol is not supported
258      */
259     protected HTTPConnection getConnection(URL url)
260 	    throws ProtocolNotSuppException
261     {
262 	// try the cache, using the host name
263 
264 	String php = url.getProtocol() + ":" + url.getHost() + ":" +
265 		     ((url.getPort() != -1) ? url.getPort() :
266 					URI.defaultPort(url.getProtocol()));
267 	php = php.toLowerCase();
268 
269 	HTTPConnection con = (HTTPConnection) connections.get(php);
270 	if (con != null)  return con;
271 
272 
273 	// Not in cache, so create new one and cache it
274 
275 	con = new HTTPConnection(url);
276 	connections.put(php, con);
277 
278 	return con;
279     }
280 
281 
282     // Methods
283 
284     /**
285      * Sets the request method (e.g. "PUT" or "HEAD"). Can only be set
286      * before connect() is called.
287      *
288      * @param method the http method.
289      * @exception ProtocolException if already connected.
290      */
291     public void setRequestMethod(String method)  throws ProtocolException
292     {
293 	if (connected)
294 	    throw new ProtocolException("Already connected!");
295 
296 	Log.write(Log.URLC, "URLC:  (" + urlString + ") Setting request method: " +
297 			    method);
298 
299 	this.method = method.trim().toUpperCase();
300 	method_set  = true;
301     }
302 
303 
304     /**
305      * Return the request method used.
306      *
307      * @return the http method.
308      */
309     public String getRequestMethod()
310     {
311 	return method;
312     }
313 
314 
315     /**
316      * Get the response code. Calls connect() if not connected.
317      *
318      * @return the http response code returned.
319      */
320     public int getResponseCode()  throws IOException
321     {
322 	if (!connected)  connect();
323 
324 	try
325 	    { return resp.getStatusCode(); }
326 	catch (ModuleException me)
327 	    { throw new IOException(me.toString()); }
328     }
329 
330 
331     /**
332      * Get the response message describing the response code. Calls connect()
333      * if not connected.
334      *
335      * @return the http response message returned with the response code.
336      */
337     public String getResponseMessage()  throws IOException
338     {
339 	if (!connected)  connect();
340 
341 	try
342 	    { return resp.getReasonLine(); }
343 	catch (ModuleException me)
344 	    { throw new IOException(me.toString()); }
345     }
346 
347 
348     /**
349      * Get the value part of a header. Calls connect() if not connected.
350      *
351      * @param  name the of the header.
352      * @return the value of the header, or null if no such header was returned.
353      */
354     public String getHeaderField(String name)
355     {
356 	try
357 	{
358 	    if (!connected)  connect();
359 	    return resp.getHeader(name);
360 	}
361 	catch (Exception e)
362 	    { return null; }
363     }
364 
365 
366     /**
367      * Get the value part of a header and converts it to an int. If the
368      * header does not exist or if its value could not be converted to an
369      * int then the default is returned. Calls connect() if not connected.
370      *
371      * @param  name the of the header.
372      * @param  def  the default value to return in case of an error.
373      * @return the value of the header, or null if no such header was returned.
374      */
375     public int getHeaderFieldInt(String name, int def)
376     {
377 	try
378 	{
379 	    if (!connected)  connect();
380 	    return resp.getHeaderAsInt(name);
381 	}
382 	catch (Exception e)
383 	    { return def; }
384     }
385 
386 
387     /**
388      * Get the value part of a header, interprets it as a date and converts
389      * it to a long representing the number of milliseconds since 1970. If
390      * the header does not exist or if its value could not be converted to a
391      * date then the default is returned. Calls connect() if not connected.
392      *
393      * @param  name the of the header.
394      * @param  def  the default value to return in case of an error.
395      * @return the value of the header, or def in case of an error.
396      */
397     public long getHeaderFieldDate(String name, long def)
398     {
399 	try
400 	{
401 	    if (!connected)  connect();
402 	    return resp.getHeaderAsDate(name).getTime();
403 	}
404 	catch (Exception e)
405 	    { return def; }
406     }
407 
408 
409     private String[] hdr_keys, hdr_values;
410 
411     /**
412      * Gets header name of the n-th header. Calls connect() if not connected.
413      * The name of the 0-th header is <var>null</var>, even though it the
414      * 0-th header has a value.
415      *
416      * @param n which header to return.
417      * @return the header name, or null if not that many headers.
418      */
419     public String getHeaderFieldKey(int n)
420     {
421 	if (hdr_keys == null)
422 	    fill_hdr_arrays();
423 
424 	if (n >= 0  &&  n < hdr_keys.length)
425 	    return hdr_keys[n];
426 	else
427 	    return null;
428     }
429 
430 
431     /**
432      * Gets header value of the n-th header. Calls connect() if not connected.
433      * The value of 0-th header is the Status-Line (e.g. "HTTP/1.1 200 Ok").
434      *
435      * @param n which header to return.
436      * @return the header value, or null if not that many headers.
437      */
438     public String getHeaderField(int n)
439     {
440 	if (hdr_values == null)
441 	    fill_hdr_arrays();
442 
443 	if (n >= 0  &&  n < hdr_values.length)
444 	    return hdr_values[n];
445 	else
446 	    return null;
447     }
448 
449 
450     /**
451      * Cache the list of headers.
452      */
453     private void fill_hdr_arrays()
454     {
455 	try
456 	{
457 	    if (!connected)  connect();
458 
459 	    // count number of headers
460 	    int num = 1;
461 	    /** ++GRINDER MODIFICATION **/
462 //	    Enumeration enum = resp.listHeaders();
463 //	    while (enum.hasMoreElements())
464 //	    {
465 //		num++;
466 //		enum.nextElement();
467 //	    }
468         Enumeration e = resp.listHeaders();
469         while (e.hasMoreElements())
470         {
471         num++;
472         e.nextElement();
473         }
474         /** --GRINDER MODIFICATION **/
475 
476 	    // allocate arrays
477 	    hdr_keys   = new String[num];
478 	    hdr_values = new String[num];
479 
480 	    // fill arrays
481 	    /** ++GRINDER MODIFICATION **/
482 //	    enum = resp.listHeaders();
483 //	    for (int idx=1; idx<num; idx++)
484 //	    {
485 //		hdr_keys[idx]   = (String) enum.nextElement();
486         e = resp.listHeaders();
487         for (int idx=1; idx<num; idx++)
488         {
489         hdr_keys[idx]   = (String) e.nextElement();
490         /** --GRINDER MODIFICATION **/
491 		hdr_values[idx] = resp.getHeader(hdr_keys[idx]);
492 	    }
493 
494 	    // the 0'th field is special
495 	    hdr_values[0] = resp.getVersion() + " " + resp.getStatusCode() +
496 			    " " + resp.getReasonLine();
497 	}
498 	catch (Exception e)
499 	    { hdr_keys = hdr_values = new String[0]; }
500     }
501 
502 
503     /**
504      * Gets an input stream from which the data in the response may be read.
505      * Calls connect() if not connected.
506      *
507      * @return an InputStream
508      * @exception ProtocolException if input not enabled.
509      * @see java.net.URLConnection#setDoInput(boolean)
510      */
511     public InputStream getInputStream()  throws IOException
512     {
513 	if (!doInput)
514 	    throw new ProtocolException("Input not enabled! (use setDoInput(true))");
515 
516 	if (!connected)  connect();
517 
518 	InputStream stream;
519 	try
520 	    { stream = resp.getInputStream(); }
521 	catch (ModuleException e)
522 	    { throw new IOException(e.toString()); }
523 
524 	return stream;
525     }
526 
527 
528     /**
529      * Returns the error stream if the connection failed
530      * but the server sent useful data nonetheless.
531      *
532      * <P>This method will not cause a connection to be initiated.
533      *
534      * @return an InputStream, or null if either the connection hasn't
535      *         been established yet or no error occured
536      * @see java.net.HttpURLConnection#getErrorStream()
537      * @since V0.3-1
538      */
539     public InputStream getErrorStream()
540     {
541 	try
542 	{
543 	    if (!doInput  ||  !connected  ||  resp.getStatusCode() < 300  ||
544 		resp.getHeaderAsInt("Content-length") <= 0)
545 		return null;
546 
547 	    return resp.getInputStream();
548 	}
549 	catch (Exception e)
550 	    { return null; }
551     }
552 
553 
554     /**
555      * Gets an output stream which can be used send an entity with the
556      * request. Can be called multiple times, in which case always the
557      * same stream is returned.
558      *
559      * <P>The default request method changes to "POST" when this method is
560      * called. Cannot be called after connect().
561      *
562      * <P>If no Content-type has been set it defaults to
563      * <var>application/x-www-form-urlencoded</var>. Furthermore, if the
564      * Content-type is <var>application/x-www-form-urlencoded</var> then all
565      * output will be collected in a buffer before sending it to the server;
566      * otherwise an HttpOutputStream is used.
567      *
568      * @return an OutputStream
569      * @exception ProtocolException if already connect()'ed, if output is not
570      *                              enabled or if the request method does not
571      *                              support output.
572      * @see java.net.URLConnection#setDoOutput(boolean)
573      * @see HTTPClient.HttpOutputStream
574      */
575     public synchronized OutputStream getOutputStream()  throws IOException
576     {
577 	if (connected)
578 	    throw new ProtocolException("Already connected!");
579 
580 	if (!doOutput)
581 	    throw new ProtocolException("Output not enabled! (use setDoOutput(true))");
582 	if (!method_set)
583 	    method = "POST";
584 	else if (method.equals("HEAD")  ||  method.equals("GET")  ||
585 		 method.equals("TRACE"))
586 	    throw new ProtocolException("Method "+method+" does not support output!");
587 
588 	if (getRequestProperty("Content-type") == null)
589 	    setRequestProperty("Content-type", "application/x-www-form-urlencoded");
590 
591 	if (output_stream == null)
592 	{
593 	    Log.write(Log.URLC, "URLC:  (" + urlString + ") creating output stream");
594 
595 	    String cl = getRequestProperty("Content-Length");
596 	    if (cl != null)
597 		output_stream = new HttpOutputStream(Integer.parseInt(cl.trim()));
598 	    else
599 	    {
600 		// Hack: because of restrictions when using true output streams
601 		// and because form-data is usually quite limited in size, we
602 		// first collect all data before sending it if this is
603 		// form-data.
604 		if (getRequestProperty("Content-type").equals(
605 			"application/x-www-form-urlencoded"))
606 		    output_stream = new ByteArrayOutputStream(300);
607 		else
608 		    output_stream = new HttpOutputStream();
609 	    }
610 
611 	    if (output_stream instanceof HttpOutputStream)
612 		connect();
613 	}
614 
615 	return output_stream;
616     }
617 
618 
619     /**
620      * Gets the url for this connection. If we're connect()'d and the request
621      * was redirected then the url returned is that of the final request.
622      *
623      * @return the final url, or null if any exception occured.
624      */
625     public URL getURL()
626     {
627 	if (connected)
628 	{
629 	    try
630 		{ return resp.getEffectiveURI().toURL(); }
631 	    catch (Exception e)
632 		{ return null; }
633 	}
634 
635 	return url;
636     }
637 
638 
639     /**
640      * Sets the <var>If-Modified-Since</var> header.
641      *
642      * @param time the number of milliseconds since 1970.
643      */
644     public void setIfModifiedSince(long time)
645     {
646 	super.setIfModifiedSince(time);
647 	setRequestProperty("If-Modified-Since", Util.httpDate(new Date(time)));
648     }
649 
650 
651     /**
652      * Sets an arbitrary request header.
653      *
654      * @param name  the name of the header.
655      * @param value the value for the header.
656      */
657     public void setRequestProperty(String name, String value)
658     {
659 	Log.write(Log.URLC, "URLC:  (" + urlString + ") Setting request property: " +
660 			    name + " : " + value);
661 
662 	int idx;
663 	for (idx=0; idx<headers.length; idx++)
664 	{
665 	    if (headers[idx].getName().equalsIgnoreCase(name))
666 		break;
667 	}
668 
669 	if (idx == headers.length)
670 	    headers = Util.resizeArray(headers, idx+1);
671 
672 	headers[idx] = new NVPair(name, value);
673     }
674 
675 
676     /**
677      * Gets the value of a given request header.
678      *
679      * @param name  the name of the header.
680      * @return the value part of the header, or null if no such header.
681      */
682     public String getRequestProperty(String name)
683     {
684 	for (int idx=0; idx<headers.length; idx++)
685 	{
686 	    if (headers[idx].getName().equalsIgnoreCase(name))
687 		return headers[idx].getValue();
688 	}
689 
690 	return null;
691     }
692 
693 
694     /**
695      * Sets an arbitrary default request header. All headers set here are
696      * automatically sent with each request.
697      *
698      * @param name  the name of the header.
699      * @param value the value for the header.
700      */
701     public static void setDefaultRequestProperty(String name, String value)
702     {
703 	Log.write(Log.URLC, "URLC:  Setting default request property: " +
704 			    name + " : " + value);
705 
706 	int idx;
707 	for (idx=0; idx<default_headers.length; idx++)
708 	{
709 	    if (default_headers[idx].getName().equalsIgnoreCase(name))
710 		break;
711 	}
712 
713 	if (idx == default_headers.length)
714 	    default_headers = Util.resizeArray(default_headers, idx+1);
715 
716 	default_headers[idx] = new NVPair(name, value);
717     }
718 
719 
720     /**
721      * Gets the value for a given default request header.
722      *
723      * @param name  the name of the header.
724      * @return the value part of the header, or null if no such header.
725      */
726     public static String getDefaultRequestProperty(String name)
727     {
728 	for (int idx=0; idx<default_headers.length; idx++)
729 	{
730 	    if (default_headers[idx].getName().equalsIgnoreCase(name))
731 		return default_headers[idx].getValue();
732 	}
733 
734 	return null;
735     }
736 
737 
738     /**
739      * Enables or disables the automatic handling of redirection responses
740      * for this instance only. Cannot be called after <code>connect()</code>.
741      *
742      * @param set enables automatic redirection handling if true.
743      */
744     public void setInstanceFollowRedirects(boolean set)
745     {
746 	if (connected)
747 	    throw new IllegalStateException("Already connected!");
748 
749 	do_redir = set;
750     }
751 
752 
753     /**
754      * @return true if automatic redirection handling for this instance is
755      *              enabled.
756      */
757     public boolean getInstanceFollowRedirects()
758     {
759 	return do_redir;
760     }
761 
762 
763     /**
764      * Connects to the server (if connection not still kept alive) and
765      * issues the request.
766      */
767     public synchronized void connect()  throws IOException
768     {
769 	if (connected)  return;
770 
771 	Log.write(Log.URLC, "URLC:  (" + urlString + ") Connecting ...");
772 
773 	// useCaches TBD!!!
774 
775 	synchronized (con)
776 	{
777 	    con.setAllowUserInteraction(allowUserInteraction);
778 	    if (do_redir)
779 		con.addModule(redir_mod, 2);
780 	    else
781 		con.removeModule(redir_mod);
782 
783 	    try
784 	    {
785 		if (output_stream instanceof ByteArrayOutputStream)
786 		    resp = con.ExtensionMethod(method, resource,
787 			((ByteArrayOutputStream) output_stream).toByteArray(),
788 					     headers);
789 		else
790 		    resp = con.ExtensionMethod(method, resource,
791 				    (HttpOutputStream) output_stream, headers);
792 	    }
793 	    catch (ModuleException e)
794 		{ throw new IOException(e.toString()); }
795 	}
796 
797 	connected = true;
798     }
799 
800 
801     /**
802      * Closes all the connections to this server.
803      */
804     public void disconnect()
805     {
806 	Log.write(Log.URLC, "URLC:  (" + urlString + ") Disconnecting ...");
807 
808 	con.stop();
809     }
810 
811 
812     /**
813      * Shows if request are being made through an http proxy or directly.
814      *
815      * @return true if an http proxy is being used.
816      */
817     public boolean usingProxy()
818     {
819 	return (con.getProxyHost() != null);
820     }
821 
822 
823     /**
824      * produces a string.
825      * @return a string containing the HttpURLConnection
826      */
827     public String toString()
828     {
829 	return getClass().getName() + "[" + url + "]";
830     }
831 }