/* * @(#)HTTPConnection.java 0.3-3 06/05/2001 * * This file is part of the HTTPClient package * Copyright (C) 1996-2001 Ronald Tschalär * * This library 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 of the License, or (at your option) any later version. * * This library 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 questions, suggestions, bug-reports, enhancement-requests etc. * I may be contacted at: * * ronald@innovation.ch * * The HTTPClient's home page is located at: * * http://www.innovation.ch/java/HTTPClient/ * */ package HTTPClient; import java.io.OutputStream; import java.io.DataOutputStream; import java.io.FilterOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.net.URL; import java.net.Socket; import java.net.InetAddress; import java.net.SocketException; import java.net.ConnectException; import java.net.UnknownHostException; import java.net.NoRouteToHostException; import java.util.Vector; import java.applet.Applet; /** * This class implements http protocol requests; it contains most of HTTP/1.1 * and ought to be unconditionally compliant. * Redirections are automatically handled, and authorizations requests are * recognized and dealt with via an authorization handler. * Only full HTTP/1.0 and HTTP/1.1 requests are generated. HTTP/1.1, HTTP/1.0 * and HTTP/0.9 responses are recognized. * *
Using the HTTPClient should be quite simple. First add the import
* statement 'import HTTPClient.*;
' to your file(s). Request
* can then be sent using one of the methods Head(),
* Get(), Post(), etc in HTTPConnection.
* These methods all return an instance of HTTPResponse which
* has methods for accessing the response headers (getHeader(),
* getHeaderAsInt(), etc), various response info
* (getStatusCode(), getReasonLine(), etc) and the
* reponse data (getData(), getText(), and
* getInputStream()). Following are some examples.
*
*
If this is in an applet you can retrieve files from your server * as follows: * *
* try * { * HTTPConnection con = new HTTPConnection(this); * HTTPResponse rsp = con.Get("/my_file"); * if (rsp.getStatusCode() >= 300) * { * System.err.println("Received Error: "+rsp.getReasonLine()); * System.err.println(rsp.getText()); * } * else * data = rsp.getData(); * * rsp = con.Get("/another_file"); * if (rsp.getStatusCode() >= 300) * { * System.err.println("Received Error: "+rsp.getReasonLine()); * System.err.println(rsp.getText()); * } * else * other_data = rsp.getData(); * } * catch (IOException ioe) * { * System.err.println(ioe.toString()); * } * catch (ModuleException me) * { * System.err.println("Error handling request: " + me.getMessage()); * } ** * This will get the files "/my_file" and "/another_file" and put their * contents into byte[]'s accessible via
getData()
. Note that
* you need to only create a new HTTPConnection when sending a
* request to a new server (different host or port); although you may create
* a new HTTPConnection for every request to the same server this
* not recommended, as various information about the server
* is cached after the first request (to optimize subsequent requests) and
* persistent connections are used whenever possible.
*
* To POST form data you would use something like this (assuming you * have two fields called name and e-mail, whose * contents are stored in the variables name and email): * *
* try * { * NVPair form_data[] = new NVPair[2]; * form_data[0] = new NVPair("name", name); * form_data[1] = new NVPair("e-mail", email); * * HTTPConnection con = new HTTPConnection(this); * HTTPResponse rsp = con.Post("/cgi-bin/my_script", form_data); * if (rsp.getStatusCode() >= 300) * { * System.err.println("Received Error: "+rsp.getReasonLine()); * System.err.println(rsp.getText()); * } * else * stream = rsp.getInputStream(); * } * catch (IOException ioe) * { * System.err.println(ioe.toString()); * } * catch (ModuleException me) * { * System.err.println("Error handling request: " + me.getMessage()); * } ** * Here the response data is read at leasure via an InputStream * instead of all at once into a byte[]. * *
As another example, if you have a URL you're trying to send a request * to you would do something like the following: * *
* try * { * URL url = new URL("http://www.mydomain.us/test/my_file"); * HTTPConnection con = new HTTPConnection(url); * HTTPResponse rsp = con.Put(url.getFile(), "Hello World"); * if (rsp.getStatusCode() >= 300) * { * System.err.println("Received Error: "+rsp.getReasonLine()); * System.err.println(rsp.getText()); * } * else * text = rsp.getText(); * } * catch (IOException ioe) * { * System.err.println(ioe.toString()); * } * catch (ModuleException me) * { * System.err.println("Error handling request: " + me.getMessage()); * } ** *
There are a whole number of methods for each request type; however the * general forms are ([...] means that the enclosed is optional): *
Note: there is a small window where a request method such as
* Get()
may have been invoked but the request has not
* been built and added to the list. Any request in this window will
* not be aborted.
*
* @since V0.2-3
*/
public void stop()
{
for (Request req = (Request) RequestList.enumerate(); req != null;
req = (Request) RequestList.next())
req.aborted = true;
for (StreamDemultiplexor demux =
(StreamDemultiplexor) DemuxList.enumerate();
demux != null; demux = (StreamDemultiplexor) DemuxList.next())
demux.abort();
}
/**
* Sets the default http headers to be sent with each request. The
* actual headers sent are determined as follows: for each header
* specified in multiple places a value given as part of the request
* takes priority over any default values set by this method, which
* in turn takes priority over any built-in default values. A different
* way of looking at it is that we start off with a list of all headers
* specified with the request, then add any default headers set by this
* method which aren't already in our list, and finally add any built-in
* headers which aren't yet in the list. There is one exception to this
* rule: the "Content-length" header is always ignored; and when posting
* form-data any default "Content-type" is ignored in favor of the built-in
* "application/x-www-form-urlencoded" (however it will be overriden by any
* content-type header specified as part of the request).
*
*
Typical headers you might want to set here are "Accept" and its
* "Accept-*" relatives, "Connection", "From", "User-Agent", etc.
*
* @param headers an array of header-name/value pairs (do not give the
* separating ':').
*/
public void setDefaultHeaders(NVPair[] headers)
{
int length = (headers == null ? 0 : headers.length);
NVPair[] def_hdrs = new NVPair[length];
// weed out undesired headers
int sidx, didx;
for (sidx=0, didx=0; sidx When creating new sockets the timeout will limit the time spent
* doing the host name translation and establishing the connection with
* the server.
*
* The timeout also influences the reading of the response headers.
* However, it does not specify a how long, for example, getStatusCode()
* may take, as might be assumed. Instead it specifies how long a read
* on the socket may take. If the response dribbles in slowly with
* packets arriving quicker than the timeout then the method will
* complete normally. I.e. the exception is only thrown if nothing
* arrives on the socket for the specified time. Furthermore, the
* timeout only influences the reading of the headers, not the reading
* of the body.
*
* Read Timeouts are associated with responses, so that you may change
* this value before each request and it won't affect the reading of
* responses to previous requests.
*
* @param time the time in milliseconds. A time of 0 means wait
* indefinitely.
* @see #stop()
*/
public void setTimeout(int time)
{
Timeout = time;
}
/**
* Gets the timeout used for reading response data.
*
* @return the current timeout value
* @see #setTimeout(int)
*/
public int getTimeout()
{
return Timeout;
}
/**
* Controls whether modules are allowed to prompt the user or pop up
* dialogs if neccessary.
*
* @param allow if true allows modules to interact with user.
*/
public void setAllowUserInteraction(boolean allow)
{
allowUI = allow;
}
/**
* returns whether modules are allowed to prompt or popup dialogs
* if neccessary.
*
* @return true if modules are allowed to interact with user.
*/
public boolean getAllowUserInteraction()
{
return allowUI;
}
/**
* Sets the default allow-user-action.
*
* @param allow if true allows modules to interact with user.
*/
public static void setDefaultAllowUserInteraction(boolean allow)
{
defaultAllowUI = allow;
}
/**
* Gets the default allow-user-action.
*
* @return true if modules are allowed to interact with user.
*/
public static boolean getDefaultAllowUserInteraction()
{
return defaultAllowUI;
}
/**
* Returns the default list of modules.
*
* @return an array of classes
*/
public static Class[] getDefaultModules()
{
return getModules(DefaultModuleList);
}
/**
* Adds a module to the default list. It must implement the
* HTTPClientModule interface. If the module is already in
* the list then this method does nothing. This method only affects
* instances of HTTPConnection created after this method has been
* invoked; it does not affect existing instances.
*
* Example:
* The default list is created at class initialization time from the
* property HTTPClient.Modules. This must contain a "|"
* separated list of classes in the order they're to be invoked. If this
* property is not set it defaults to:
*
* "HTTPClient.RetryModule | HTTPClient.CookieModule |
* HTTPClient.RedirectionModule | HTTPClient.AuthorizationModule |
* HTTPClient.DefaultModule | HTTPClient.TransferEncodingModule |
* HTTPClient.ContentMD5Module | HTTPClient.ContentEncodingModule"
*
* @see HTTPClientModule
* @param module the module's Class object
* @param pos the position of this module in the list; if pos
* >= 0 then this is the absolute position in the list (0 is
* the first position); if pos < 0 then this is
* the position relative to the end of the list (-1 means
* the last element, -2 the second to last element, etc).
* @return true if module was successfully added; false if the
* module is already in the list.
* @exception ArrayIndexOutOfBoundsException if pos >
* list-size or if pos < -(list-size).
* @exception ClassCastException if module does not
* implement the HTTPClientModule interface.
* @exception RuntimeException if module cannot be
* instantiated.
*/
public static boolean addDefaultModule(Class module, int pos)
{
return addModule(DefaultModuleList, module, pos);
}
/**
* Removes a module from the default list. If the module is not in the
* list it does nothing. This method only affects instances of
* HTTPConnection created after this method has been invoked; it does not
* affect existing instances.
*
* @param module the module's Class object
* @return true if module was successfully removed; false otherwise
*/
public static boolean removeDefaultModule(Class module)
{
return removeModule(DefaultModuleList, module);
}
/**
* Returns the list of modules used currently.
*
* @return an array of classes
*/
public Class[] getModules()
{
return getModules(ModuleList);
}
/**
* Adds a module to the current list. It must implement the
* HTTPClientModule interface. If the module is already in
* the list then this method does nothing.
*
* @see HTTPClientModule
* @param module the module's Class object
* @param pos the position of this module in the list; if pos
* >= 0 then this is the absolute position in the list (0 is
* the first position); if pos < 0 then this is
* the position relative to the end of the list (-1 means
* the last element, -2 the second to last element, etc).
* @return true if module was successfully added; false if the
* module is already in the list.
* @exception ArrayIndexOutOfBoundsException if pos >
* list-size or if pos < -(list-size).
* @exception ClassCastException if module does not
* implement the HTTPClientModule interface.
* @exception RuntimeException if module cannot be
* instantiated.
*/
public boolean addModule(Class module, int pos)
{
return addModule(ModuleList, module, pos);
}
/**
* Removes a module from the current list. If the module is not in the
* list it does nothing.
*
* @param module the module's Class object
* @return true if module was successfully removed; false otherwise
*/
public boolean removeModule(Class module)
{
return removeModule(ModuleList, module);
}
private static final Class[] getModules(Vector list)
{
synchronized(list)
{
Class[] modules = new Class[list.size()];
list.copyInto(modules);
return modules;
}
}
private static final boolean addModule(Vector list, Class module, int pos)
{
if (module == null) return false;
// check if module implements HTTPClientModule
try
{ HTTPClientModule tmp = (HTTPClientModule) module.newInstance(); }
catch (RuntimeException re)
{ throw re; }
catch (Exception e)
{ throw new RuntimeException(e.toString()); }
synchronized (list)
{
// check if module already in list
if (list.contains(module))
return false;
// add module to list
if (pos < 0)
list.insertElementAt(module, DefaultModuleList.size()+pos+1);
else
list.insertElementAt(module, pos);
}
Log.write(Log.CONN, "Conn: Added module " + module.getName() +
" to " +
((list == DefaultModuleList) ? "default " : "") +
"list");
return true;
}
private static final boolean removeModule(Vector list, Class module)
{
if (module == null) return false;
boolean removed = list.removeElement(module);
if (removed)
Log.write(Log.CONN, "Conn: Removed module " + module.getName() +
" from " +
((list == DefaultModuleList) ? "default " : "") +
"list");
return removed;
}
/**
* Sets the current context. The context is used by modules such as
* the AuthorizationModule and the CookieModule which keep lists of
* info that is normally shared between all instances of HTTPConnection.
* This is usually the desired behaviour. However, in some cases one
* would like to simulate multiple independent clients within the
* same application and hence the sharing of such info should be
* restricted. This is where the context comes in. Modules will only
* share their info between requests using the same context (i.e. they
* keep multiple lists, one for each context).
*
* The context may be any object. Contexts are considered equal
* if When a new HTTPConnection is created it is initialized with a
* default context which is the same for all instances. This method
* must be invoked immediately after a new HTTPConnection is created
* and before any request method is invoked. Furthermore, this method
* may only be called once (i.e. the context is "sticky").
*
* @param context the new context; must be non-null
* @exception IllegalArgumentException if context is null
* @exception IllegalStateException if the context has already been set
*/
public void setContext(Object context)
{
if (context == null)
throw new IllegalArgumentException("Context must be non-null");
if (Context != null)
throw new IllegalStateException("Context already set");
Context = context;
}
/**
* Returns the current context.
*
* @see #setContext(java.lang.Object)
* @return the current context, or the default context if
* This is a convenience method and just invokes the corresponding
* method in AuthorizationInfo.
*
* @param realm the realm
* @param user the username
* @param passw the password
* @see AuthorizationInfo#addDigestAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
*/
public void addDigestAuthorization(String realm, String user, String passwd)
{
AuthorizationInfo.addDigestAuthorization(Host, Port, realm, user,
passwd, getContext());
}
/**
* Adds an authorization entry for the "basic" authorization scheme to
* the list. If an entry already exists for the "basic" scheme and the
* specified realm then it is overwritten.
*
* This is a convenience method and just invokes the corresponding
* method in AuthorizationInfo.
*
* @param realm the realm
* @param user the username
* @param passw the password
* @see AuthorizationInfo#addBasicAuthorization(java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
*/
public void addBasicAuthorization(String realm, String user, String passwd)
{
AuthorizationInfo.addBasicAuthorization(Host, Port, realm, user,
passwd, getContext());
}
/**
* Sets the default proxy server to use. The proxy will only be used
* for new HTTPConnections created after this call and will
* not affect currrent instances of HTTPConnection. A null
* or empty string host parameter disables the proxy.
*
* In an application or using the Appletviewer an alternative to
* this method is to set the following properties (either in the
* properties file or on the command line):
* http.proxyHost and http.proxyPort. Whether
* http.proxyHost is set or not determines whether a proxy
* server is used.
*
* If the proxy server requires authorization and you wish to set
* this authorization information in the code, then you may use any
* of the AuthorizationInfo.addXXXAuthorization() methods to
* do so. Specify the same host and port as in
* this method. If you have not given any authorization info and the
* proxy server requires authorization then you will be prompted for
* the necessary info via a popup the first time you do a request.
*
* @see #setCurrentProxy(java.lang.String, int)
* @param host the host on which the proxy server resides.
* @param port the port the proxy server is listening on.
*/
public static void setProxyServer(String host, int port)
{
if (host == null || host.trim().length() == 0)
Default_Proxy_Host = null;
else
{
Default_Proxy_Host = host.trim().toLowerCase();
Default_Proxy_Port = port;
}
}
/**
* Sets the proxy used by this instance. This can be used to override
* the proxy setting inherited from the default proxy setting. A null
* or empty string host parameter disables the proxy.
*
* Note that if you set a proxy for the connection using this
* method, and a request made over this connection is redirected
* to a different server, then the connection used for new server
* will not pick this proxy setting, but instead will use
* the default proxy settings.
*
* @see #setProxyServer(java.lang.String, int)
* @param host the host the proxy runs on
* @param port the port the proxy is listening on
*/
public synchronized void setCurrentProxy(String host, int port)
{
if (host == null || host.trim().length() == 0)
Proxy_Host = null;
else
{
Proxy_Host = host.trim().toLowerCase();
if (port <= 0)
Proxy_Port = 80;
else
Proxy_Port = port;
}
// the proxy might be talking a different version, so renegotiate
switch(Protocol)
{
case HTTP:
case HTTPS:
if (force_1_0)
{
ServerProtocolVersion = HTTP_1_0;
ServProtVersKnown = true;
RequestProtocolVersion = "HTTP/1.0";
}
else
{
ServerProtocolVersion = HTTP_1_1;
ServProtVersKnown = false;
RequestProtocolVersion = "HTTP/1.1";
}
break;
case HTTP_NG:
ServerProtocolVersion = -1; /* Unknown */
ServProtVersKnown = false;
RequestProtocolVersion = "";
break;
case SHTTP:
ServerProtocolVersion = -1; /* Unknown */
ServProtVersKnown = false;
RequestProtocolVersion = "Secure-HTTP/1.3";
break;
default:
throw new Error("HTTPClient Internal Error: invalid protocol " +
Protocol);
}
keepAliveUnknown = true;
doesKeepAlive = false;
input_demux = null;
early_stall = null;
late_stall = null;
prev_resp = null;
}
/**
* Add host to the list of hosts which should be accessed
* directly, not via any proxy set by The host may be any of:
* The two properties HTTPClient.nonProxyHosts and
* http.nonProxyHosts are used when this class is loaded to
* initialize the list of non-proxy hosts. The second property is only
* read if the first one is not set; the second property is also used
* the JDK's URLConnection. These properties must contain a "|"
* separated list of entries which conform to the above rules for the
* host parameter (e.g. "11.22.33.44|.disney.com").
*
* @param host a host name, domain name, IP-address or IP-subnet.
* @exception ParseException if the length of the netmask does not match
* the length of the IP-address
*/
public static void dontProxyFor(String host) throws ParseException
{
host = host.trim().toLowerCase();
// check for domain name
if (host.charAt(0) == '.')
{
if (!non_proxy_dom_list.contains(host))
non_proxy_dom_list.addElement(host);
return;
}
// check for host name
for (int idx=0; idx The code will try to determine the SOCKS version to use at
* connection time. This might fail for a number of reasons, however,
* in which case you must specify the version explicitly.
*
* @see #setSocksServer(java.lang.String, int, int)
* @param host the host on which the proxy server resides.
* @param port the port the proxy server is listening on.
*/
public static void setSocksServer(String host, int port)
{
if (port <= 0)
port = 1080;
if (host == null || host.length() == 0)
Default_Socks_client = null;
else
Default_Socks_client = new SocksClient(host, port);
}
/**
* Sets the SOCKS server to use. The server will only be used
* for new HTTPConnections created after this call and will not affect
* currrent instances of HTTPConnection. A null or empty string host
* parameter disables SOCKS.
*
* In an application or using the Appletviewer an alternative to
* this method is to set the following properties (either in the
* properties file or on the command line):
* HTTPClient.socksHost, HTTPClient.socksPort
* and HTTPClient.socksVersion. Whether
* HTTPClient.socksHost is set or not determines whether a
* SOCKS server is used; if HTTPClient.socksPort is not set
* it defaults to 1080; if HTTPClient.socksVersion is not
* set an attempt will be made to automatically determine the version
* used by the server.
*
* Note: If you have also set a proxy server then a connection
* will be made to the SOCKS server, which in turn then makes a
* connection to the proxy server (possibly via other SOCKS servers),
* which in turn makes the final connection.
*
* If the proxy server is running SOCKS version 5 and requires
* username/password authorization, and you wish to set
* this authorization information in the code, then you may use the
* AuthorizationInfo.addAuthorization() method to do so.
* Specify the same host and port as in this
* method, give the scheme "SOCKS5" and the realm
* "USER/PASS", set the cookie to null and the
* params to an array containing a single NVPair
* in turn containing the username and password. Example:
*
* HTTPConnection.addDefaultModule(Class.forName("HTTPClient.CookieModule"), 1);
*
* adds the cookie module as the second module in the list.
*
* equals()
returns true. Examples of useful context
* objects are threads (e.g. if you are running multiple clients, one
* per thread) and sockets (e.g. if you are implementing a gateway).
*
* setContext()
hasn't been invoked
*/
public Object getContext()
{
if (Context != null)
return Context;
else
return dflt_context;
}
/**
* Returns the default context.
*
* @see #setContext(java.lang.Object)
* @return the default context
*/
public static Object getDefaultContext()
{
return dflt_context;
}
/**
* Adds an authorization entry for the "digest" authorization scheme to
* the list. If an entry already exists for the "digest" scheme and the
* specified realm then it is overwritten.
*
* setProxyServer()
.
*
*
*
*
* dontProxyFor()
uses, i.e. this is used to undo a
* dontProxyFor()
setting. The syntax for host is
* specified in dontProxyFor()
.
*
* @param host a host name, domain name, IP-address or IP-subnet.
* @return true if the remove was sucessful, false otherwise
* @exception ParseException if the length of the netmask does not match
* the length of the IP-address
* @see #dontProxyFor(java.lang.String)
*/
public static boolean doProxyFor(String host) throws ParseException
{
host = host.trim().toLowerCase();
// check for domain name
if (host.charAt(0) == '.')
return non_proxy_dom_list.removeElement(host);
// check for host name
for (int idx=0; idx
* NVPair[] up = { new NVPair(username, password) };
* AuthorizationInfo.addAuthorization(host, port, "SOCKS5", "USER/PASS",
* null, up);
*
* If you have not given any authorization info and the proxy server
* requires authorization then you will be prompted for the necessary
* info via a popup the first time you do a request.
*
* @param host the host on which the proxy server resides.
* @param port the port the proxy server is listening on.
* @param version the SOCKS version the server is running. Currently
* this must be '4' or '5'.
* @exception SocksException If version is not '4' or '5'.
*/
public static void setSocksServer(String host, int port, int version)
throws SocksException
{
if (port <= 0)
port = 1080;
if (host == null || host.length() == 0)
Default_Socks_client = null;
else
Default_Socks_client = new SocksClient(host, port, version);
}
/**
* Removes the #... part. Returns the stripped name, or "" if either
* the file is null or is the empty string (after stripping).
*
* @param file the name to strip
* @return the stripped name
*/
private final String stripRef(String file)
{
if (file == null) return "";
int hash = file.indexOf('#');
if (hash != -1)
file = file.substring(0,hash);
return file.trim();
}
// private helper methods
/**
* Sets up the request, creating the list of headers to send and
* creating instances of the modules. This may be invoked by subclasses
* which add further methods (such as those from DAV and IPP).
*
* @param method GET, POST, etc.
* @param resource the resource
* @param headers an array of headers to be used
* @param entity the entity (or null)
* @param stream the output stream (or null) - only one of stream and
* entity may be non-null
* @return the response.
* @exception java.io.IOException when an exception is returned from
* the socket.
* @exception ModuleException if an exception is encountered in any module.
*/
protected final HTTPResponse setupRequest(String method, String resource,
NVPair[] headers, byte[] entity,
HttpOutputStream stream)
throws IOException, ModuleException
{
Request req = new Request(this, method, resource,
mergedHeaders(headers), entity, stream,
allowUI);
RequestList.addToEnd(req);
try
{
HTTPResponse resp = new HTTPResponse(gen_mod_insts(), Timeout, req);
handleRequest(req, resp, null, true);
return resp;
}
finally
{ RequestList.remove(req); }
}
/**
* This merges built-in default headers, user-specified default headers,
* and method-specified headers. Method-specified take precedence over
* user defaults, which take precedence over built-in defaults.
*
* The following headers are removed if found: "Content-length".
*
* @param spec the headers specified in the call to the method
* @return an array consisting of merged headers.
*/
private NVPair[] mergedHeaders(NVPair[] spec)
{
int spec_len = (spec != null ? spec.length : 0),
defs_len;
NVPair[] merged;
synchronized (DefaultHeaders)
{
defs_len = (DefaultHeaders != null ? DefaultHeaders.length : 0);
merged = new NVPair[spec_len + defs_len];
// copy default headers
System.arraycopy(DefaultHeaders, 0, merged, 0, defs_len);
}
// merge in selected headers
int sidx, didx = defs_len;
for (sidx=0; sidx
*
*
* Furthermore, it escapes various characters in request-URI.
*
* @param req the Request
* @param hdr_buf the buffer onto which to write the headers
* @return an array of headers; the first element contains the
* the value of the Connection or Proxy-Connectin header,
* the second element the value of the Expect header.
* @exception IOException if writing on hdr_buf generates an
* an IOException, or if an error occurs during
* parsing of a header
*/
private String[] assembleHeaders(Request req,
ByteArrayOutputStream hdr_buf)
throws IOException
{
DataOutputStream dataout = new DataOutputStream(hdr_buf);
String[] con_hdrs = { "", "" };
NVPair[] hdrs = req.getHeaders();
// remember various headers
int ho_idx = -1,
ct_idx = -1,
ua_idx = -1,
co_idx = -1,
pc_idx = -1,
ka_idx = -1,
ex_idx = -1,
te_idx = -1,
tc_idx = -1,
ug_idx = -1;
for (int idx=0; idx