001         package com.croftsoft.core.net.http;
002    
003         import java.io.*;
004         import java.net.*;
005         import java.util.*;
006    
007         import com.croftsoft.core.io.Parser;
008    
009         /*********************************************************************
010         * Somewhat of a replacement for HttpURLConnection.
011         *
012         * <p />
013         *
014         * @version
015         *   2003-05-19
016         * @since
017         *   2000-04-27
018         * @author
019         *   <a href="https://www.croftsoft.com/">David W. Croft</a>
020         *********************************************************************/
021    
022         public final class  HttpConnection
023         //////////////////////////////////////////////////////////////////////
024         //////////////////////////////////////////////////////////////////////
025         {
026    
027         public static final int  HTTP_ACCEPTED   = 202;
028    
029         public static final int  HTTP_NO_CONTENT = 204;
030    
031         private final String     host;
032    
033         private final int        port;
034    
035         private final String     path;
036    
037         private final String     userAgent;
038    
039         private final String     contentType;
040    
041         private       byte [ ]   requestBytes;
042    
043         private final Parser     contentParser;
044    
045    // need to modify so that Headers used during outgoing posts;
046    // don't forget to clear!
047    
048         private final Hashtable  headerHashtable;
049    
050         private       int        responseCode;
051    
052         private       Object     parsedContent;
053    
054         private       Socket     socket;
055    
056         //////////////////////////////////////////////////////////////////////
057         //////////////////////////////////////////////////////////////////////
058    
059         public  HttpConnection (
060           String    host,
061           int       port,
062           String    path,
063           String    userAgent,
064           String    contentType,
065           byte [ ]  requestBytes,
066           Parser    contentParser )
067         //////////////////////////////////////////////////////////////////////
068         {
069           if ( host == null )
070           {
071             throw new IllegalArgumentException ( "null host" );
072           }
073    
074           if ( port == -1 )
075           {
076             port = 80;
077           }
078    
079           if ( path == null )
080           {
081             throw new IllegalArgumentException ( "null path" );
082           }
083    
084           if ( userAgent == null )
085           {
086             throw new IllegalArgumentException ( "null userAgent" );
087           }
088    
089           if ( contentType == null )
090           {
091             throw new IllegalArgumentException ( "null contentType" );
092           }
093    
094           this.host          = host;
095    
096           this.port          = port;
097    
098           this.path          = path;
099    
100           this.userAgent     = userAgent;
101    
102           this.contentType   = contentType;
103    
104           this.requestBytes  = requestBytes;
105    
106           this.contentParser = contentParser;
107    
108           headerHashtable    = new Hashtable ( );
109         }
110    
111         //////////////////////////////////////////////////////////////////////
112         //////////////////////////////////////////////////////////////////////
113    
114         public void  post ( )
115           throws IOException
116         //////////////////////////////////////////////////////////////////////
117         {
118           // Save a reference to requestBytes in case it gets changed.
119    
120           byte [ ]  requestBytes = this.requestBytes;
121    
122           OutputStream  out    = null;
123    
124           InputStream   in     = null;
125    
126           try
127           {
128             socket = new Socket ( host, port );
129    
130             out = socket.getOutputStream ( );
131    
132             StringBuffer  stringBuffer = new StringBuffer ( );
133    
134             stringBuffer.append ( "POST " + path + " HTTP/1.0\r\n" );
135    
136             stringBuffer.append ( "Connection: Keep-Alive\r\n" );
137    
138             stringBuffer.append ( "User-Agent: " + userAgent + "\r\n" );
139    
140    // What was that other one?  Webmaster?
141    
142             stringBuffer.append ( "Content-type: " + contentType + "\r\n" );
143    
144             if ( requestBytes != null )
145             {
146               stringBuffer.append (
147                 "Content-length: " + requestBytes.length + "\r\n" );
148             }
149    
150             stringBuffer.append ( "\r\n" );
151    
152    // US-ASCII was here
153             out.write ( stringBuffer.toString ( ).getBytes ( ) );
154    
155             if ( requestBytes != null )
156             {
157               out.write ( requestBytes );
158             }
159    
160             out.flush ( );
161    
162             in = socket.getInputStream ( );
163    
164             parseResponse ( in );
165           }
166           finally
167           {
168             try
169             {
170               if ( in != null )
171               {
172                 in.close ( );
173               }
174             }
175             catch ( Exception  ex )
176             {
177               ex.printStackTrace ( );
178             }
179    
180             try
181             {
182               if ( out != null )
183               {
184                 out.close ( );
185               }
186             }
187             catch ( Exception  ex )
188             {
189               ex.printStackTrace ( );
190             }
191    
192    // keep-alive?
193    
194             try
195             {
196               if ( socket != null )
197               {
198                 socket.close ( );
199               }
200             }
201             catch ( Exception  ex )
202             {
203               ex.printStackTrace ( );
204             }
205           }
206         }
207    
208         public void  abort ( )
209         //////////////////////////////////////////////////////////////////////
210         {
211    //System.out.println ( "HttpConnection.abort()" );
212    
213           try
214           {
215             if ( socket != null )
216             {
217               socket.close ( );
218             }
219           }
220           catch ( Exception  ex )
221           {
222           }
223         }
224    
225         //////////////////////////////////////////////////////////////////////
226         //////////////////////////////////////////////////////////////////////
227    
228         public int  getResponseCode ( ) { return responseCode; }
229    
230         public Object  getParsedContent ( ) { return parsedContent; }
231    
232         public Hashtable  getHeaderHashtable ( ) { return headerHashtable; }
233    
234         public void  setRequestBytes ( byte [ ]  requestBytes )
235         //////////////////////////////////////////////////////////////////////
236         {
237           this.requestBytes = requestBytes;
238         }
239    
240         //////////////////////////////////////////////////////////////////////
241         //////////////////////////////////////////////////////////////////////
242    
243         private void  parseResponse ( InputStream  inputStream )
244           throws IOException
245         //////////////////////////////////////////////////////////////////////
246         {
247           headerHashtable.clear ( );
248    
249           if ( inputStream == null )
250           {
251             responseCode = -1;
252    
253             return;
254           }
255    
256           byte [ ]  statusBytes = new byte [ 12 ];
257    
258           int  bytesRead = inputStream.read ( statusBytes );
259    
260           if ( bytesRead < 12 )
261           {
262             responseCode = -1;
263    
264             return;
265           }
266    
267    // US-ASCII was here
268           String  statusString = new String ( statusBytes );
269    
270    // weak
271           if ( !statusString.startsWith ( "HTTP/1." ) )
272           {
273             responseCode = -1;
274    
275             return;
276           }
277    
278           try
279           {
280             responseCode = Integer.parseInt ( statusString.substring ( 9 ) );
281           }
282           catch ( NumberFormatException  ex )
283           {
284             responseCode = -1;
285    
286             return;
287           }
288    
289           readLine ( inputStream );
290    
291           String  line;
292    
293           while ( ( line = readLine ( inputStream ) ) != null )
294           {
295             if ( line.equals ( "" ) )
296             {
297               break;
298             }
299    
300             int  index = line.indexOf ( ":" );
301    
302             if ( index < 1 )
303             {
304               responseCode = -1;
305    
306               return;
307             }
308    
309             String  headerName = line.substring ( 0, index );
310    
311             if ( line.length ( ) > index + 2 )
312             {
313               String  headerValue = line.substring ( index + 2 ).trim ( );
314    
315    //System.out.println ( "HttpConnection:  (\"" + headerName + "\",\""
316    //  + headerValue + "\")" );
317    
318               headerHashtable.put ( headerName, headerValue );
319             }
320             else
321             {
322               responseCode = -1;
323    
324               return;
325             }
326           }
327    
328           if ( line == null )
329           {
330             return;
331           }
332    
333           if ( contentParser == null )
334           {
335             return;
336           }
337    
338           if ( ( responseCode == HTTP_ACCEPTED   )
339             || ( responseCode == HTTP_NO_CONTENT ) )
340           {
341             return;
342           }
343    
344           String  contentLengthStr
345             = ( String ) headerHashtable.get ( "Content-Length" );
346    
347           int  contentLength;
348    
349           if ( contentLengthStr == null )
350           {
351             contentLength = -1;
352           }
353           else
354           {
355             try
356             {
357               contentLength = Integer.parseInt ( contentLengthStr );
358             }
359             catch ( NumberFormatException  ex )
360             {
361               responseCode = -1;
362    
363               return;
364             }
365           }
366    
367           if ( contentLength == 0 )
368           {
369             return;
370           }
371    
372           parsedContent = contentParser.parse ( inputStream, contentLength );
373         }
374    
375    // don't want to buffer but do want to have readLine capability;
376    // DataInputStream.readLine deprecated, BufferedReader buffers
377    
378    // only works with "\r\n"; note:  no pushback
379    
380         private static String  readLine ( InputStream  inputStream )
381           throws IOException
382         //////////////////////////////////////////////////////////////////////
383         {
384           // looking for "\r\n"
385    
386           ByteArrayOutputStream  byteArrayOutputStream
387             = new ByteArrayOutputStream ( );
388    
389           int  state = 0;
390    
391           while ( true )
392           {
393             int  c = inputStream.read ( );
394    
395             if ( c < 0 )
396             {
397    // encoding?
398    
399               String  s = byteArrayOutputStream.toString ( );
400    
401               if ( s.length ( ) < 1 )
402               {
403                 return null;
404               }
405    
406               return s;
407             }
408    
409             if ( c == '\r' )
410             {
411               if ( state == 0 )
412               {
413                 state = 1;
414               }
415               else
416               {
417                 state = 0;
418               }
419             }
420             else if ( c == '\n' )
421             {
422               if ( state == 1 )
423               {
424    // encoding?
425    
426                 String  s = byteArrayOutputStream.toString ( );
427    
428                 // trim off the "\r"
429    
430                 return s.substring ( 0, s.length ( ) - 1 );
431               }
432               else
433               {
434                 state = 0;
435               }
436             }
437             else
438             {
439               state = 0;
440             }
441    
442             byteArrayOutputStream.write ( c );
443           }
444         }
445    
446         //////////////////////////////////////////////////////////////////////
447         //////////////////////////////////////////////////////////////////////
448         }