/*
License $Id: HttpURLConnection.java,v 1.5 2003/09/13 04:59:55 hendriks73 Exp $
Copyright (c) 2001-2005 tagtraum industries.
LGPL
====
jo! is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
jo! 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
Sun license
===========
This release contains software by Sun Microsystems. Therefore
the following conditions have to be met, too. They apply to the
files
- lib/mail.jar
- lib/activation.jar
- lib/jsse.jar
- lib/jcert.jar
- lib/jaxp.jar
- lib/crimson.jar
- lib/servlet.jar
- lib/jnet.jar
- lib/jaas.jar
- lib/jaasmod.jar
contained in this release.
a. Licensee may not modify the Java Platform
Interface (JPI, identified as classes contained within the javax
package or any subpackages of the javax package), by creating additional
classes within the JPI or otherwise causing the addition to or modification
of the classes in the JPI. In the event that Licensee creates any
Java-related API and distribute such API to others for applet or
application development, you must promptly publish broadly, an accurate
specification for such API for free use by all developers of Java-based
software.
b. Software is confidential copyrighted information of Sun and
title to all copies is retained by Sun and/or its licensors. Licensee
shall not modify, decompile, disassemble, decrypt, extract, or otherwise
reverse engineer Software. Software may not be leased, assigned, or
sublicensed, in whole or in part. Software is not designed or intended
for use in on-line control of aircraft, air traffic, aircraft navigation
or aircraft communications; or in the design, construction, operation or
maintenance of any nuclear facility. Licensee warrants that it will not
use or redistribute the Software for such purposes.
c. Software is provided "AS IS," without a warranty
of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
d. This License is effective until terminated. Licensee may
terminate this License at any time by destroying all copies of Software.
This License will terminate immediately without notice from Sun if Licensee
fails to comply with any provision of this License. Upon such termination,
Licensee must destroy all copies of Software.
e. Software, including technical data, is subject to U.S.
export control laws, including the U.S. Export Administration Act and its
associated regulations, and may be subject to export or import regulations
in other countries. Licensee agrees to comply strictly with all such
regulations and acknowledges that it has the responsibility to obtain
licenses to export, re-export, or import Software. Software may not be
downloaded, or otherwise exported or re-exported (i) into, or to a national
or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
country to which the U.S. has embargoed goods; or (ii) to anyone on the
U.S. Treasury Department's list of Specially Designated Nations or the U.S.
Commerce Department's Table of Denial Orders.
Feedback
========
We encourage your feedback and suggestions and want to use your feedback to
improve the Software. Send all such feedback to:
<feedback@tagtraum.com>
For more information on tagtraum industries and jo!
please see <http://www.tagtraum.com/>.
*/
package com.tagtraum.framework.http;
import com.tagtraum.framework.util.InetAddressPort;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.net.InetAddress;
import java.net.ProtocolException;
import java.net.Socket;
import java.net.URL;
import java.util.Hashtable;
/**
* <b>This class is nut fully implemented yet. It is not usable.</b>
*
*
* A URLConnection with support for HTTP-specific features. See
* <A HREF="http://www.w3.org/pub/WWW/Protocols/"> the spec </A> for
* details.
* <p>
*
* Each HttpURLConnection instance is used to make a single request
* but the underlying network connection to the HTTP server may be
* transparently shared by other instances. Calling the close() methods
* on the InputStream or OutputStream of an HttpURLConnection
* after a request may free network resources associated with this
* instance but has no effect on any shared persistent connection.
* Calling the disconnect() method may close the underlying socket
* if a persistent connection is otherwise idle at that time.
*
* @author Hendrik Schreiber
* @version 1.1beta1 $Id: HttpURLConnection.java,v 1.5 2003/09/13 04:59:55 hendriks73 Exp $
*/
public class HttpURLConnection extends java.net.HttpURLConnection implements C_Http {
/**
* Source-Version
*/
public static String vcid = "$Id: HttpURLConnection.java,v 1.5 2003/09/13 04:59:55 hendriks73 Exp $";
private static Hashtable freeConnectionMap;
static {
freeConnectionMap = new Hashtable();
}
private HttpHeader requestHeader;
private HttpHeader responseHeader;
private RequestLine requestLine;
private ResponseLine responseLine;
private Connection connection;
private boolean readResponse = false;
private boolean wroteRequest = false;
/* do we automatically follow redirects? The default is true. */
private static boolean followRedirects = true;
/**
* If <code>true</code>, the protocol will automatically follow redirects.
* If <code>false</code>, the protocol will not automatically follow
* redirects.
* <p>
* This field is set by the <code>setInstanceFollowRedirects</code>
* method. Its value is returned by the <code>getInstanceFollowRedirects</code>
* method.
* <p>
* Its default value is based on the value of the static followRedirects
* at HttpURLConnection construction time.
*
* @see java.net.HttpURLConnection#setInstanceFollowRedirects(boolean)
* @see java.net.HttpURLConnection#getInstanceFollowRedirects()
* @see java.net.HttpURLConnection#setFollowRedirects(boolean)
*/
protected boolean instanceFollowRedirects = followRedirects;
/**
* Constructor for the URLStreamHandler.
* @param u the URL
*/
protected HttpURLConnection(URL u) {
super(u);
requestHeader = new HttpHeader();
responseHeader = new HttpHeader();
requestLine = new RequestLine();
responseLine = new ResponseLine();
requestLine.setMethod(C_HTTP_GET);
requestLine.setProtocol(C_HTTP_1_1);
requestLine.setURI(new URI(u.getFile()));
System.out.println("Requestline: " + requestLine);
}
/**
* Gets HTTP response status from responses like:
* <PRE>
* HTTP/1.0 200 OK
* HTTP/1.0 401 Unauthorized
* </PRE>
* Extracts the ints 200 and 401 respectively.
* Returns -1 if none can be discerned
* from the response (i.e., the response is not valid HTTP).
* @throws IOException if an error occurred connecting to the server.
* @return the HTTP Status-Code
*/
public int getResponseCode() throws IOException {
// make sure we've gotten the headers
getInputStream();
return responseLine.getStatusCode();
}
/**
* Gets the HTTP response message, if any, returned along with the
* response code from a server. From responses like:
* <PRE>
* HTTP/1.0 200 OK
* HTTP/1.0 404 Not Found
* </PRE>
* Extracts the Strings "OK" and "Not Found" respectively.
* Returns null if none could be discerned from the responses
* (the result was not valid HTTP).
* @throws IOException if an error occurred connecting to the server.
* @return the HTTP response message, or <code>null</code>
*/
public String getResponseMessage() throws IOException {
// make sure we've gotten the headers
getInputStream();
return responseLine.getReasonPhrase();
}
/**
* Opens a communications link to the resource referenced by this
* URL, if such a connection has not already been established.
* <p>
* If the <code>connect</code> method is called when the connection
* has already been opened (indicated by the <code>connected</code>
* field having the value <code>true</code>), the call is ignored.
* <p>
* URLConnection objects go through two phases: first they are
* created, then they are connected. After being created, and
* before being connected, various options can be specified
* (e.g., doInput and UseCaches). After connecting, it is an
* error to try to set them. Operations that depend on being
* connected, like getContentLength, will implicitly perform the
* connection, if necessary.
*
* @exception IOException if an I/O error occurs while opening the
* connection.
* @see java.net.URLConnection#connected
*/
public synchronized void connect() throws IOException {
if (connection != null) return;
InetAddressPort inetAddressPort = new InetAddressPort(InetAddress.getByName(url.getHost()), url.getPort());
connection = (Connection)freeConnectionMap.get(inetAddressPort);
if (connection == null) connection = new Connection(inetAddressPort);
connected = true;
}
/**
* Indicates that other requests to the server
* are unlikely in the near future. Calling disconnect()
* should not imply that this HttpURLConnection
* instance can be reused for other requests.
*/
public void disconnect() {
if (connection != null) connection.close();
}
/**
* Indicates if the connection is going through a proxy.
* @return a boolean indicating if the connection is
* using a proxy.
*/
public boolean usingProxy() {
return false;
}
/**
* Returns an input stream that reads from this open connection.
*
* @return an input stream that reads from this open connection.
* @exception IOException if an I/O error occurs while
* creating the input stream.
* @exception java.net.UnknownServiceException if the protocol does not support
* input.
*/
public InputStream getInputStream() throws IOException {
connect();
if (!readResponse) {
synchronized (this) {
if (!readResponse) {
connection.read(responseLine, responseHeader);
readResponse = true;
}
}
}
return connection.getInputStream();
}
/**
* Returns an output stream that writes to this connection.
*
* @return an output stream that writes to this connection.
* @exception IOException if an I/O error occurs while
* creating the output stream.
* @exception java.net.UnknownServiceException if the protocol does not support
* output.
*/
public OutputStream getOutputStream() throws IOException {
connect();
if (!wroteRequest) {
synchronized (this) {
if (!wroteRequest) {
connection.write(requestLine, requestHeader);
wroteRequest = true;
}
}
}
return connection.getOutputStream();
}
/**
* Returns the error stream if the connection failed
* but the server sent useful data nonetheless. The
* typical example is when an HTTP server responds
* with a 404, which will cause a FileNotFoundException
* to be thrown in connect, but the server sent an HTML
* help page with suggestions as to what to do.
*
* <p>This method will not cause a connection to be initiated.
* If there the connection was not connected, or if the server
* did not have an error while connecting or if the server did
* have an error but there no error data was sent, this method
* will return null. This is the default.
*
* @return an error stream if any, null if there have been
* no errors, the connection is not connected or the server
* sent no useful data.
*/
public InputStream getErrorStream() {
return null;
}
/**
* Returns the name of the specified header field.
*
* @param name the name of a header field.
* @return the value of the named header field, or <code>null</code>
* if there is no such field in the header.
*/
public String getHeaderField(String name) {
return responseHeader.getHeader(name);
}
/**
* Returns the value of the named field parsed as date.
* The result is the number of milliseconds since January 1, 1970 GMT
* represented by the named field.
*
* @param name the name of the header field.
* @param Default a default value.
* @return the value of the field, parsed as a date. The value of the
* <code>Default</code> argument is returned if the field is
* missing or malformed.
*/
public long getHeaderFieldDate(String name, long Default) {
try {
return HttpDate.parse(getHeaderField(name));
}
catch (Throwable t) {
}
return Default;
}
/**
* Returns the key for the <code>n</code><sup>th</sup> header field.
*
* @param n an index.
* @return the key for the <code>n</code><sup>th</sup> header field,
* or <code>null</code> if there are fewer than <code>n</code>
* fields.
*/
public String getHeaderFieldKey(int n) {
return responseHeader.getHeaderName(n);
}
/**
* Returns the value for the <code>n</code><sup>th</sup> header field.
* It returns <code>null</code> if there are fewer than
* <code>n</code> fields.
* <p>
* This method can be used in conjunction with the
* <code>getHeaderFieldKey</code> method to iterate through all
* the headers in the message.
*
* @param n an index.
* @return the value of the <code>n</code><sup>th</sup> header field.
* @see java.net.URLConnection#getHeaderFieldKey(int)
*/
public String getHeaderField(int n) {
return responseHeader.getHeader(n);
}
/**
* Sets the value of the <code>ifModifiedSince</code> field of
* this <code>URLConnection</code> to the specified value.
*
* @param ifmodifiedsince the new value.
* @see #getIfModifiedSince()
*/
public void setIfModifiedSince(long ifmodifiedsince) {
super.setIfModifiedSince(ifmodifiedsince);
requestHeader.setDateHeader(C_HTTP_IfModifiedSince, ifmodifiedsince);
}
/**
* Set the method for the URL request, one of:
* <UL>
* <LI>GET
* <LI>POST
* <LI>HEAD
* <LI>OPTIONS
* <LI>PUT
* <LI>DELETE
* <LI>TRACE
* </UL> are legal, subject to protocol restrictions. The default
* method is GET.
*
* @param method the HTTP method
* @exception ProtocolException if the method cannot be reset
* @see #getRequestMethod()
*/
public void setRequestMethod(String method) throws ProtocolException {
if (connected) {
throw new ProtocolException("Can't reset method: already connected");
}
requestLine.setMethod(method);
}
/**
* Get the request method.
* @return the HTTP request method
* @see #setRequestMethod(java.lang.String)
*/
public String getRequestMethod() {
return requestLine.getMethod();
}
/**
* Set the protocol for the URL request, one of:
* <UL>
* <LI>HTTP/1.0
* <LI>HTTP/1.1
* </UL> are legal, subject to protocol restrictions. The default
* version is HTTP/1.1.
*
* @param protocol http version
* @exception ProtocolException if the protocl cannot be reset
* @see #getRequestProtocol()
*/
public void setRequestProtocol(String protocol) throws ProtocolException {
if (connected) {
throw new ProtocolException("Can't reset method: already connected");
}
requestLine.setProtocol(protocol);
}
/**
* Get the request protocol.
* @return the HTTP request protocol
* @see #setRequestProtocol(java.lang.String)
*/
public String getRequestProtocol() {
return requestLine.getProtocol();
}
/**
* Sets the general request property. If a property with the key already
* exists, overwrite its value with the new value.
*
* <p> NOTE: HTTP requires all request properties which can
* legally have multiple instances with the same key
* to use a comma-seperated list syntax which enables multiple
* properties to be appended into a single property.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
* @see #getRequestProperty(java.lang.String)
* @see #addRequestProperty(java.lang.String, String)
*/
public void setRequestProperty(String key, String value) {
super.setRequestProperty(key, value);
requestHeader.setHeader(key, value);
}
/**
* Adds a general request property.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
* @see #getRequestProperty(java.lang.String)
*/
public void addRequestProperty(String key, String value) {
super.setRequestProperty(key, value);
requestHeader.addHeader(key, value);
}
/**
* Returns the value of the named general request property for this
* connection.
*
* @param key the keyword by which the request is known (e.g., "accept").
* @return the value of the named general request property for this
* connection.
* @see #setRequestProperty(java.lang.String, java.lang.String)
*/
public String getRequestProperty(String key) {
super.getRequestProperty(key);
return requestHeader.getHeader(key);
}
}
class Connection implements C_Http {
private InputStream in;
private OutputStream out;
private InputStream exposedIn;
private OutputStream exposedOut;
private boolean supportsChunking = false;
private Socket socket;
public Connection(InetAddressPort inetAddressPort) throws IOException {
socket = new Socket(inetAddressPort.getInetAddress(), inetAddressPort.getPort());
in = new PushbackInputStream(socket.getInputStream());
out = socket.getOutputStream();
exposedIn = in;
exposedOut = out;
}
public InputStream getInputStream() throws IOException {
return exposedIn;
}
public OutputStream getOutputStream() throws IOException {
return exposedOut;
}
public void write(RequestLine requestLine, HttpHeader requestHeader) throws IOException {
if (requestHeader.getIntHeader(C_HTTP_ContentLength) == -1 && supportsChunking) {
requestHeader.setHeader(C_HTTP_TransferEncoding, "chunked");
exposedOut = new ChunkedOutputStream(out);
} else if (requestHeader.getIntHeader(C_HTTP_ContentLength) != -1) {
//exposedOut = new Limitedout;
} else {
// neither length nor chunking
requestHeader.setHeader(C_HTTP_Connection, "close");
}
requestLine.write(out);
requestHeader.write(out);
}
public void read(ResponseLine responseLine, HttpHeader responseHeader) throws IOException {
responseLine.read(in);
responseHeader.read(in);
if ("chunked".equals(responseHeader.getHeader(C_HTTP_TransferEncoding))) {
exposedIn = new ChunkedInputStream(in);
}
}
public void close() {
// this must be changed once keep alive is supported.
try {
socket.close();
}
catch (IOException ioe) {
// ignore
}
}
}
|