001/* ----------------------------------------------------------------------------
002   The Kiwi Toolkit - A Java Class Library
003   Copyright (C) 1998-2004 Mark A. Lindner
004
005   This library is free software; you can redistribute it and/or
006   modify it under the terms of the GNU General Public License as
007   published by the Free Software Foundation; either version 2 of the
008   License, or (at your option) any later version.
009
010   This library is distributed in the hope that it will be useful,
011   but WITHOUT ANY WARRANTY; without even the implied warranty of
012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013   General Public License for more details.
014
015   You should have received a copy of the GNU General Public License
016   along with this library; if not, write to the Free Software
017   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018   02111-1307, USA.
019 
020   The author may be contacted at: mark_a_lindner@yahoo.com
021   ----------------------------------------------------------------------------
022   $Log: HTTPRequest.java,v $
023   Revision 1.7  2004/05/05 21:36:35  markl
024   comment block updates
025
026   Revision 1.6  2003/11/07 19:06:05  markl
027   bug fix
028
029   Revision 1.5  2003/01/19 09:37:12  markl
030   Javadoc & comment header updates.
031
032   Revision 1.4  2001/03/12 06:01:18  markl
033   Javadoc cleanup.
034
035   Revision 1.3  2001/03/12 01:58:42  markl
036   Source code cleanup.
037
038   Revision 1.2  2000/08/07 07:56:52  markl
039   *** empty log message ***
040
041   Revision 1.1  1999/06/03 08:07:22  markl
042   Initial revision
043   ----------------------------------------------------------------------------
044*/
045
046package kiwi.io;
047
048import java.io.*;
049import java.net.*;
050import java.util.*;
051
052/** This class represents an HTTP request, e.g., a single transaction with an
053 * HTTP server. It may be used to send arbitrary requests to an HTTP server.
054 * This class extends the abstract class
055 * <code>java.net.HttpURLConnection</code>; see the documentation on that class
056 * for more information.
057 * 
058 * @author Mark Lindner
059 */
060
061public class HTTPRequest extends HttpURLConnection
062  {
063  private Socket socket = null;
064  private static final char keySeparator = ':';
065  private Hashtable keys = new Hashtable();
066  private Vector headers = new Vector(), keyList = new Vector();
067  private boolean gotHeaders = false;
068  private static final String CRLF = "\r\n";
069
070  /** Construct a new <code>HTTPRequest</code> for the given URL.
071   *
072   * @param url The URL identifying the destination of the transaction.
073   */
074  
075  public HTTPRequest(URL url)
076    {
077    super(url);
078    }
079
080  /** Connect to the HTTP server.
081   *
082   * @exception java.io.IOException If the connection failed.
083   */
084
085  public void connect() throws IOException
086    {
087    if(connected) return;
088
089    InetAddress dest = InetAddress.getByName(getURL().getHost());
090    int port = getURL().getPort();
091    if(port < 0)
092      port = 80;
093
094    socket = new Socket(dest, port);
095    OutputStream out = socket.getOutputStream();
096
097    send(out, getRequestMethod() + " " + url.getFile() + " HTTP/1.0" + CRLF);
098    send(out, "Host: " + getURL().getHost() + CRLF);
099    send(out, CRLF);
100
101    InputStream in = socket.getInputStream();
102    
103    String status = recv(in);
104
105    StringTokenizer st = new StringTokenizer(status);
106
107    if(st.hasMoreTokens())
108      {
109      st.nextToken();
110
111      if(st.hasMoreTokens())
112        {
113        responseCode = Integer.parseInt(st.nextToken());
114        responseMessage = st.nextToken("");
115        }
116      }
117
118    connected = true;
119    }
120
121  /** Disconnect from the HTTP server. */
122  
123  public void disconnect()
124    {
125    if(socket != null)
126      {
127      try
128        {
129        socket.close();
130        }
131      catch(IOException ex) { /* ignore */ }
132      socket = null;
133      }
134    
135    connected = false;
136    }
137
138  /** Determine if this implementation uses a proxy.
139   *
140   * @return This implementation returns <code>false</code>.
141   */
142
143  public boolean usingProxy()
144    {
145    return(false);
146    }
147
148  /** Get the input stream associated with the request. If no connection has
149   * been established yet, an attempt is made to make one.
150   */
151  
152  public InputStream getInputStream() throws IOException
153    {
154    if(!connected)
155      connect();
156
157    return(socket.getInputStream());
158    }
159
160  /** Get the output stream associated with the request. If no connection has
161   * been established yet, an attempt is made to make one.
162   */
163  
164  public OutputStream getOutputStream() throws IOException
165    {
166    if(!connected)
167      connect();
168
169    return(socket.getOutputStream());
170    }
171
172  /** Get the header field value for the specified key.
173   *
174   * @param key The key.
175   * @return The value of the field.
176   */
177
178  public String getHeaderField(String key)
179    {
180    getHeaders();
181    return((String)keys.get(key.toLowerCase()));
182    }
183
184  /** Get the header field key for the specified position.
185   *
186   * @param n The position.
187   * @return The key of the header at the specified position.
188   */
189  
190  public String getHeaderFieldKey(int n)
191    {
192    getHeaders();
193    if(n >= headers.size())
194      return(null);
195
196    return((String)keyList.elementAt(n));
197    }
198
199  /* send a line on a stream */
200  
201  private void send(OutputStream out, String s) throws IOException
202    {
203    //System.out.println("SEND: " + s);
204    
205    for(int i = 0; i < s.length(); i++)
206      out.write((byte)s.charAt(i));
207    
208    out.flush();
209    }
210
211  /* receive a line on a stream */
212  
213  private String recv(InputStream in) throws IOException
214    {
215    StringBuffer result = new StringBuffer();
216
217    for(;;)
218      {
219      int c = in.read();
220
221      if((c < 0) || (c == '\n'))
222        break;
223
224      if(c != '\r')
225        result.append((char)c);
226      }
227
228    String s = result.toString();
229    
230    //System.out.println("RECV: " + s);
231    
232    return(s);
233    }
234
235  /** Retrieve header key/value pairs. */
236  
237  public void getHeaders()
238    {
239    if(gotHeaders)
240      return;
241
242    gotHeaders = true;
243
244    try
245      {
246      InputStream in = getInputStream();
247      for(;;)
248        {
249        String header = recv(in);
250        if(header.length() == 0)
251          break;
252
253        // break out key & value
254
255        String key, value;
256        int i = header.indexOf(keySeparator);
257        if(i < 0)
258          {
259          key = header;
260          value = "";
261          }
262        else
263          {
264          key = header.substring(0, i).toLowerCase();
265          value = header.substring(++i).trim();
266          }
267
268        keys.put(key, value);
269        keyList.addElement(key);
270        headers.addElement(header);
271        }
272      }
273    catch(IOException ex)
274      {
275      ex.printStackTrace();
276      }
277    }
278  
279  }
280
281/* end of source file */